
(function(modules) {
    var require = function(identifier) {
        var module, exports, closure;
        if (!modules[identifier].hasOwnProperty('exports')) {
            exports = {};
            module = {id: identifier, exports: exports};
            closure = modules[identifier];
            modules[identifier] = module;
            closure.call(module, require, exports, module);
        }
        return modules[identifier].exports;
    };


modules['helpcenter/primary/plugins/cookieconsent'] = function(require, exports, module, modules) {
!function(e){if(!e.hasInitialised){var t={escapeRegExp:function(e){return e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},hasClass:function(e,t){var i=" ";return 1===e.nodeType&&(i+e.className+i).replace(/[\n\t]/g,i).indexOf(i+t+i)>=0},addClass:function(e,t){e.className+=" "+t},removeClass:function(e,t){var i=new RegExp("\\b"+this.escapeRegExp(t)+"\\b");e.className=e.className.replace(i,"")},interpolateString:function(e,t){var i=/{{([a-z][a-z0-9\-_]*)}}/gi;return e.replace(i,function(e){return t(arguments[1])||""})},getCookie:function(e){var t="; "+document.cookie,i=t.split("; "+e+"=");return 2!=i.length?void 0:i.pop().split(";").shift()},setCookie:function(e,t,i,n,o){var s=new Date;s.setDate(s.getDate()+(i||365));var r=[e+"="+t,"expires="+s.toUTCString(),"path="+(o||"/")];n&&r.push("domain="+n),document.cookie=r.join(";")},deepExtend:function(e,t){for(var i in t)t.hasOwnProperty(i)&&(i in e&&this.isPlainObject(e[i])&&this.isPlainObject(t[i])?this.deepExtend(e[i],t[i]):e[i]=t[i]);return e},throttle:function(e,t){var i=!1;return function(){i||(e.apply(this,arguments),i=!0,setTimeout(function(){i=!1},t))}},hash:function(e){var t,i,n,o=0;if(0===e.length)return o;for(t=0,n=e.length;t<n;++t)i=e.charCodeAt(t),o=(o<<5)-o+i,o|=0;return o},normaliseHex:function(e){return"#"==e[0]&&(e=e.substr(1)),3==e.length&&(e=e[0]+e[0]+e[1]+e[1]+e[2]+e[2]),e},getContrast:function(e){e=this.normaliseHex(e);var t=parseInt(e.substr(0,2),16),i=parseInt(e.substr(2,2),16),n=parseInt(e.substr(4,2),16),o=(299*t+587*i+114*n)/1e3;return o>=128?"#000":"#fff"},getLuminance:function(e){var t=parseInt(this.normaliseHex(e),16),i=38,n=(t>>16)+i,o=(t>>8&255)+i,s=(255&t)+i,r=(16777216+65536*(n<255?n<1?0:n:255)+256*(o<255?o<1?0:o:255)+(s<255?s<1?0:s:255)).toString(16).slice(1);return"#"+r},isMobile:function(){return/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)},isPlainObject:function(e){return"object"==typeof e&&null!==e&&e.constructor==Object}};e.status={deny:"deny",allow:"allow",dismiss:"dismiss"},e.transitionEnd=function(){var e=document.createElement("div"),t={t:"transitionend",OT:"oTransitionEnd",msT:"MSTransitionEnd",MozT:"transitionend",WebkitT:"webkitTransitionEnd"};for(var i in t)if(t.hasOwnProperty(i)&&"undefined"!=typeof e.style[i+"ransition"])return t[i];return""}(),e.hasTransition=!!e.transitionEnd;var i=Object.keys(e.status).map(t.escapeRegExp);e.customStyles={},e.Popup=function(){function n(){this.initialise.apply(this,arguments)}function o(e){this.openingTimeout=null,t.removeClass(e,"cc-invisible")}function s(t){t.style.display="none",t.removeEventListener(e.transitionEnd,this.afterTransition),this.afterTransition=null}function r(){var t=this.options.onInitialise.bind(this);if(!window.navigator.cookieEnabled)return t(e.status.deny),!0;if(window.CookiesOK||window.navigator.CookiesOK)return t(e.status.allow),!0;var i=Object.keys(e.status),n=this.getStatus(),o=i.indexOf(n)>=0;return o&&t(n),o}function a(){var e=this.options.position.split("-"),t=[];return e.forEach(function(e){t.push("cc-"+e)}),t}function c(){var e=this.options,i="top"==e.position||"bottom"==e.position?"banner":"floating";t.isMobile()&&(i="floating");var n=["cc-"+i,"cc-type-"+e.type,"cc-theme-"+e.theme];e["static"]&&n.push("cc-static"),n.push.apply(n,a.call(this));p.call(this,this.options.palette);return this.customStyleSelector&&n.push(this.customStyleSelector),n}function l(){var e={},i=this.options;i.showLink||(i.elements.link="",i.elements.messagelink=i.elements.message),Object.keys(i.elements).forEach(function(n){e[n]=t.interpolateString(i.elements[n],function(e){var t=i.content[e];return e&&"string"==typeof t&&t.length?t:""})});var n=i.compliance[i.type];n||(n=i.compliance.info),e.compliance=t.interpolateString(n,function(t){return e[t]});var o=i.layouts[i.layout];return o||(o=i.layouts.basic),t.interpolateString(o,function(t){return e[t]})}function u(i){var n=this.options,o=document.createElement("div"),s=n.container&&1===n.container.nodeType?n.container:document.body;o.innerHTML=i;var r=o.children[0];return r.style.display="none",t.hasClass(r,"cc-window")&&e.hasTransition&&t.addClass(r,"cc-invisible"),this.onButtonClick=h.bind(this),r.addEventListener("click",this.onButtonClick),n.autoAttach&&(s.firstChild?s.insertBefore(r,s.firstChild):s.appendChild(r)),r}function h(n){var o=n.target;if(t.hasClass(o,"cc-btn")){var s=o.className.match(new RegExp("\\bcc-("+i.join("|")+")\\b")),r=s&&s[1]||!1;r&&(this.setStatus(r),this.close(!0))}t.hasClass(o,"cc-close")&&(this.setStatus(e.status.dismiss),this.close(!0)),t.hasClass(o,"cc-revoke")&&this.revokeChoice()}function p(e){var i=t.hash(JSON.stringify(e)),n="cc-color-override-"+i,o=t.isPlainObject(e);return this.customStyleSelector=o?n:null,o&&d(i,e,"."+n),o}function d(i,n,o){if(e.customStyles[i])return void++e.customStyles[i].references;var s={},r=n.popup,a=n.button,c=n.highlight;r&&(r.text=r.text?r.text:t.getContrast(r.background),r.link=r.link?r.link:r.text,s[o+".cc-window"]=["color: "+r.text,"background-color: "+r.background],s[o+".cc-revoke"]=["color: "+r.text,"background-color: "+r.background],s[o+" .cc-link,"+o+" .cc-link:active,"+o+" .cc-link:visited"]=["color: "+r.link],a&&(a.text=a.text?a.text:t.getContrast(a.background),a.border=a.border?a.border:"transparent",s[o+" .cc-btn"]=["color: "+a.text,"border-color: "+a.border,"background-color: "+a.background],"transparent"!=a.background&&(s[o+" .cc-btn:hover, "+o+" .cc-btn:focus"]=["background-color: "+v(a.background)]),c?(c.text=c.text?c.text:t.getContrast(c.background),c.border=c.border?c.border:"transparent",s[o+" .cc-highlight .cc-btn:first-child"]=["color: "+c.text,"border-color: "+c.border,"background-color: "+c.background]):s[o+" .cc-highlight .cc-btn:first-child"]=["color: "+r.text]));var l=document.createElement("style");document.head.appendChild(l),e.customStyles[i]={references:1,element:l.sheet};var u=-1;for(var h in s)s.hasOwnProperty(h)&&l.sheet.insertRule(h+"{"+s[h].join(";")+"}",++u)}function v(e){return e=t.normaliseHex(e),"000000"==e?"#222":t.getLuminance(e)}function f(i){if(t.isPlainObject(i)){var n=t.hash(JSON.stringify(i)),o=e.customStyles[n];if(o&&!--o.references){var s=o.element.ownerNode;s&&s.parentNode&&s.parentNode.removeChild(s),e.customStyles[n]=null}}}function m(e,t){for(var i=0,n=e.length;i<n;++i){var o=e[i];if(o instanceof RegExp&&o.test(t)||"string"==typeof o&&o.length&&o===t)return!0}return!1}function b(){var t=this.setStatus.bind(this),i=this.options.dismissOnTimeout;"number"==typeof i&&i>=0&&(this.dismissTimeout=window.setTimeout(function(){t(e.status.dismiss)},Math.floor(i)));var n=this.options.dismissOnScroll;if("number"==typeof n&&n>=0){var o=function(i){window.pageYOffset>Math.floor(n)&&(t(e.status.dismiss),window.removeEventListener("scroll",o),this.onWindowScroll=null)};this.onWindowScroll=o,window.addEventListener("scroll",o)}}function y(){if("info"!=this.options.type&&(this.options.revokable=!0),t.isMobile()&&(this.options.animateRevokable=!1),this.options.revokable){var e=a.call(this);this.options.animateRevokable&&e.push("cc-animate"),this.customStyleSelector&&e.push(this.customStyleSelector);var i=this.options.revokeBtn.replace("{{classes}}",e.join(" "));this.revokeBtn=u.call(this,i);var n=this.revokeBtn;if(this.options.animateRevokable){var o=t.throttle(function(e){var i=!1,o=20,s=window.innerHeight-20;t.hasClass(n,"cc-top")&&e.clientY<o&&(i=!0),t.hasClass(n,"cc-bottom")&&e.clientY>s&&(i=!0),i?t.hasClass(n,"cc-active")||t.addClass(n,"cc-active"):t.hasClass(n,"cc-active")&&t.removeClass(n,"cc-active")},200);this.onMouseMove=o,window.addEventListener("mousemove",o)}}}var g={enabled:!0,container:null,cookie:{name:"cookieconsent_status",path:"/",domain:"",expiryDays:365},onPopupOpen:function(){},onPopupClose:function(){},onInitialise:function(e){},onStatusChange:function(e,t){},onRevokeChoice:function(){},content:{header:"Cookies used on the website!",message:"This website uses cookies to ensure you get the best experience on our website.",dismiss:"Got it!",allow:"Allow cookies",deny:"Decline",link:"Learn more",href:"http://cookiesandyou.com",close:"&#x274c;"},elements:{header:'<span class="cc-header">{{header}}</span>&nbsp;',message:'<span id="cookieconsent:desc" class="cc-message">{{message}}</span>',messagelink:'<span id="cookieconsent:desc" class="cc-message">{{message}} <a aria-label="learn more about cookies" role=button tabindex="0" class="cc-link" href="{{href}}" rel="noopener noreferrer nofollow" target="_blank">{{link}}</a></span>',dismiss:'<a aria-label="dismiss cookie message" role=button tabindex="0" class="cc-btn cc-dismiss">{{dismiss}}</a>',allow:'<a aria-label="allow cookies" role=button tabindex="0"  class="cc-btn cc-allow">{{allow}}</a>',deny:'<a aria-label="deny cookies" role=button tabindex="0" class="cc-btn cc-deny">{{deny}}</a>',link:'<a aria-label="learn more about cookies" role=button tabindex="0" class="cc-link" href="{{href}}" target="_blank">{{link}}</a>',close:'<span aria-label="dismiss cookie message" role=button tabindex="0" class="cc-close">{{close}}</span>'},window:'<div role="dialog" aria-live="polite" aria-label="cookieconsent" aria-describedby="cookieconsent:desc" class="cc-window {{classes}}"><!--googleoff: all-->{{children}}<!--googleon: all--></div>',revokeBtn:'<div class="cc-revoke {{classes}}">Cookie Policy</div>',compliance:{info:'<div class="cc-compliance">{{dismiss}}</div>',"opt-in":'<div class="cc-compliance cc-highlight">{{dismiss}}{{allow}}</div>',"opt-out":'<div class="cc-compliance cc-highlight">{{deny}}{{dismiss}}</div>'},type:"info",layouts:{basic:"{{messagelink}}{{compliance}}","basic-close":"{{messagelink}}{{compliance}}{{close}}","basic-header":"{{header}}{{message}}{{link}}{{compliance}}"},layout:"basic",position:"bottom",theme:"block","static":!1,palette:null,revokable:!1,animateRevokable:!0,showLink:!0,dismissOnScroll:!1,dismissOnTimeout:!1,autoOpen:!0,autoAttach:!0,whitelistPage:[],blacklistPage:[],overrideHTML:null};return n.prototype.initialise=function(e){this.options&&this.destroy(),t.deepExtend(this.options={},g),t.isPlainObject(e)&&t.deepExtend(this.options,e),r.call(this)&&(this.options.enabled=!1),m(this.options.blacklistPage,location.pathname)&&(this.options.enabled=!1),m(this.options.whitelistPage,location.pathname)&&(this.options.enabled=!0);var i=this.options.window.replace("{{classes}}",c.call(this).join(" ")).replace("{{children}}",l.call(this)),n=this.options.overrideHTML;if("string"==typeof n&&n.length&&(i=n),this.options["static"]){var o=u.call(this,'<div class="cc-grower">'+i+"</div>");o.style.display="",this.element=o.firstChild,this.element.style.display="none",t.addClass(this.element,"cc-invisible")}else this.element=u.call(this,i);b.call(this),y.call(this),this.options.autoOpen&&this.autoOpen()},n.prototype.destroy=function(){this.onButtonClick&&this.element&&(this.element.removeEventListener("click",this.onButtonClick),this.onButtonClick=null),this.dismissTimeout&&(clearTimeout(this.dismissTimeout),this.dismissTimeout=null),this.onWindowScroll&&(window.removeEventListener("scroll",this.onWindowScroll),this.onWindowScroll=null),this.onMouseMove&&(window.removeEventListener("mousemove",this.onMouseMove),this.onMouseMove=null),this.element&&this.element.parentNode&&this.element.parentNode.removeChild(this.element),this.element=null,this.revokeBtn&&this.revokeBtn.parentNode&&this.revokeBtn.parentNode.removeChild(this.revokeBtn),this.revokeBtn=null,f(this.options.palette),this.options=null},n.prototype.open=function(t){if(this.element)return this.isOpen()||(e.hasTransition?this.fadeIn():this.element.style.display="",this.options.revokable&&this.toggleRevokeButton(),this.options.onPopupOpen.call(this)),this},n.prototype.close=function(t){if(this.element)return this.isOpen()&&(e.hasTransition?this.fadeOut():this.element.style.display="none",t&&this.options.revokable&&this.toggleRevokeButton(!0),this.options.onPopupClose.call(this)),this},n.prototype.fadeIn=function(){var i=this.element;if(e.hasTransition&&i&&(this.afterTransition&&s.call(this,i),t.hasClass(i,"cc-invisible"))){if(i.style.display="",this.options["static"]){var n=this.element.clientHeight;this.element.parentNode.style.maxHeight=n+"px"}var r=20;this.openingTimeout=setTimeout(o.bind(this,i),r)}},n.prototype.fadeOut=function(){var i=this.element;e.hasTransition&&i&&(this.openingTimeout&&(clearTimeout(this.openingTimeout),o.bind(this,i)),t.hasClass(i,"cc-invisible")||(this.options["static"]&&(this.element.parentNode.style.maxHeight=""),this.afterTransition=s.bind(this,i),i.addEventListener(e.transitionEnd,this.afterTransition),t.addClass(i,"cc-invisible")))},n.prototype.isOpen=function(){return this.element&&""==this.element.style.display&&(!e.hasTransition||!t.hasClass(this.element,"cc-invisible"))},n.prototype.toggleRevokeButton=function(e){this.revokeBtn&&(this.revokeBtn.style.display=e?"":"none")},n.prototype.revokeChoice=function(e){this.options.enabled=!0,this.clearStatus(),this.options.onRevokeChoice.call(this),e||this.autoOpen()},n.prototype.hasAnswered=function(t){return Object.keys(e.status).indexOf(this.getStatus())>=0},n.prototype.hasConsented=function(t){var i=this.getStatus();return i==e.status.allow||i==e.status.dismiss},n.prototype.autoOpen=function(e){!this.hasAnswered()&&this.options.enabled&&this.open()},n.prototype.setStatus=function(i){var n=this.options.cookie,o=t.getCookie(n.name),s=Object.keys(e.status).indexOf(o)>=0;Object.keys(e.status).indexOf(i)>=0?(t.setCookie(n.name,i,n.expiryDays,n.domain,n.path),this.options.onStatusChange.call(this,i,s)):this.clearStatus()},n.prototype.getStatus=function(){return t.getCookie(this.options.cookie.name)},n.prototype.clearStatus=function(){var e=this.options.cookie;t.setCookie(e.name,"",-1,e.domain,e.path)},n}(),e.Location=function(){function e(e){t.deepExtend(this.options={},s),t.isPlainObject(e)&&t.deepExtend(this.options,e),this.currentServiceIndex=-1}function i(e,t,i){var n,o=document.createElement("script");o.type="text/"+(e.type||"javascript"),o.src=e.src||e,o.async=!1,o.onreadystatechange=o.onload=function(){var e=o.readyState;clearTimeout(n),t.done||e&&!/loaded|complete/.test(e)||(t.done=!0,t(),o.onreadystatechange=o.onload=null)},document.body.appendChild(o),n=setTimeout(function(){t.done=!0,t(),o.onreadystatechange=o.onload=null},i)}function n(e,t,i,n,o){var s=new(window.XMLHttpRequest||window.ActiveXObject)("MSXML2.XMLHTTP.3.0");if(s.open(n?"POST":"GET",e,1),s.setRequestHeader("X-Requested-With","XMLHttpRequest"),s.setRequestHeader("Content-type","application/x-www-form-urlencoded"),Array.isArray(o))for(var r=0,a=o.length;r<a;++r){var c=o[r].split(":",2);s.setRequestHeader(c[0].replace(/^\s+|\s+$/g,""),c[1].replace(/^\s+|\s+$/g,""))}"function"==typeof t&&(s.onreadystatechange=function(){s.readyState>3&&t(s)}),s.send(n)}function o(e){return new Error("Error ["+(e.code||"UNKNOWN")+"]: "+e.error)}var s={timeout:5e3,services:["freegeoip","ipinfo","maxmind"],serviceDefinitions:{freegeoip:function(){return{url:"//freegeoip.net/json/?callback={callback}",isScript:!0,callback:function(e,t){try{var i=JSON.parse(t);return i.error?o(i):{code:i.country_code}}catch(n){return o({error:"Invalid response ("+n+")"})}}}},ipinfo:function(){return{url:"//ipinfo.io",headers:["Accept: application/json"],callback:function(e,t){try{var i=JSON.parse(t);return i.error?o(i):{code:i.country}}catch(n){return o({error:"Invalid response ("+n+")"})}}}},ipinfodb:function(e){return{url:"//api.ipinfodb.com/v3/ip-country/?key={api_key}&format=json&callback={callback}",isScript:!0,callback:function(e,t){try{var i=JSON.parse(t);return"ERROR"==i.statusCode?o({error:i.statusMessage}):{code:i.countryCode}}catch(n){return o({error:"Invalid response ("+n+")"})}}}},maxmind:function(){return{url:"//js.maxmind.com/js/apis/geoip2/v2.1/geoip2.js",isScript:!0,callback:function(e){return window.geoip2?void geoip2.country(function(t){try{e({code:t.country.iso_code})}catch(i){e(o(i))}},function(t){e(o(t))}):void e(new Error("Unexpected response format. The downloaded script should have exported `geoip2` to the global scope"))}}}}};return e.prototype.getNextService=function(){var e;do e=this.getServiceByIdx(++this.currentServiceIndex);while(this.currentServiceIndex<this.options.services.length&&!e);return e},e.prototype.getServiceByIdx=function(e){var i=this.options.services[e];if("function"==typeof i){var n=i();return n.name&&t.deepExtend(n,this.options.serviceDefinitions[n.name](n)),n}return"string"==typeof i?this.options.serviceDefinitions[i]():t.isPlainObject(i)?this.options.serviceDefinitions[i.name](i):null},e.prototype.locate=function(e,t){var i=this.getNextService();return i?(this.callbackComplete=e,this.callbackError=t,void this.runService(i,this.runNextServiceOnError.bind(this))):void t(new Error("No services to run"))},e.prototype.setupUrl=function(e){var t=this.getCurrentServiceOpts();return e.url.replace(/\{(.*?)\}/g,function(i,n){if("callback"===n){var o="callback"+Date.now();return window[o]=function(t){e.__JSONP_DATA=JSON.stringify(t)},o}if(n in t.interpolateUrl)return t.interpolateUrl[n]})},e.prototype.runService=function(e,t){var o=this;if(e&&e.url&&e.callback){var s=e.isScript?i:n,r=this.setupUrl(e);s(r,function(i){var n=i?i.responseText:"";e.__JSONP_DATA&&(n=e.__JSONP_DATA,delete e.__JSONP_DATA),o.runServiceCallback.call(o,t,e,n)},this.options.timeout,e.data,e.headers)}},e.prototype.runServiceCallback=function(e,t,i){var n=this,o=function(t){s||n.onServiceResult.call(n,e,t)},s=t.callback(o,i);s&&this.onServiceResult.call(this,e,s)},e.prototype.onServiceResult=function(e,t){t instanceof Error||t&&t.error?e.call(this,t,null):e.call(this,null,t)},e.prototype.runNextServiceOnError=function(e,t){if(e){this.logError(e);var i=this.getNextService();i?this.runService(i,this.runNextServiceOnError.bind(this)):this.completeService.call(this,this.callbackError,new Error("All services failed"))}else this.completeService.call(this,this.callbackComplete,t)},e.prototype.getCurrentServiceOpts=function(){var e=this.options.services[this.currentServiceIndex];return"string"==typeof e?{name:e}:"function"==typeof e?e():t.isPlainObject(e)?e:{}},e.prototype.completeService=function(e,t){this.currentServiceIndex=-1,e&&e(t)},e.prototype.logError=function(e){var t=this.currentServiceIndex,i=this.getServiceByIdx(t);console.error("The service["+t+"] ("+i.url+") responded with the following error",e)},e}(),e.Law=function(){function e(e){this.initialise.apply(this,arguments)}var i={regionalLaw:!0,hasLaw:["AT","BE","BG","HR","CZ","CY","DK","EE","FI","FR","DE","EL","HU","IE","IT","LV","LT","LU","MT","NL","PL","PT","SK","SI","ES","SE","GB","UK"],revokable:["HR","CY","DK","EE","FR","DE","LV","LT","NL","PT","ES"],explicitAction:["HR","IT","ES"]};return e.prototype.initialise=function(e){t.deepExtend(this.options={},i),t.isPlainObject(e)&&t.deepExtend(this.options,e)},e.prototype.get=function(e){var t=this.options;return{hasLaw:t.hasLaw.indexOf(e)>=0,revokable:t.revokable.indexOf(e)>=0,explicitAction:t.explicitAction.indexOf(e)>=0}},e.prototype.applyLaw=function(e,t){var i=this.get(t);return i.hasLaw||(e.enabled=!1),this.options.regionalLaw&&(i.revokable&&(e.revokable=!0),i.explicitAction&&(e.dismissOnScroll=!1,e.dismissOnTimeout=!1)),e},e}(),e.initialise=function(t,i,n){var o=new e.Law(t.law);i||(i=function(){}),n||(n=function(){}),e.getCountryCode(t,function(n){delete t.law,delete t.location,n.code&&(t=o.applyLaw(t,n.code)),i(new e.Popup(t))},function(i){delete t.law,delete t.location,n(i,new e.Popup(t))})},e.getCountryCode=function(t,i,n){if(t.law&&t.law.countryCode)return void i({code:t.law.countryCode});if(t.location){var o=new e.Location(t.location);return void o.locate(function(e){i(e||{})},n)}i({})},e.utils=t,e.hasInitialised=!0,window.cookieconsent=e}}(window.cookieconsent||{});
};


modules['helpcenter/primary/plugins/autosize'] = function(require, exports, module, modules) {
/*!
  Autosize 3.0.14
  license: MIT
  http://www.jacklmoore.com/autosize
*/
(function (global, factory) {
  if (typeof define === 'function' && define.amd) {
    define(['exports', 'module'], factory);
  } else if (typeof exports !== 'undefined' && typeof module !== 'undefined') {
    factory(exports, module);
  } else {
    var mod = {
      exports: {}
    };
    factory(mod.exports, mod);
    global.autosize = mod.exports;
  }
})(this, function (exports, module) {
  'use strict';

  var set = typeof Set === 'function' ? new Set() : (function () {
    var list = [];

    return {
      has: function has(key) {
        return Boolean(list.indexOf(key) > -1);
      },
      add: function add(key) {
        list.push(key);
      },
      'delete': function _delete(key) {
        list.splice(list.indexOf(key), 1);
      } };
  })();

  function assign(ta) {
    var _ref = arguments[1] === undefined ? {} : arguments[1];

    var _ref$setOverflowX = _ref.setOverflowX;
    var setOverflowX = _ref$setOverflowX === undefined ? true : _ref$setOverflowX;
    var _ref$setOverflowY = _ref.setOverflowY;
    var setOverflowY = _ref$setOverflowY === undefined ? true : _ref$setOverflowY;

    if (!ta || !ta.nodeName || ta.nodeName !== 'TEXTAREA' || set.has(ta)) return;

    var heightOffset = null;
    var overflowY = null;
    var clientWidth = ta.clientWidth;

    function init() {
      var style = window.getComputedStyle(ta, null);

      overflowY = style.overflowY;

      if (style.resize === 'vertical') {
        ta.style.resize = 'none';
      } else if (style.resize === 'both') {
        ta.style.resize = 'horizontal';
      }

      if (style.boxSizing === 'content-box') {
        heightOffset = -(parseFloat(style.paddingTop) + parseFloat(style.paddingBottom));
      } else {
        heightOffset = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth);
      }
      // Fix when a textarea is not on document body and heightOffset is Not a Number
      if (isNaN(heightOffset)) {
        heightOffset = 0;
      }

      update();
    }

    function changeOverflow(value) {
      {
        // Chrome/Safari-specific fix:
        // When the textarea y-overflow is hidden, Chrome/Safari do not reflow the text to account for the space
        // made available by removing the scrollbar. The following forces the necessary text reflow.
        var width = ta.style.width;
        ta.style.width = '0px';
        // Force reflow:
        /* jshint ignore:start */
        ta.offsetWidth;
        /* jshint ignore:end */
        ta.style.width = width;
      }

      overflowY = value;

      if (setOverflowY) {
        ta.style.overflowY = value;
      }

      resize();
    }

    function resize() {
      var htmlTop = window.pageYOffset;
      var bodyTop = document.body.scrollTop;
      var originalHeight = ta.style.height;

      ta.style.height = 'auto';

      var endHeight = ta.scrollHeight + heightOffset;

      if (ta.scrollHeight === 0) {
        // If the scrollHeight is 0, then the element probably has display:none or is detached from the DOM.
        ta.style.height = originalHeight;
        return;
      }

      ta.style.height = endHeight + 'px';

      // used to check if an update is actually necessary on window.resize
      clientWidth = ta.clientWidth;

      // prevents scroll-position jumping
      document.documentElement.scrollTop = htmlTop;
      document.body.scrollTop = bodyTop;
    }

    function update() {
      var startHeight = ta.style.height;

      resize();

      var style = window.getComputedStyle(ta, null);

      if (style.height !== ta.style.height) {
        if (overflowY !== 'visible') {
          changeOverflow('visible');
        }
      } else {
        if (overflowY !== 'hidden') {
          changeOverflow('hidden');
        }
      }

      if (startHeight !== ta.style.height) {
        var evt = document.createEvent('Event');
        evt.initEvent('autosize:resized', true, false);
        ta.dispatchEvent(evt);
      }
    }

    var pageResize = function pageResize() {
      if (ta.clientWidth !== clientWidth) {
        update();
      }
    };

    var destroy = (function (style) {
      window.removeEventListener('resize', pageResize, false);
      ta.removeEventListener('input', update, false);
      ta.removeEventListener('keyup', update, false);
      ta.removeEventListener('autosize:destroy', destroy, false);
      ta.removeEventListener('autosize:update', update, false);
      set['delete'](ta);

      Object.keys(style).forEach(function (key) {
        ta.style[key] = style[key];
      });
    }).bind(ta, {
      height: ta.style.height,
      resize: ta.style.resize,
      overflowY: ta.style.overflowY,
      overflowX: ta.style.overflowX,
      wordWrap: ta.style.wordWrap });

    ta.addEventListener('autosize:destroy', destroy, false);

    // IE9 does not fire onpropertychange or oninput for deletions,
    // so binding to onkeyup to catch most of those events.
    // There is no way that I know of to detect something like 'cut' in IE9.
    if ('onpropertychange' in ta && 'oninput' in ta) {
      ta.addEventListener('keyup', update, false);
    }

    window.addEventListener('resize', pageResize, false);
    ta.addEventListener('input', update, false);
    ta.addEventListener('autosize:update', update, false);
    set.add(ta);

    if (setOverflowX) {
      ta.style.overflowX = 'hidden';
      ta.style.wordWrap = 'break-word';
    }

    init();
  }

  function destroy(ta) {
    if (!(ta && ta.nodeName && ta.nodeName === 'TEXTAREA')) return;
    var evt = document.createEvent('Event');
    evt.initEvent('autosize:destroy', true, false);
    ta.dispatchEvent(evt);
  }

  function update(ta) {
    if (!(ta && ta.nodeName && ta.nodeName === 'TEXTAREA')) return;
    var evt = document.createEvent('Event');
    evt.initEvent('autosize:update', true, false);
    ta.dispatchEvent(evt);
  }

  var autosize = null;

  // Do nothing in Node.js environment and IE8 (or lower)
  if (typeof window === 'undefined' || typeof window.getComputedStyle !== 'function') {
    autosize = function (el) {
      return el;
    };
    autosize.destroy = function (el) {
      return el;
    };
    autosize.update = function (el) {
      return el;
    };
  } else {
    autosize = function (el, options) {
      if (el) {
        Array.prototype.forEach.call(el.length ? el : [el], function (x) {
          return assign(x, options);
        });
      }
      return el;
    };
    autosize.destroy = function (el) {
      if (el) {
        Array.prototype.forEach.call(el.length ? el : [el], destroy);
      }
      return el;
    };
    autosize.update = function (el) {
      if (el) {
        Array.prototype.forEach.call(el.length ? el : [el], update);
      }
      return el;
    };
  }

  module.exports = autosize;
});
};


modules['helpcenter/primary/plugins/froala/char_counter'] = function(require, exports, module, modules) {
/*!
 * froala_editor v2.3.4 (https://www.froala.com/wysiwyg-editor)
 * License https://froala.com/wysiwyg-editor/terms/
 * Copyright 2014-2016 Froala Labs
 */

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof module === 'object' && module.exports) {
        // Node/CommonJS
        module.exports = function( root, jQuery ) {
            if ( jQuery === undefined ) {
                // require('jQuery') returns a factory that requires window to
                // build a jQuery instance, we normalize how we use modules
                // that require this pattern but the window provided is a noop
                // if it's defined (how jquery works)
                if ( typeof window !== 'undefined' ) {
                    jQuery = require('jquery');
                }
                else {
                    jQuery = require('jquery')(root);
                }
            }
            factory(jQuery);
            return jQuery;
        };
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {

  'use strict';

  // Extend defaults.
  $.extend($.FE.DEFAULTS, {
    charCounterMax: -1,
    charCounterCount: true
  });


  $.FE.PLUGINS.charCounter = function (editor) {
    var $counter;

    /**
     * Get the char number.
     */
    function count () {
      return editor.$el.text().length;
    }

    /**
     * Check chars on typing.
     */
    function _checkCharNumber (e) {
      // Continue if infinite characters;
      if (editor.opts.charCounterMax < 0) return true;

      // Continue if enough characters.
      if (count() < editor.opts.charCounterMax) return true;

      // Stop if the key will produce a new char.
      var keyCode = e.which;
      if (!editor.keys.ctrlKey(e) && editor.keys.isCharacter(keyCode)) {
        e.preventDefault();
        e.stopPropagation();
        editor.events.trigger('charCounter.exceeded');
        return false;
      }

      return true;
    }

    /**
     * Check chars on paste.
     */
    function _checkCharNumberOnPaste (html) {
      if (editor.opts.charCounterMax < 0) return html;

      var len = $('<div>').html(html).text().length;
      if (len + count() <= editor.opts.charCounterMax) return html;

      editor.events.trigger('charCounter.exceeded');

      return '';
    }

    /**
     * Update the char counter.
     */
    function _updateCharNumber () {
      if (editor.opts.charCounterCount) {
        var chars = count() + (editor.opts.charCounterMax > 0 ?  '/' + editor.opts.charCounterMax : '');

        $counter.text(chars);

        if (editor.opts.toolbarBottom) {
          $counter.css('margin-bottom', editor.$tb.outerHeight(true))
        }

        // Scroll size correction.
        var scroll_size = editor.$wp.get(0).offsetWidth - editor.$wp.get(0).clientWidth;
        if (scroll_size >= 0) {
          if (editor.opts.direction == 'rtl') {
            $counter.css('margin-left', scroll_size);
          }
          else {
            $counter.css('margin-right', scroll_size);
          }
        }
      }
    }

    /*
     * Initialize.
     */
    function _init () {
      if (!editor.$wp) return false;

      if (!editor.opts.charCounterCount) return false;

      $counter = $('<span class="fr-counter"></span>');
      $counter.css('bottom', editor.$wp.css('border-bottom-width'));
      editor.$box.append($counter);

      editor.events.on('keydown', _checkCharNumber, true);
      editor.events.on('paste.afterCleanup', _checkCharNumberOnPaste);
      editor.events.on('keyup contentChanged', function () {
        editor.events.trigger('charCounter.update');
      });

      editor.events.on('charCounter.update', _updateCharNumber);
      editor.events.trigger('charCounter.update');

      editor.events.on('destroy', function () {
        $(editor.o_win).off('resize.char' + editor.id);
        $counter.removeData().remove();
        $counter = null;
      });
    }

    return {
      _init: _init,
      count: count
    }
  }

}));

};


modules['helpcenter/primary/plugins/tags/ajax'] = function(require, exports, module, modules) {
var jQuery = require('jquery');
/**
 * jQuery TextExt Plugin
 * http://textextjs.com
 *
 * @version 1.3.1
 * @copyright Copyright (C) 2011 Alex Gorbatchev. All rights reserved.
 * @license MIT License
 */
(function($)
{
	/**
	 * AJAX plugin is very useful if you want to load list of items from a data point and pass it
	 * to the Autocomplete or Filter plugins.
	 *
	 * Because it meant to be as a helper method for either Autocomplete or Filter plugin, without
	 * either of these two present AJAX plugin won't do anything.
	 *
	 * @author agorbatchev
	 * @date 2011/08/16
	 * @id TextExtAjax
	 */
	function TextExtAjax() {};

	$.fn.textext.TextExtAjax = TextExtAjax;
	$.fn.textext.addPlugin('ajax', TextExtAjax);

	var p = TextExtAjax.prototype,

		/**
		 * AJAX plugin options are grouped under `ajax` when passed to the `$().textext()` function. Be
		 * mindful that the whole `ajax` object is also passed to jQuery `$.ajax` call which means that
		 * you can change all jQuery options as well. Please refer to the jQuery documentation on how
		 * to set url and all other parameters. For example:
		 *
		 *     $('textarea').textext({
		 *         plugins: 'ajax',
		 *         ajax: {
		 *             url: 'http://...'
		 *         }
		 *     })
		 *
		 * **Important**: Because it's necessary to pass options to `jQuery.ajax()` in a single object,
		 * all jQuery related AJAX options like `url`, `dataType`, etc **must** be within the `ajax` object.
		 * This is the exception to general rule that TextExt options can be specified in dot or camel case 
		 * notation.
		 * 
		 * @author agorbatchev
		 * @date 2011/08/16
		 * @id TextExtAjax.options
		 */

		/**
		 * By default, when user starts typing into the text input, AJAX plugin will start making requests
		 * to the `url` that you have specified and will pass whatever user has typed so far as a parameter
		 * named `q`, eg `?q=foo`.
		 *
		 * If you wish to change this behaviour, you can pass a function as a value for this option which
		 * takes one argument (the user input) and should return a key/value object that will be converted
		 * to the request parameters. For example:
		 *
		 *     'dataCallback' : function(query)
		 *     {
		 *         return { 'search' : query };
		 *     } 
		 *
		 * @name ajax.data.callback
		 * @default null
		 * @author agorbatchev
		 * @date 2011/08/16
		 * @id TextExtAjax.options.data.callback
		 */
		OPT_DATA_CALLBACK = 'ajax.data.callback',
		
		/**
		 * By default, the server end point is constantly being reloaded whenever user changes the value
		 * in the text input. If you'd rather have the client do result filtering, you can return all
		 * possible results from the server and cache them on the client by setting this option to `true`.
		 *
		 * In such a case, only one call to the server will be made and filtering will be performed on
		 * the client side using `ItemManager` attached to the core.
		 *
		 * @name ajax.data.results
		 * @default false
		 * @author agorbatchev
		 * @date 2011/08/16
		 * @id TextExtAjax.options.cache.results
		 */
		OPT_CACHE_RESULTS = 'ajax.cache.results',
		
		/**
		 * The loading message delay is set in seconds and will specify how long it would take before
		 * user sees the message. If you don't want user to ever see this message, set the option value
		 * to `Number.MAX_VALUE`.
		 *
		 * @name ajax.loading.delay
		 * @default 0.5
		 * @author agorbatchev
		 * @date 2011/08/16
		 * @id TextExtAjax.options.loading.delay
		 */
		OPT_LOADING_DELAY = 'ajax.loading.delay',

		/**
		 * Whenever an AJAX request is made and the server takes more than the number of seconds specified
		 * in `ajax.loading.delay` to respond, the message specified in this option will appear in the drop
		 * down.
		 *
		 * @name ajax.loading.message
		 * @default "Loading..."
		 * @author agorbatchev
		 * @date 2011/08/17
		 * @id TextExtAjax.options.loading.message
		 */
		OPT_LOADING_MESSAGE = 'ajax.loading.message',

		/**
		 * When user is typing in or otherwise changing the value of the text input, it's undesirable to make
		 * an AJAX request for every keystroke. Instead it's more conservative to send a request every number
		 * of seconds while user is typing the value. This number of seconds is specified by the `ajax.type.delay`
		 * option.
		 *
		 * @name ajax.type.delay
		 * @default 0.5
		 * @author agorbatchev
		 * @date 2011/08/17
		 * @id TextExtAjax.options.type.delay
		 */
		OPT_TYPE_DELAY = 'ajax.type.delay',

		/**
		 * AJAX plugin dispatches or reacts to the following events.
		 *
		 * @author agorbatchev
		 * @date 2011/08/17
		 * @id TextExtAjax.events
		 */

		/**
		 * AJAX plugin reacts to the `getSuggestions` event dispatched by the Autocomplete plugin.
		 *
		 * @name getSuggestions
		 * @author agorbatchev
		 * @date 2011/08/17
		 * @id TextExtAjax.events.getSuggestions
		 */

		/**
		 * In the event of successful AJAX request, the AJAX coponent dispatches the `setSuggestions`
		 * event meant to be recieved by the Autocomplete plugin.
		 *
		 * @name setSuggestions
		 * @author agorbatchev
		 * @date 2011/08/17
		 * @id TextExtAjax.events.setSuggestions
		 */
		EVENT_SET_SUGGESTION = 'setSuggestions',

		/**
		 * AJAX plugin dispatches the `showDropdown` event which Autocomplete plugin is expecting.
		 * This is used to temporarily show the loading message if the AJAX request is taking longer
		 * than expected.
		 *
		 * @name showDropdown
		 * @author agorbatchev
		 * @date 2011/08/17
		 * @id TextExtAjax.events.showDropdown
		 */
		EVENT_SHOW_DROPDOWN = 'showDropdown',

		TIMER_LOADING = 'loading',

		DEFAULT_OPTS = {
			ajax : {
				typeDelay      : 0.5,
				loadingMessage : 'Loading...',
				loadingDelay   : 0.5,
				cacheResults   : false,
				dataCallback   : null
			}
		}
		;

	/**
	 * Initialization method called by the core during plugin instantiation.
	 *
	 * @signature TextExtAjax.init(core)
	 *
	 * @param core {TextExt} Instance of the TextExt core class.
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAjax.init
	 */
	p.init = function(core)
	{
		var self = this;

		self.baseInit(core, DEFAULT_OPTS);

		self.on({
			getSuggestions : self.onGetSuggestions
		});

		self._suggestions = null;
	};

	/**
	 * Performas an async AJAX with specified options.
	 *
	 * @signature TextExtAjax.load(query)
	 *
	 * @param query {String} Value that user has typed into the text area which is
	 * presumably the query.
	 *
	 * @author agorbatchev
	 * @date 2011/08/14
	 * @id TextExtAjax.load
	 */
	p.load = function(query)
	{
		var self         = this,
			dataCallback = self.opts(OPT_DATA_CALLBACK) || function(query) { return { name : query } },
			opts
			;

		opts = $.extend(true,
			{
				data    : dataCallback(query),
				success : function(data) { self.onComplete(data, query) },
				error   : function(jqXHR, message) { console.error(message, query) }
			}, 
			self.opts('ajax')
		);

		$.ajax(opts);
	};

	/**
	 * Successful call AJAX handler. Takes the data that came back from AJAX and the
	 * original query that was used to make the call.
	 *
	 * @signature TextExtAjax.onComplete(data, query)
	 *
	 * @param data {Object} Data loaded from the server, should be an Array of strings
	 * by default or whatever data structure your custom `ItemManager` implements.
	 *
	 * @param query {String} Query string, ie whatever user has typed in.
	 *
	 * @author agorbatchev
	 * @date 2011/08/14
	 * @id TextExtAjax.onComplete
	 */
	p.onComplete = function(data, query)
	{

		var resultArray = [];
		var dataObject = data.data;
		for (var i = 0; i < dataObject.length; i++) {
			if (data.resource === 'team') {
				resultArray.push(dataObject[i].title);
			} else {
				resultArray.push(dataObject[i].name);
			}
		}

		var self   = this,
			result = resultArray;

		self.dontShowLoading();

		// If results are expected to be cached, then we store the original
		// data set and return the filtered one based on the original query.
		// That means we do filtering on the client side, instead of the
		// server side.

		self._suggestions = result;
		result = self.itemManager().filter(result, query);

		self.trigger(EVENT_SET_SUGGESTION, { result : result });
	};

	/**
	 * If show loading message timer was started, calling this function disables it,
	 * otherwise nothing else happens.
	 *
	 * @signature TextExtAjax.dontShowLoading()
	 *
	 * @author agorbatchev
	 * @date 2011/08/16
	 * @id TextExtAjax.dontShowLoading
	 */
	p.dontShowLoading = function()
	{
		this.stopTimer(TIMER_LOADING);
	};

	/**
	 * Shows message specified in `ajax.loading.message` if loading data takes more than
	 * number of seconds specified in `ajax.loading.delay`.
	 *
	 * @signature TextExtAjax.showLoading()
	 *
	 * @author agorbatchev
	 * @date 2011/08/15
	 * @id TextExtAjax.showLoading
	 */
	p.showLoading = function()
	{
		var self = this;

		self.dontShowLoading();
		self.startTimer(
			TIMER_LOADING,
			self.opts(OPT_LOADING_DELAY),
			function()
			{
				self.trigger(EVENT_SHOW_DROPDOWN, function(autocomplete)
				{
					autocomplete.clearItems();
					var node = autocomplete.addDropdownItem(self.opts(OPT_LOADING_MESSAGE));
					node.addClass('text-loading');
				});
			}
		);
	};

	/**
	 * Reacts to the `getSuggestions` event and begin loading suggestions. If
	 * `ajax.cache.results` is specified, all calls after the first one will use
	 * cached data and filter it with the `core.itemManager.filter()`.
	 *
	 * @signature TextExtAjax.onGetSuggestions(e, data)
	 *
	 * @param e {Object} jQuery event.
	 * @param data {Object} Data structure passed with the `getSuggestions` event
	 * which contains the user query, eg `{ query : "..." }`.
	 *
	 * @author agorbatchev
	 * @date 2011/08/15
	 * @id TextExtAjax.onGetSuggestions
	 */
	p.onGetSuggestions = function(e, data)
	{
		var self        = this,
			suggestions = self._suggestions,
			query       = (data || {}).query || '';

		if (query.length) {
			self.startTimer(
				'ajax',
				self.opts(OPT_TYPE_DELAY),
				function()
				{
					self.showLoading();
					self.load(query);
				}
			);
		}
	};
})(jQuery);

};


modules['helpcenter/primary/models/retrieve-comment'] = function(require, exports, module, modules) {
module.exports = Backbone.Model.extend({
    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @param  {Object} options
     */
    initialize: function (options) {
    	this.articleId = options.articleId;
    	this.commentId = options.commentId;
        if (options.unlike) {
            this.idAttribute = 'commentId';
        }
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @return {Object}
     */
    url: function() {
    	return _Payload.portalpath + 'api/v1/articles/' + this.articleId + '/comments/' + this.commentId + '.json?include=*'
    }
});
};


modules['helpcenter/primary/plugins/autocomplete'] = function(require, exports, module, modules) {
var counts = require('helpcenter/primary/services/counts');

(function($, Backbone) {
      /**
       * View for an interactive list.
       * To use pass the following options:
       * - template (JST/Underscore template for each line item)
       * - collection: Optional Backbone collection
       * - url: Url for collection
       * - limit: Optional limit of items to display
       * - filter: Optional filter method that will be applied to the collection before creating the list
       * - click: Optional click callback for each item. receives 2 args, model and index
       * Instantiate and attach render() to any event of your choosing
       */
      Backbone.InteractiveList = Backbone.View.extend({
        initialize: function(options) {
          this.options = options;
          this.template = options.template;
          this.timer = null;
          if (!this.collection) {
            this.collection = new Backbone.Collection();
          }
          this.collection.url = options.url;
        },

        render: function(allowServer) {
          var view = this,
              click = this.options.click,
              filter = this.options.filter,
              limit = 5;
          this.refresh(function() {
            // append items to el
            view.$el.find('.js-ac-item').remove();
            if (this.collection.length==0) {
              view.$el.removeClass('searching').addClass('no-results');
            } else {
              var lis = document.createDocumentFragment();
              _.each(this.collection.filter(function(model) {
                return !_.isFunction(filter) || filter.call(view, model);
              }), function(model, i) {
                if (!limit || i < limit) {
                  var $li = $(view.template(model.attributes));
                  if (_.isFunction(click)) {
                    $li.on('mousedown', function() {
                      click.call(view, model, i);
                    }).on('mouseover', function() {
                      $(this).siblings().removeClass('selected');
                      $(this).addClass('selected');
                    });
                  }
                  lis.appendChild($li[0]);
                }
              });
              view.$el.append(lis);
              var inputParent = view.$el[0].parentElement;

              // Show all results link
              var totalCount = counts.get();
              if (totalCount > 5) {
                var seeAll = _.template($("#tpl-all-results").html());
                view.$el.append(seeAll({ term: $(inputParent).find('.js-search-input').val(), totalCount: totalCount - 5 }));
                $('.js-show-all').on('mouseover', function() {
                    $(this).siblings().removeClass('selected');
                    $(this).addClass('selected');
                  });
              }

              this.$el.show().removeClass('searching no-results').find(".js-timeago").timeago();
            }
            this.trigger('render');

          }, allowServer);
          return this;
        },

        refresh: function(callback, allowServer) {
          var view = this;
          clearTimeout(this.timer);

            this.timer = setTimeout(function() {
              // fetch collection from server and render it
              view.collection.fetch({
                success: function() {
                  view.lastUrl = _.result(view.collection, 'url');
                  callback.call(view);
                },
                error: function(collection, response) {
                  // Remove result items
                  view.$el.find('.js-ac-item').remove();

                  // Remove previous error message
                  $('.search__error-msg').remove();

                  // Show error message
                  var errorTpl = _.template($('#tpl-search-error').html());
                  var notifications = response.responseJSON.notifications;
                  $('.js-autoresults')
                    .removeClass('searching')
                    .addClass('has-error')
                    .append(errorTpl({ errorMessage: notifications ? notifications[0].message : null }));
                }
              });
            }, this.options.delay);
        }
      });

      /**
       * View for an autocomplete.
       * To use pass the same options as InteractiveList plus:
       * - value: Function that returns the value to be used as the input's value when the model is selected. May be a function that accepts a
           model as its argument or a string naming a model method.
       * - results: Optional results element. If not passed, one will be created and appended to body.
       */
      Backbone.AutocompleteList = Backbone.View.extend({
        initialize: function(options) {
          this.options = options;
          var view = this;
          this.term = this.$el.val();
          if (!this.collection) {
            this.collection = new Backbone.Collection();
          }
          this.collection.url = options.url;
          this.shortList = this.collection ? _.clone(this.collection.models) : [];

          // if no filter method passed use standard case-insensitive contains using the input value
          if (this.options.filter===undefined) {
            this.options.filter = function(model) {
              return view.options.value.call(model, model).toLowerCase().indexOf(view.$el.val().toLowerCase())!=-1;
            };
          }

          this.options.click = this.options.click || _.bind(function(model) {
            this.$selected().removeClass('selected');
            this.term = this.$el.val();
            this.$el.focus();
            this.trigger('selected', model);
          }, this);

          // append 'contact support' button if homepage
          if (this.$el.hasClass('inputaddon__field--big')) {
            // create the results element if not passed as an option
            this.results = options.results || $('<div>', { 'class': 'autocomplete js-autoresults' }).appendTo($('#search--home'));
            $('.autocomplete').append(_.template( $("#tpl-contact-support").html()));
          }
          else if (this.$el.hasClass('inputaddon__field--wide')) {
            // create the results element if not passed as an option
            this.results = options.results || $('<div>', { 'class': 'autocomplete--minimal js-autoresults' }).appendTo($('#header__search'));
            $('.autocomplete--minimal').append(_.template( $("#tpl-autocomplete-loader").html()));
          }
          else if (this.$el.hasClass('js-mobile-search')) {
            // create the results element if not passed as an option
            this.results = options.results || $('<div>', { 'class': 'autocomplete--minimal js-autoresults' }).appendTo($('#mobile-search'));
            $('.autocomplete--minimal').append(_.template( $("#tpl-autocomplete-loader").html()));
          }

          $(document).click(function() {
            $('.js-autoresults').hide();
            $('.js-show-all').remove();
          });
          $('.js-autoresults, #search, #search--mobile').click(function(e) {
            e.stopPropagation();
          });

          this.resultsView = new Backbone.InteractiveList(_.defaults({ el: this.results }, this.options));
        },

        events: {
          keyup: 'keyup',
          keydown: 'keydown'
        },

        $selected: function() {
          return this.resultsView.$('.selected');
        },

        keyup: function() {
          var resultsView = this.resultsView;
          if (this.term != this.$el.val().trim()) {
            this.term = this.$el.val();
            this.collection.reset(this.shortList);
            if (this.term.length >= this.options.minLength) {
              resultsView.render(!this.options.minLength || this.term.length >= this.options.minLength);
              this.results.show().addClass('searching').removeClass('no-results has-error');
            } else {
              this.results.hide().removeClass('searching no-results has-error');
              $('.js-show-all').remove();
            }
          }
          return true;
        },

        keydown: function(e) {
          // Esc
          if (e.keyCode == 27) {
            this.blur();
            return false;
          }
          // Enter
          if (e.keyCode == 13 || e.keyCode == 9) {
            // if an item is selected, act like it was clicked
            if (this.$selected().length > 0 && this.$selected().hasClass('autocomplete__item')) {
              location.href = this.$selected().find('a').attr('href');
              this.$selected().mousedown();
              return false;
            }
          }
          // Down
          if (e.keyCode == 40) {
            this.next();
            return false;
          }
          // Up
          if (e.keyCode == 38) {
            this.previous();
            return false;
          }
          return true;
        },

        next: function() {
          var next = this.$selected().removeClass('selected').next('.js-ac-item');
          if (next.length==0) { next = this.results.children('.js-ac-item').first(); }
          next.addClass('selected');
          this.scroll();
        },

        previous: function() {
          var prev = this.$selected().removeClass('selected').prev('.js-ac-item');
          if (prev.length==0) { prev = this.results.children().last(); }
          prev.addClass('selected');
          this.scroll();
        },

        blur: function(e) {
          this.results.hide();
        },

        // scroll till selected element is in view
        scroll: function() {
          if (this.$selected().length==1) {
            if (this.$selected().position().top + this.$selected().height() > this.results.height()) {
              this.results.scrollTop(this.results.scrollTop() + this.$selected().position().top + this.$selected().height() - this.results.height());
            } else if (this.$selected().position().top < 0) {
              this.results.scrollTop(this.results.scrollTop() + this.$selected().position().top);
            }
          }
        },
      });
    }(window.$, window.Backbone));
};


modules['helpcenter/primary/models/session'] = function(require, exports, module, modules) {
var instance = null;

var Session = Backbone.Model.extend({
	defaults: {
		id: null
	},

	url: function() {
		return _Payload.portalpath + 'api/v1/session.json?include=*';
	}
});

module.exports = instance || (instance = new Session());
};


modules['helpcenter/primary/views/validate'] = function(require, exports, module, modules) {
module.exports = Backbone.View.extend({
	events: {

		// Search forms
		'submit #search-form'           : 'fieldSubmit',
		'keypress #search'              : 'fieldTyping',

		// General forms
		'submit .js-validate'           : 'submit',
		'keypress .js-required-field'   : 'typing',
		'change input[type="file"]'     : 'checkSize',
		'change input[type="checkbox"]' : 'removeError',
		'change input[type="radio"]'    : 'removeError',
		'click .dd__link'               : 'removeError',
		'blur .js-required-field:not(.js-noblur)' : 'blur',

		// Forms with only one input field
		'submit .js-validate--field'    : 'fieldSubmit',
		'keypress .js-required-input'   : 'fieldTyping'
	},

	/**
	 * @author Werner Garcia <werner.garcia@crossover.com>
	 * @description Validates file upload size
	 * @param  event
	 */
	checkSize: function(e) {
		if ($('input[name="maxuploadsize"]').length === 0) {
			return;
		}

		const target = $(e.target);
		const maxSize = $('input[name="maxuploadsize"]').val();
		let count = $(target)[0].files.length;
		let totalFilesSize = 0;
		for (let i = 0; i < count; i++) {
			const file = $(target)[0].files[i];
			totalFilesSize += file.size;
		}
		
		if (totalFilesSize > maxSize) {
			e.preventDefault();
			$(e.target).parents('.textfield__label').addClass('textfield__label--error');
			$(e.target).parents('.textfield__label').find('.textfield__error-msg').html('The attachments are too large to be processed. The maximum file size is ' + (maxSize / 1000000) + ' MB');
			return;
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Checks if all the required fields are filled
	 * @param  event
	 */
	submit: function(e) {
		var valid = true;
		var target = $(e.target);

		// Validate text fields
		target.find('.js-required-field').each(function() {
		    if (!$.trim($(this).val()).length) {
		        e.preventDefault();
		        $(this).closest('.textfield__label').addClass('textfield__label--error');
		        valid = false;
		    }
		});

		// Validate file upload fields
		let totalFilesSize = 0;
		const maxSize = $('input[name="maxuploadsize"]').val();
		target.find('.file-input').each(function() {
			if ($('input[name="maxuploadsize"]').length === 0) {
				return;
			}

			let count = $(this)[0].files.length;
			for (let i = 0; i < count; i++) {
				const file = $(this)[0].files[i];
				totalFilesSize += file.size;
			}
			
			if (totalFilesSize > maxSize) {
				e.preventDefault();
				$(e.target).parents('.textfield__label').addClass('textfield__label--error');
				$(e.target).parents('.textfield__label').find('.textfield__error-msg').html('The attachments are too large to be processed. The maximum file size is ' + (maxSize / 1000000) + ' MB');
				valid = false;
			}
			
		});

		// Validate checkbox/radio button
		if (target.find('.js-required-checkbox').length) {
			target.find('.js-required-checkbox').each(function() {
			    if (!$(this).find('input').serializeArray().length) {
			        e.preventDefault();
			        $(this).closest('.textfield__label').addClass('textfield__label--error');
			        valid = false;
			    }
			});
		}

		if (valid) {
			$(e.target).find('input[type="submit"]').prop('disabled', 'true').addClass('button--loading');
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Adds error class if user focuses out without filling a required field
	 * @param  event
	 */
	blur: function(e) {
		if(!$.trim(e.target.value).length) {
		    $(e.target).parent('.textfield__label').addClass('textfield__label--error');
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Removes error class if user begins typing
	 * @param  event
	 */
	typing: function(e) {
		var label = $(e.target).parent('.textfield__label');
		if (label.hasClass('textfield__label--error')) {
		    label.removeClass('textfield__label--error');
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Validates if the field is filled in a single field form
	 * @param  event
	 */
	fieldSubmit: function(e) {
		var field = $(e.target).find('.js-required-input');
		if (!$.trim(field.val()).length) {
			e.preventDefault();
			field.addClass('textfield--error');
			return false;
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Removes error class if user begins typing in a single field form
	 * @param  event
	 */
	fieldTyping: function(e) {
		var field = $(e.target);
		if (field.hasClass('textfield--error')) {
		    field.removeClass('textfield--error');
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Removes error class form label
	 * @param  e
	 */
	removeError: function(e) {
		$(e.target).closest('.textfield__label').removeClass('textfield__label--error');
	}
});

};


modules['helpcenter/primary/views/manage/section'] = function(require, exports, module, modules) {
var autosize = require('helpcenter/primary/plugins/autosize');
var redirect = require('helpcenter/primary/views/redirect');
var Sortable = require('helpcenter/primary/plugins/sortable');

// Load tags plugin
require('helpcenter/primary/plugins/tags/core');
require('helpcenter/primary/plugins/tags/autocomplete');
require('helpcenter/primary/plugins/tags');
require('helpcenter/primary/plugins/tags/ajax');

module.exports = Backbone.View.extend({
	model: require('helpcenter/primary/models/manage/content'),

	el: $('#section'),
	titles: [],
	descriptions: [],

	events: {
		'click #add-section': 'add',
		'click #edit-content': 'edit',
		'click #cancel-edit': 'cancelEdit',
		'click #prompt': 'prompt',
		'click #cancel-delete': 'cancelDelete',
		'click #save': 'update',
		'click #delete': 'delete',
		'click .js-locale-selector': 'toggleLocaleMenu',
		'click .locale__item': 'switchLocale',
		'change #section-order': 'switchSectionOrder',
		'change #section-visibility': 'showTagsAutocomplete'
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Gets options from router while instantiating view
	 * @param  {Object}
	 */
	initialize: function(options) {
		var view = this;
		this.options = options || {};
		this.itemId = this.options.itemId.split('-')[0];
		this.lastUpdated = null;
		this.articleOrder = [];

		// Initiate textarea auto resizer
		autosize($('.textfield--heading'));

		// Hide locale menu
		$(document).click(function() {
			$('.js-locale-list').removeClass('is-open');
		});

		// Initialize section tags for logged in users
		if ($('#section-tags').length) {
			$('#section-tags').textext({
				plugins: 'tags autocomplete ajax',
				ajax: {
					url: _Payload.portalpath + 'api/v1/autocomplete/tags.json?include=*',
					dataType: 'json'
				},
				ext: {
					tags: {
						onEnterKeyPress: function() {}
					},
					autocomplete: {
						onEnterKeyPress: function() {},
						onClick: function (e) {
							var self   = this,
								target = $(e.target),
								elm = $('#section-tags')
								;
							if(target.is('.text-suggestion') || target.is('.textlabel')) {
								if(self.isDropdownVisible()) {
									self.selectFromDropdown();
									var tagValue = self.val().trim();
									elm.val('').textext()[0]
										.tags().addTags([tagValue]);
										self.onBlur();
								}
							}

							if (self.core().hasPlugin('tags'))
								self.val('');
						},
						onAnyKeyUp: function(e, keyCode) {
							var self = this;
							var isFunctionKey = self.opts('keys.' + keyCode) != null;

							if(self.val().length > 0 && !isFunctionKey && keyCode !== 13 && keyCode !== 32)
								self.getSuggestions();

							let elm = $('#section-tags');

							if ((keyCode == 32 || keyCode == 13) && elm.val().trim().length) {
								var tagValue = self.val().trim();
								if(self.isDropdownVisible()) {
									self.selectFromDropdown();
									tagValue = self.val().trim();
								}
								if( $('#section-tags-container').find('#tags-array').val().match(new RegExp("(?:^|,)" + tagValue + "(?:,|$)"))) {
									return false;
								}
								elm.val('').textext()[0]
									.tags().addTags([tagValue]);
									self.onBlur();
							}
						}
					}
				}
			});
		}

		if ($('#section-agent-tags').length) {
			$('#section-agent-tags').textext({
				plugins: 'tags autocomplete ajax',
				ajax: {
					url: _Payload.portalpath + 'api/v1/autocomplete/tags.json?include=*',
					dataType: 'json'
				},
				ext: {
					tags: {
						onEnterKeyPress: function() {}
					},
					autocomplete: {
						onEnterKeyPress: function () {},
						renderSuggestions: function (suggestions) {
							var self = this;

							self.clearItems();

							var existingTags = $('#section-agent-tags-container').find('#tags-array').val();

							$.each(suggestions || [], function(index, item) {
								if (existingTags.match(new RegExp("(?:^|,)" + item + "(?:,|$)")) === null) {
									self.addSuggestion(item);
								}
							});
						},
						onClick: function (e) {
							var self   = this,
								target = $(e.target),
								elm = $('#section-agent-tags')
								;

							if(target.is('.text-suggestion') || target.is('.textlabel')) {
								if(self.isDropdownVisible()) {
									self.selectFromDropdown();
									var tagValue = self.val().trim();

									elm.val('').textext()[0]
										.tags().addTags([tagValue]);
										self.onBlur();
								}
							}

							if (self.core().hasPlugin('tags'))
								self.val('');
						},
						onAnyKeyUp: function(e, keyCode) {
							var self = this;
							var isFunctionKey = self.opts('keys.' + keyCode) != null;

							if(self.val().length > 0 && !isFunctionKey && keyCode !== 13 && keyCode !== 32)
								self.getSuggestions();

							var elm = $('#section-agent-tags');

							if ((keyCode == 32 || keyCode == 13) && elm.val().trim().length) {
								var tagValue = self.val().trim();
								if(self.isDropdownVisible()) {
									self.selectFromDropdown();
									tagValue = self.val().trim();
								}
								if( $('#section-agent-tags-container').find('#tags-array').val().match(new RegExp("(?:^|,)" + tagValue + "(?:,|$)"))) {
									return false;
								}
								elm.val('').textext()[0]
									.tags().addTags([tagValue]);
									self.onBlur();
							}
						}
					}
				}
			});
		}

		if ($('#section-agent-teams').length) {
			$('#section-agent-teams').textext({
				plugins: 'tags autocomplete',
				ext: {
					tags: {
						onEnterKeyPress: function() {}
					},
					autocomplete: {
						onEnterKeyPress: function () {},
						onClick: function (e) {
							var self   = this,
								target = $(e.target),
								elm = $('#section-agent-teams')
								;

							if(target.is('.text-suggestion') || target.is('.textlabel')) {
								if(self.isDropdownVisible()) {
									self.selectFromDropdown();
									var tagValue = self.val().trim();

									elm.val('').textext()[0]
										.tags().addTags([tagValue]);
										self.onBlur();
								}
							}

							if (self.core().hasPlugin('tags'))
								self.val('');
						},
						onAnyKeyUp: function(e, keyCode) {
							var self = this;
							var isFunctionKey = self.opts('keys.' + keyCode) != null;

							if(self.val().length > 0 && !isFunctionKey && keyCode !== 13 && keyCode !== 32)
								self.getSuggestions();

							var elm = $('#section-agent-teams');

							if ((keyCode == 32 || keyCode == 13) && elm.val().trim().length) {

								var tagValue = self.val().trim();
								if(!self.isDropdownVisible()) {
									return false;
								}

								self.selectFromDropdown();
								tagValue = self.val().trim();

								elm.val('').textext()[0]
									.tags().addTags([tagValue]);
									self.onBlur();
							}
							var teamPlaceholder = $('#section-team-placeholder').attr('placeholder');

							if(elm.textext()[0].tags().tagElements().length > 0) {
								elm.attr('placeholder', '');
							} else {
								elm.attr('placeholder', teamPlaceholder);
							}
						}
					}
				}
			}).bind('getSuggestions', function(e, data)
			{
					var list = [
							],
							textext = $(e.target).textext()[0],
							query = (data ? data.query.toLowerCase() : '') || ''
							;

					var teams = $('#section-agent-teams-container').find('#tags-array').val().split(',').map(function(team) {
						return team.trim().toLowerCase();
					});

					$('.section-agent-teamlist').children().each(function() {
						var team = $(this).text();
						var teamLowerCase = team.trim().toLowerCase();

						if(!teams.some(function(t) { return t === teamLowerCase })) {
							list.push(team);
						}
					});

					$(this).trigger(
						'setSuggestions',
						{ result : textext.itemManager().filter(list, query) }
					);
			});
		}

		// Get content for other locales
		if (!$('#add-section').length) {
			new this.model({
				'itemId': this.itemId,
				'item': 'sections',
				'query': 'update'
			}).fetch({
				success: function(response) {
					$.each(response.attributes.data.titles, function(index, val) {
						view.titles.push({
							"locale": val.locale,
							"translation": val.translation
						});
					});
					$.each(response.attributes.data.descriptions, function(index, val) {
						// the API might return null instead of
						// empty string which will fail during PUT
						// @see https://kayako.atlassian.net/browse/API-717
						if (val.translation === null) {
							val.translation = '';
						}

						view.descriptions.push({
							"locale": val.locale,
							"translation": val.translation
						});
					});
					$('#edit-content').removeClass('is-disabled');
				},
				error: function(response) {
					// Show error message
					$('#save-bar').addClass('admin-bar--error');
				}
			});
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Loads the editing view
	 */
	edit: function() {
		var view = this;

		// Store last updated timestamp
		this.lastUpdated = $('#last-updated').find('span').data('time');

		// Show moderate option in the sidebar
		if ($('#moderate').length) {
			$('#moderate').removeClass('u-hiddenvisually');
		} else {
			var moderateTpl = _.template($('#tpl-moderate').html());
			$('#aside--section').prepend(moderateTpl);
		}

		// Remove grey bar
		$('#edit-bar').remove();

		// Show blue bar
		var saveBarTpl = _.template($('#tpl-save-bar').html());
		$('#section').prepend(saveBarTpl({referrer: 'section'}));

		// Show input fields in place of static content
		$('#input-content').removeClass('u-hidden');
		$('#display--heading, #display--description').addClass('u-hidden');

		// Disable uneditable content
		$('#add-block, #listing, #pagination').addClass('u-hiddenvisually');

		// Initialize tags editor
		if (!$('#section-tags').textext().length) {
			$('#section-tags').textext({
				plugins: 'tags autocomplete ajax',
				ajax: {
					url: _Payload.portalpath + 'api/v1/autocomplete/tags.json?include=*',
					dataType: 'json'
				},
				ext: {
					tags: {
						onEnterKeyPress: function() {}
					},
					autocomplete: {
						onEnterKeyPress: function() {},
						onClick: function (e) {
							var self   = this,
								target = $(e.target),
								elm = $('#section-tags')
								;
							if(target.is('.text-suggestion') || target.is('.textlabel')) {
								if(self.isDropdownVisible()) {
									self.selectFromDropdown();
									var tagValue = self.val().trim();
									elm.val('').textext()[0]
										.tags().addTags([tagValue]);
										self.onBlur();
								}
							}

							if (self.core().hasPlugin('tags'))
								self.val('');
						},
						onAnyKeyUp: function(e, keyCode) {
							var self = this;
							var isFunctionKey = self.opts('keys.' + keyCode) != null;

							if(self.val().length > 0 && !isFunctionKey && keyCode !== 13 && keyCode !== 32)
								self.getSuggestions();

							let elm = $('#section-tags');

							if ((keyCode == 32 || keyCode == 13) && elm.val().trim().length) {
								var tagValue = self.val().trim();
								if(self.isDropdownVisible()) {
									self.selectFromDropdown();
									tagValue = self.val().trim();
								}
								if( $('#section-tags-container').find('#tags-array').val().match(new RegExp("(?:^|,)" + tagValue + "(?:,|$)"))) {
									return false;
								}
								elm.val('').textext()[0]
									.tags().addTags([tagValue]);
									self.onBlur();
							}
						}
					}
				}
			});
			if ($('#section-visibility').val() === 'AUTHORIZED') {
				$('#section-tags-container').removeClass('u-hiddenvisually');
				var top = $('.text-dropdown').css('top');
				if (top === '0px') {
					$('.text-dropdown').css('top', '36px');
				}
			}
		}
		if ($('#section-tags').textext().length && $('#current-section-visibility').text().trim() === 'AUTHORIZED') {
			var sectionTagsInstance = $('#section-tags').textext()[0]._plugins.tags
			var existingSectionTags = sectionTagsInstance.tagElements();
			for (var i = 0 ; i < existingSectionTags.length; i++) {
				sectionTagsInstance.removeTag($(existingSectionTags[i]))
			}
			var sectionTagItems = [];
			$('.section-tags').children().each(function() {
				sectionTagItems.push($(this).text());
			});
			sectionTagsInstance.addTags(sectionTagItems);
		}
		// Complete tags editor

		// Initiliaze tags editor for agents
		if (!$('#section-agent-tags').textext().length) {
			$('#section-agent-tags').textext({
				plugins: 'tags autocomplete ajax',
				ajax: {
					url: _Payload.portalpath + 'api/v1/autocomplete/tags.json?include=*',
					dataType: 'json'
				},
				ext: {
					tags: {
						onEnterKeyPress: function() {}
					},
					autocomplete: {
						onEnterKeyPress: function () {},
						renderSuggestions: function (suggestions) {
							var self = this;

							self.clearItems();

							var existingTags = $('#section-agent-tags-container').find('#tags-array').val();

							$.each(suggestions || [], function(index, item) {
								if (existingTags.match(new RegExp("(?:^|,)" + item + "(?:,|$)")) === null) {
									self.addSuggestion(item);
								}
							});
						},
						onClick: function (e) {
							var self   = this,
								target = $(e.target),
								elm = $('#section-agent-tags')
								;

							if(target.is('.text-suggestion') || target.is('.textlabel')) {
								if(self.isDropdownVisible()) {
									self.selectFromDropdown();
									var tagValue = self.val().trim();

									elm.val('').textext()[0]
										.tags().addTags([tagValue]);
										self.onBlur();
								}
							}

							if (self.core().hasPlugin('tags'))
								self.val('');
						},
						onAnyKeyUp: function(e, keyCode) {
							var self = this;
							var isFunctionKey = self.opts('keys.' + keyCode) != null;

							if(self.val().length > 0 && !isFunctionKey && keyCode !== 13 && keyCode !== 32)
								self.getSuggestions();

							var elm = $('#section-agent-tags');

							if ((keyCode == 32 || keyCode == 13) && elm.val().trim().length) {
								var tagValue = self.val().trim();
								if(self.isDropdownVisible()) {
									self.selectFromDropdown();
									tagValue = self.val().trim();
								}
								if( $('#section-agent-tags-container').find('#tags-array').val().match(new RegExp("(?:^|,)" + tagValue + "(?:,|$)"))) {
									return false;
								}
								elm.val('').textext()[0]
									.tags().addTags([tagValue]);
									self.onBlur();
							}
						}
					}
				}
			});
			if ($('#section-visibility').val() === 'AGENTS') {
				$('#section-agent-tags-container').removeClass('u-hiddenvisually');
				var top = $('.text-dropdown').css('top');
				if (top === '0px') {
					$('.text-dropdown').css('top', '36px');
				}
			}
		}
		if ($('#section-agent-tags').textext().length && $('#current-section-visibility').text().trim() === 'AGENTS') {
			var agentTagsInstance = $('#section-agent-tags').textext()[0]._plugins.tags
			var existingAgentTags = agentTagsInstance.tagElements();
			for (var i = 0 ; i < existingAgentTags.length; i++) {
				agentTagsInstance.removeTag($(existingAgentTags[i]))
			}
			var agentTagItems = [];
			$('.section-tags').children().each(function() {
				agentTagItems.push($(this).text());
			});
			agentTagsInstance.addTags(agentTagItems);
		}
		// Complete tags editor for agents


		// Initialize teams editor for agents
		if (!$('#section-agent-teams').textext().length) {
			$('#section-agent-teams').textext({
				plugins: 'tags autocomplete',
				ext: {
					tags: {
						onEnterKeyPress: function() {}
					},
					autocomplete: {
						onEnterKeyPress: function () {},
						onClick: function (e) {
							var self   = this,
								target = $(e.target),
								elm = $('#section-agent-teams')
								;

							if(target.is('.text-suggestion') || target.is('.textlabel')) {
								if(self.isDropdownVisible()) {
									self.selectFromDropdown();
									var tagValue = self.val().trim();

									elm.val('').textext()[0]
										.tags().addTags([tagValue]);
										self.onBlur();
								}
							}

							if (self.core().hasPlugin('tags'))
								self.val('');
						},
						onAnyKeyUp: function(e, keyCode) {
							var self = this;
							var isFunctionKey = self.opts('keys.' + keyCode) != null;

							if(self.val().length > 0 && !isFunctionKey && keyCode !== 13 && keyCode !== 32)
								self.getSuggestions();

							var elm = $('#section-agent-teams');

							if ((keyCode == 32 || keyCode == 13) && elm.val().trim().length) {
								var tagValue = self.val().trim();
								if(!self.isDropdownVisible()) {
									return false;
								}

								self.selectFromDropdown();
								tagValue = self.val().trim();

								elm.val('').textext()[0]
									.tags().addTags([tagValue]);
									self.onBlur();
							}

							var teamPlaceholder = $('#section-team-placeholder').attr('placeholder');
							if(elm.textext()[0].tags().tagElements().length > 0) {
								elm.attr('placeholder', '');
							} else {
								elm.attr('placeholder', teamPlaceholder);
							}
						}
					}
				}
			}).bind('getSuggestions', function(e, data)
			{
					var list = [
							],
							textext = $(e.target).textext()[0],
							query = (data ? data.query.toLowerCase() : '') || ''
							;

					var teams = $('#section-agent-teams-container').find('#tags-array').val().split(',').map(function(team) {
						return team.trim().toLowerCase();
					});

					$('.section-agent-teamlist').children().each(function() {
						var team = $(this).text();
						var teamLowerCase = team.trim().toLowerCase();

						if(!teams.some(function(t) { return t === teamLowerCase })) {
							list.push(team);
						}
					});

					$(this).trigger(
							'setSuggestions',
							{ result : textext.itemManager().filter(list, query) }
					);
			});
			if ($('#section-visibility').val() === 'AGENTS') {
				$('#section-agent-teams-container').removeClass('u-hiddenvisually');
				var top = $('.text-dropdown').css('top');
				if (top === '0px') {
					$('.text-dropdown').css('top', '36px');
				}
			}
		}
		if ($('#section-agent-teams').textext().length && $('#current-section-visibility').text().trim() === 'AGENTS') {
			var sectionTeamsInstance = $('#section-agent-teams').textext()[0]._plugins.tags
			var existingTeams = sectionTeamsInstance.tagElements();
			for (var i = 0 ; i < existingTeams.length; i++) {
				sectionTeamsInstance.removeTag($(existingTeams[i]))
			}
			var sectionTeamItems = [];
			var teamIds = [];
			$('.section-assigned-teams').children().each(function() {
				teamIds.push($(this).text());
			});
			$('.section-agent-teamlist').children().each(function() {
				if (teamIds.includes($(this).text())) {
					sectionTeamItems.push($(this).text());
				}
			});
			sectionTeamsInstance.addTags(sectionTeamItems);
			// Logic to check whether placeholder should be removed or not.
			var elm = $('#section-agent-teams');
			var teamPlaceholder = $('#section-team-placeholder').attr('placeholder');

			if(elm.textext()[0].tags().tagElements().length > 0) {
				elm.attr('placeholder', '');
			} else {
				elm.attr('placeholder', teamPlaceholder);
			}
		}
		// Complete teams editor for agents


		// Fill data into the editor
		var activeTitle = activeDescription = '';
		$.each(view.titles, function(index, val) {
			if (val.locale == _Payload.activelocale) {
				activeTitle = val.translation;
			}
		});
		$.each(view.descriptions, function(index, val) {
			if (val.locale == _Payload.activelocale) {
				activeDescription = val.translation;
			}
		});
		$('#input--heading').val(activeTitle);
		$('#input--description').val(activeDescription);

		var itemId = this.itemId;
		var articles = document.getElementById('article-reorder-listing');
		if (articles) {
			this.originalOrder = $('#article-reorder-listing')
				.children('li')
				.map(function(index, x) { return x.id; } )
				.toArray();
			this.articleOrder = this.originalOrder;

			this.sortable = Sortable.create(articles, {
				ghostClass: "sortable-listing-article-ghost",
				chosenClass: "sortable-listing-article-selected",
				animation: 150,
				group: "article-list--sort-order",
				store: {
					get: function(sortable) {
						return view.articleOrder;
					},
					set: function(sortable) {
						view.articleOrder = sortable.toArray();
					}
				}
			});
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Reverts the editing view
	 */
	cancelEdit: function() {
		// Remove blue bar and moderate options
		$('#save-bar').remove();
		$('#moderate').addClass('u-hiddenvisually');

		if (this.sortable) {
			this.sortable.sort(this.originalOrder);
			this.sortable.destroy();
		}

		// Show grey bar
		var editBarTpl = _.template($('#tpl-edit-bar').html());
		$('#section').prepend(editBarTpl({referrer: 'section', lastUpdated: this.lastUpdated}));
		$(".js-timeago").timeago();

		// Show static data inplace of input fields
		$('#input-content').addClass('u-hidden');
		$('#display--heading, #display--description').removeClass('u-hidden');

		// Remove disabled class from uneditable content
		$('#add-block, #listing, #pagination').removeClass('u-hiddenvisually');
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Checks if title and content of main locale are filled
	 * @return {Boolean}
	 */
	validate: function() {
		var view = this;
		var hasTitle = false;

		// Check if primary locale title is filled
		if (view.titles.length) {
			$.each(view.titles, function(index, el) {
				if (el.locale == _Payload.defaultlocale && el.translation.length) {
					hasTitle = true;
				}
			});
		}

		if (hasTitle) {
			$('#add-section').prop('disabled', 'true').addClass('button--loading');
			return true;
		} else {
			// Show error message
			$('#save-bar').addClass('admin-bar--required');
			return false;
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Pushes current locale data to titles[] and desctiptions[]
	 */
	pushData: function() {
		var view = this;
		var titleInArray, titleInArrayIndex, descInArray, descInArrayIndex, hasDefaultDesc;
		var activeLocale = $('.js-locale__item.is-active').clone().children().remove().end().text();
		$('#save-bar').removeClass('admin-bar--required');

		// Check if title is already there in array
		$.each(view.titles, function(index, value) {
			if (value.locale == activeLocale) {
				titleInArray = true;
				titleInArrayIndex = index;
			}
		});

		// Remove title from array if already present
		if (titleInArray) {
			view.titles.splice(titleInArrayIndex, 1);
		}

		// Check if description is already there in array
		$.each(view.descriptions, function(index, value) {
			if (value.locale == activeLocale) {
				descInArray = true;
				descInArrayIndex = index;
			}
		});

		// Remove description from array if already present
		if (descInArray) {
			view.descriptions.splice(descInArrayIndex, 1);
		}

		view.titles.push({
			"locale": activeLocale,
			"translation": $('#input--heading').val().trim()
		});

		view.descriptions.push({
			"locale": activeLocale,
			"translation": $('#input--description').val()
		});

		// Push empty description for default language
		// because API does not accept it's empty value
		$.each(view.descriptions, function(index, value) {
			if (value.locale == _Payload.defaultlocale) {
				hasDefaultDesc = true;
			}
		});
		if (!hasDefaultDesc) {
			view.descriptions.push({
				"locale": _Payload.defaultlocale,
				"translation": ''
			});
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Creates new section
	 */
	add: function() {
		var view = this;

		// Push current locale data
		view.pushData();

		// Figure out tags and teamIds
		var tags, teamIds = [];
		if ($('#section-visibility').val() === 'AUTHORIZED') {
			tags = $('#section-tags-container #tags-array').val()
		} else if ($('#section-visibility').val() === 'AGENTS') {
			tags = $('#section-agent-tags-container #tags-array').val()
			var teams = $('#section-agent-teams-container #tags-array').val()
			if (typeof teams !== 'undefined') {
				var teamList = teams.split(',')
				$('.section-agent-teamlist').children().each(function() {
					if (teamList.includes($(this).text())) {
						teamIds.push($(this).attr('data-id'));
					}
				});
			}
		}

		// Send to server if valid
		if (view.validate()) {
			// Disable buttons and show loading animation
			$('#cancel-edit').addClass('is-disabled');
			new this.model({
				'item': 'sections',
				'query': 'fetch'
			}).save({
				'titles': view.titles,
				'tags': tags,
				'team_ids': teamIds.join(','),
				'descriptions': view.descriptions,
				'category_id': $('#section-cat').val(),
				'visibility': $('#section-visibility').val(),
				'article_order_by': $('#section-order').val()
			}, {
				success: function(response) {
					// Redirect to section page
					$.each(response.attributes.data.slugs, function(index, el) {
						if (el.locale == _Payload.activelocale) {
							window.location.replace(_Payload.url + 'section/' + el.translation);
						} else if (el.locale == _Payload.defaultlocale) {
							window.location.replace(_Payload.url + 'section/' + el.translation);
						}
					});
				},
				error: function(model, xhr, response) {
					view.notSaved(xhr.status, xhr.responseJSON.errors[0]);
				}
			});
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Updates the section
	 */
	update: function() {
		var view = this;

		// Push current locale data
		view.pushData();

		// Figure out tags and teamIds
		var tags, teamIds = [];
		if ($('#section-visibility').val() === 'AUTHORIZED') {
			tags = $('#section-tags-container #tags-array').val()
		} else if ($('#section-visibility').val() === 'AGENTS') {
			tags = $('#section-agent-tags-container #tags-array').val()
			var teams = $('#section-agent-teams-container #tags-array').val()
			if (typeof teams !== 'undefined') {
				var teamList = teams.split(',')
				$('.section-agent-teamlist').children().each(function() {
					if (teamList.includes($(this).text())) {
						teamIds.push($(this).attr('data-id'));
					}
				});
			}
		}

		// Send to server if valid
		if (view.validate()) {
			// Disable buttons and show loading animation
			$('#cancel-edit').addClass('is-disabled');
			$('#save')
				.addClass('button--loading')
				.prop('disabled', 'true')
				.after(_.template($('#tpl-loader').html()));
			new this.model({
				'itemId': this.itemId,
				'item': 'sections',
				'query': 'update'
			}).save({
				'titles': view.titles,
				'tags': tags,
				'teamIds': teamIds,
				'descriptions': view.descriptions,
				'category_id': $('#section-cat').val(),
				'visibility': $('#section-visibility').val(),
				'article_order_by': $('#section-order').val()
			}, {
				success: function() {
					// Reload the page
					if ($('#section-order').val() === 'MANUAL' && view.sortable && !_.isEqual(view.originalOrder, view.articleOrder)) {
						var sortable = view.sortable;
						var data = {
							'article_ids': view.articleOrder.join(','),
							'section_id': view.itemId
						};

						$.ajax({
							type: 'PUT',
							url: '/api/v1/articles/reorder',
							headers: {'X-CSRF-Token': localStorage.getItem('ko:core:csrf') ? localStorage.getItem('ko:core:csrf').replace(/\"/g, '') : '', 'Content-Type': 'application/json'},
							data: JSON.stringify(data),
							success: function(res) {
								// store order to localStorage
								view.articleOrder = [];
								location.reload();
							},
							error: function(response) {
								sortable.sort(view.articleOrder);
								view.originalOrder = [];
								view.sortable.destroy();

								// display error
								var res = response.responseJSON;
								view.notSaved(res.status, res.errors[0]);
							}
						});
					} else {
						location.reload();
					}
				},
				error: function(model, xhr, response) {
					view.notSaved(xhr.status, xhr.responseJSON.errors[0]);
				}
			});
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Shows error if section fails to save
	 */
	notSaved: function(errorCode, errorMsg) {
		// Redirect if required
		if (errorCode == 404 || errorCode == 401) {
			redirect('section', view.options.itemId, errorCode);
		} else {
			// Show error message
			$('#save-bar').addClass('admin-bar--api-error');
			$('.admin-api-error').remove();

			var errorParameter = '';
			if (errorMsg.parameter) {
				errorParameter = '<span class="u-textbold u-capitalize">' + errorMsg.parameter + '</span>: ';
			}

			$('#save-bar__text').append('<span class="admin-api-error u-textwarning">' + errorParameter + '' + errorMsg.message + '.</span>');

			// Re-enable buttons and show loading animation
			$('#cancel-edit').removeClass('is-disabled');
			$('#save, #add-section').removeClass('is-disabled button--loading').removeAttr('disabled');
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Shows the modal
	 */
	prompt: function() {
		$('#prompt').addClass('is-disabled');
		var view = this;
		new this.model({
			'itemId': view.itemId,
			'item': 'sections',
			'query': 'update'
		}).fetch(
			{
				success: function(response) {
					var articleCount = response.attributes.data.total_articles;
					var modalTpl = _.template($('#tpl-delete-modal').html());
					$('#section').append(modalTpl({articleCount: articleCount}));
					$('#delete-modal').fadeIn(300);
					$(document).keyup(function(e) {
						if (e.keyCode == 27) {
							view.cancelDelete();
						}
					});
				},
				error: function(model, xhr, options) {
					// Redirect to signin page if session expired
					if (xhr.status === 401) {
						window.location.href = _Payload.url + 'login?return=' + _Payload.url + 'section/' + this.options.itemId;
					} else {
						$('#prompt').removeClass('is-disabled');
					}
				}
			});
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Hides the modal
	 */
	cancelDelete: function() {
		$('#prompt').removeClass('is-disabled');
		$('#delete-modal').fadeOut(200, function() {
			// Remove modal
			$(this).remove();
		});
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Deletes the section from database
	 */
	delete: function() {
		var view = this;
		var itemId = this.itemId;
		// Disable buttons and show loading animation
		$('#cancel-delete').addClass('is-disabled');
		$('#delete')
			.addClass('is-disabled button--loading')
			.prop('disabled', 'true')
			.after(_.template($('#tpl-loader').html()));

		// Get parent category slug
		new this.model({
			'itemId': itemId,
			'item': 'sections',
			'query': 'update'
		}).fetch(
			{
				success: function(response) {
					var catSlug;
					$.each(response.attributes.data.category.slugs, function(index, el) {
						if (el.locale == _Payload.activelocale) {
							catSlug = el.translation
						} else if (el.locale == _Payload.defaultlocale) {
							catSlug = el.translation
						}
					});

					// Delete from database
					new view.model({
						'itemId': itemId,
						'item': 'sections',
						'query': 'update'
					}).destroy({
						success: function(model, response) {
							// Redirect to parent category
							window.location.href = _Payload.url + 'category/' + catSlug;
						},
						error: function() {
							view.modalError();
						}
					});

				},
				error: function() {
					view.modalError();
				}
			});
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Show error message inside modal
	 */
	modalError: function() {
		// Show error message
		$('.modal__footer').addClass('modal__footer--error');

		// Re-enable buttons and hide loading animation
		$('#cancel-delete').removeClass('is-disabled');
		$('#delete')
			.removeClass('is-disabled button--loading')
			.removeAttr('disabled');
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Show/hide locale dropdown menu
	 */
	toggleLocaleMenu: function(e) {
		e.stopPropagation();
		$('.js-locale-list').toggleClass('is-open');
	},

	switchSectionOrder: function(e) {
		var value = e.currentTarget.value;
		if (value !== 'MANUAL') {
			$('#article-reorder-listing').addClass('u-hiddenvisually');
		} else {
			$('#article-reorder-listing').removeClass('u-hiddenvisually');
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Switches among active locales (Changes data of input fields)
	 * @param  {Object} e
	 */
	switchLocale: function(e) {
		var view = this;
		var titleInArray, titleInArrayIndex, descInArray, descInArrayIndex;
		var selectedTitle = selectedDesc = '';
		var selected = $(e.target).closest('.js-locale__item');

		if (!selected.hasClass('is-active')) {
			var activeLocale = $('.js-locale__item.is-active').clone().children().remove().end().text();
			var activeTitle = $('#input--heading').val();
			var activeDesc = $('#input--description').val();
			var selectedLocale = selected.clone().children().remove().end().text();

			// Check if title is already there in array
			$.each(view.titles, function(index, value) {
				if (value.locale == activeLocale) {
					titleInArray = true;
					titleInArrayIndex = index;
				}
				if (value.locale == selectedLocale) {
					selectedTitle = value.translation;
				}
			});

			// Remove title from array if already present
			if (titleInArray) {
				view.titles.splice(titleInArrayIndex, 1);
			}

			// Check if description is already there in array
			$.each(view.descriptions, function(index, value) {
				if (value.locale == activeLocale) {
					descInArray = true;
					descInArrayIndex = index;
				}
				if (value.locale == selectedLocale) {
					selectedDesc = value.translation;
				}
			});

			// Remove description from array if already present
			if (descInArray) {
				view.descriptions.splice(descInArrayIndex, 1);
			}

			// Fill input fields with selected locale
			$('#input--heading').val(selectedTitle);
			$('#input--description').val(selectedDesc);

			// Push currrent locale data into array
			view.titles.push({
				"locale": activeLocale,
				"translation": activeTitle
			});
			view.descriptions.push({
				"locale": activeLocale,
				"translation": activeDesc
			});

			// Tick the selected locale
			$(e.target).closest('.js-locale__item').addClass('is-active').siblings('.js-locale__item').removeClass('is-active');
			if (selectedLocale != _Payload.defaultlocale) {
				$('#locale-icon').removeClass('icon-locale').addClass('icon-locale-alert');
				$('#active-locale').text(selectedLocale);
			} else {
				$('#locale-icon').removeClass('icon-locale-alert').addClass('icon-locale');
				$('#active-locale').empty();
			}

		}
	},
	showTagsAutocomplete: function(e) {
		if ($('#section-visibility').val() === 'AUTHORIZED') {
			$('#section-tags-container').removeClass('u-hiddenvisually');
			$('.text-dropdown').each(function() {
				var top = $(this).css('top');
				if (top === '0px') {
					$(this).css('top', '36px');
				}
			});
		} else {
			$('#section-tags-container').addClass('u-hiddenvisually');
		}

		if ($('#section-visibility').val() === 'AGENTS') {
			$('#section-agent-tags-container').removeClass('u-hiddenvisually');
			$('#section-agent-teams-container').removeClass('u-hiddenvisually');
			$('.text-dropdown').each(function() {
				var top = $(this).css('top');
				if (top === '0px') {
					$(this).css('top', '36px');
				}
			});
			$('#section-agent-tags-container .text-tags').addClass('text-tags-on-top');
			$('#section-agent-teams-container .text-tags').addClass('text-tags-on-top');
		} else {
			$('#section-agent-tags-container').addClass('u-hiddenvisually');
			$('#section-agent-teams-container').addClass('u-hiddenvisually');
		}
	}
});

};


modules['helpcenter/primary/main'] = function(require, exports, module, modules) {
// Load libraries
$ = require('jquery');
_ = require('underscore');
Backbone = require('backbone');

// Load plugins
require('helpcenter/primary/plugins/datepicker');
require('helpcenter/primary/plugins/drilldown');
var moment = require('helpcenter/primary/plugins/moment');

// Load Cookie Consent
require('helpcenter/primary/plugins/cookieconsent');

// Load views and router
require('helpcenter/primary/views/autocomplete');
require('helpcenter/primary/router');

$(function() {
	// Toggle menu
	var $nav = $('.nav__menu'),
			$menuLink = $('.menu__link');
	
	$('a[href$="profile/logout"]').click(function() {
		localStorage.removeItem('ko:ask_register_consent');
	});

	$menuLink.click(function(e) {
		e.stopPropagation();
		$(this).toggleClass('active');
		$nav.toggleClass('open');
	});
	$(document).click(function(e) {
		$menuLink.removeClass('active');
		$nav.removeClass('open');
	});

	// Switch locale
	$('.menu__language').change(function() {
		var currentUrl = window.location.pathname,
		selectedLocale = $(this).val();
		window.location.href = _Payload.path.replace(/\/$/, "") + currentUrl.replace(_Payload.activelocale, selectedLocale);
	});

    // Initialize datepicker (Ref: http://amsul.ca/pickadate.js/)
    $('.datepicker__input').pickadate();

    // Initialize moment.js plugin
    $.fn.timeago = function() {
		moment.locale(_Payload.activelocale);
    	var self = this;
			function updateTime() {
				self.each(function() {
					var $elem = $(this),
							timestamp = $elem.data('time');
					$elem.text(moment(timestamp).fromNow()).prop('title',moment(timestamp).format('MMMM Do YYYY, h:mm A'));
				});
			}
			updateTime();
			setInterval(function() {
			  updateTime();
			}, 60000);
    };
	$(".js-timeago").timeago();
	
	var initRegisterConsent = function(message, policy, dismiss, privacyPolicy) { 
		var policyUrl = "#";
		if(privacyPolicy && privacyPolicy.url)
			policyUrl = privacyPolicy.url
		
		window.cookieconsent.initialise({
			"palette": {
				"popup": {
					"background": "#40364d",
					"text": "#ffffff"
				},
				"button": {
					"background": "#4eafcb",
					"text": "#ffffff"
				}
			},
			"position": "bottom-right",
			"type": "info",
			"elements": {
				"messagelink": '<span id="cookieconsent:desc" class="cc-message">' + message + '<a tabindex="0" class="cc-link" href="{{href}}" target="_blank">' + policy + '</a></span>'
			},
			"content": {
				"dismiss": dismiss,
				"href": policyUrl
			},
			"cookie": {
				"domain": window.location.hostname,
				"expiryDays": 30,
				"name": "registerconsent_status"
			},
			onStatusChange: function(status, chosenBefore) {
				console.log(status, chosenBefore);
				var self = this;
				$.ajax({
					url: _Payload.portalpath + 'api/v1/consent',
					type: 'POST',
					data: JSON.stringify({ consent_type: 'REGISTRATION', channel_name: 'HELPCENTER', source: 'POPUP', url: window.location.href }),
					headers: {'X-CSRF-Token': localStorage.getItem('ko:core:csrf') ? localStorage.getItem('ko:core:csrf').replace(/\"/g, '') : '', 'Content-Type': 'application/json'},
					success: function(response, status, xhr) {
						localStorage.removeItem('ko:ask_register_consent');
					},
					error: function(response) {
						self.clearStatus();
					}
				});
			}
		});
	}
	
	var initCookieConsent = function(message, policy, dismiss, deny, privacyPolicy){
		var policyUrl = "#";
		if(privacyPolicy && privacyPolicy.url)
			policyUrl = privacyPolicy.url
		
		window.cookieconsent.initialise({
			"palette": {
				"popup": {
					"background": "#40364d",
					"text": "#ffffff"
				},
				"button": {
					"background": "#4eafcb",
					"text": "#ffffff"
				}
			},
			"position": "bottom",
			"type": "opt-out",
			"elements": {
				"messagelink": '<span id="cookieconsent:desc" class="cc-message">' + message + '<a tabindex="0" class="cc-link" href="{{href}}" target="_blank">' + policy + '</a></span>'
			},
			"content": {
				"dismiss": dismiss,
				"deny": deny,
				"href": policyUrl
			},
			"cookie": {
				"domain": window.location.hostname,
				"expiryDays": 3650, // expires in ten years
				"name": "cookieconsent_status"
			}
		});
	};

	var shouldAskForCookieConsent = function (){
		return false;
		//return document.cookie.indexOf("cookieconsent_status=") === -1; // uncomment this line to enable the cookie consent banner
	};

	var shouldAskForRegistrationConset = function () {
		return localStorage.getItem('ko:ask_register_consent') === 'true' && document.cookie.indexOf("registerconsent_status=") === -1;
	}

	if (shouldAskForRegistrationConset()) {
		$.ajax({
			url: _Payload.portalpath + 'api/v1/privacy?locale=' + _Payload.activelocale + '&privacy_type=REGISTRATION',
			type: 'GET',
			success: function(response, status, xhr) {
				var policyUrlObject;

				if(response.data.length > 0 )
					policyUrlObject = response.data[0];

				initRegisterConsent(_RegistrationConsent.message, _RegistrationConsent.policy, _RegistrationConsent.dismiss, policyUrlObject);
			}
		});
	}

	if (shouldAskForCookieConsent()) {
		$.ajax({
			url: _Payload.portalpath + 'api/v1/privacy?limit=10000',
			type: 'GET',
			success: function success(response, status, xhr) {
				var policyUrlObject = response.data.find(function (x) {
					return x.locale == _Payload.activelocale && x.privacy_type.toUpperCase() === 'COOKIE';
				}) || response.data.find(function (x) {
					return x.default && x.privacy_type.toUpperCase() === 'COOKIE';
				});

				initCookieConsent(_CookieConsent.message, _CookieConsent.policy, _CookieConsent.dismiss, _CookieConsent.deny, policyUrlObject);
			}
		});
	}
});
};


modules['helpcenter/primary/models/deletefields'] = function(require, exports, module, modules) {
module.exports = Backbone.Model.extend({
    idAttribute: 'id',

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @param  {Object}
     * @param  {Object}
     */
    initialize: function(models, options) {
        if (options) {
            this.fieldId = options.fieldId;
            this.fieldType = options.fieldType;
        }
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @return {Object}
     */
    url: function() {
        if (this.fieldType) {
        	return _Payload.portalpath + 'api/v1/profile/identities/' + this.fieldType + '/' + this.fieldId + '.json?include=*';
        } else {
            return _Payload.portalpath + 'api/v1/profile/avatar.json?include=*';
        }
    }
});
};


modules['helpcenter/primary/views/autocomplete'] = function(require, exports, module, modules) {
/* jshint ignore:start */
'use strict';
/* jshint ignore:end */

require('helpcenter/primary/plugins/autocomplete');
var searchCount = require('helpcenter/primary/services/counts');
var app = app || {};

/**
 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
 * @param  {String} url API URL for search
 * @param  {Boolean} filter Optional filter method that will be applied to the collection before creating the list
 * @param  {String} el Backbone view element
 * @param  {String} template Underscore template for each line item
 * @param  {Number} delay
 * @param  {Number} minLength
 * @return {Object} response.data
 */

var locale = _Payload.is_localized ? '&locale=' + _Payload.activelocale : '';

if ($('#search').length) {
    app.autocompleteRemote = new Backbone.AutocompleteList({
        url: function() {
            return _Payload.portalpath + 'api/v1/helpcenter/search' + locale + '&' + $.param({
                brand_id: _Payload.brand_id,
                query: $('#search').val(),
                limit: 5,
                include: '*'
            });
        },
        filter: null,
        el: $('#search'),
        template: _.template($('#tpl-search-autocomplete').html()),
        delay: 500,
        minLength: 3,
    }).resultsView.collection.parse = function(response) {
        searchCount.set(response.total_count);
        return response.data;
    };
}

app.autocompleteRemote = new Backbone.AutocompleteList({
    url: function() {
        return _Payload.portalpath + 'api/v1/helpcenter/search' + locale + '&' + $.param({
            brand_id: _Payload.brand_id,
            query: $('#search--mobile').val(),
            limit: 5,
            include: '*'
        });
    },
    filter: null,
    el: $('#search--mobile'),
    template: _.template($('#tpl-search-autocomplete--mob').html()),
    delay: 500,
    minLength: 3,
}).resultsView.collection.parse = function(response) {
    searchCount.set(response.total_count);
    return response.data;
};
};


modules['helpcenter/primary/models/manage/content'] = function(require, exports, module, modules) {
module.exports = Backbone.Model.extend({
    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @param  {Object}
     */
    initialize: function(options) {
        this.item = options.item;
        this.itemId = options.itemId;
        this.query = options.query;
        if (this.itemId) {
            this.idAttribute = 'itemId';
        }
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @return {Obejct}
     */
    url: function() {
        switch (this.query) {
            case 'update':
                return _Payload.portalpath + 'api/v1/' + this.item + '/' + this.itemId + '.json?include=*';
            case 'fetch':
                return _Payload.portalpath + 'api/v1/' + this.item + '?include=*';
            case 'move':
                return _Payload.portalpath + 'api/v1/' + this.item + '.json?include=*&ids=' + this.itemId;
        }
    }
});

};


modules['helpcenter/primary/views/comment'] = function(require, exports, module, modules) {
var redirect = require('helpcenter/primary/views/redirect');

module.exports = Backbone.View.extend({
    model: require('helpcenter/primary/models/comment'),

    el: $('#article'),

    events: {
        'focus #new-comment'           : 'expand',
        'submit #add-comment'          : 'add',
        'click .js-comment--del'       : 'prompt',
        'click #cancel-delete-comment' : 'cancelDelete',
        'click #delete-comment'        : 'delete',
        'click .js-comment--edit'      : 'edit',
        'click .js-comment--publish'   : 'publish',
        'click .js-comment--save'      : 'update',
        'click .js-comment--cancel'    : 'cancel',
        'click .js-comment__options'   : 'toggleOptions'
    },

    articleId: null,
    newestId: null,
    newestContent: null,

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Gets options from router while instantiating view
     * @param  {Object}
     * @return {Object}
     */
    initialize: function (options) {
        this.options = options || {};
        this.articleId = this.options.articleId.split('-')[0];

        if ($('#comments-container').hasClass('comments--admin')) {
            $('.js-comment').addClass('js-comment--editable');
        } else {
            $('.js-comment').each(function() {
                var publishDate = new Date($(this).find('.js-timeago').attr('data-time'));
                var currentDate = new Date();
                var diff = (currentDate - publishDate) / 60000; // in minutes
                if (Math.round(diff) < 10) { // editable upto 5 minutes after publish
                    $(this).addClass('js-comment--editable');
                }
            });
            setInterval(function() {
                $('.js-comment').each(function() {
                    var publishDate = new Date($(this).find('.js-timeago').attr('data-time'));
                    var currentDate = new Date();
                    var diff = (currentDate - publishDate) / 60000; // in minutes
                    if (Math.round(diff) > 9) { // editable upto 5 minutes after publish
                        $(this).removeClass('js-comment--editable');
                    }
                });
            }, 60000); // check every minute
        }
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Expands comment textarea on click
     *              and shows submit button
     */
    expand: function() {
        // Initialize HTML editor
        $('#new-comment').froalaEditor({
            key: 'GIBEVFBOHF1c1UNYVM==',
            placeholderText: 'Add a comment',
            charCounterCount: false,
            toolbarSticky: false,
            toolbarButtons: ['bold', 'italic', 'underline', 'strikethrough', 'paragraphFormat', 'emoticons'],
            toolbarButtonsMD: ['bold', 'italic', 'underline', 'strikethrough', 'paragraphFormat', 'emoticons'],
            toolbarButtonsSM: ['bold', 'italic', 'underline', 'strikethrough', 'paragraphFormat', 'emoticons'],
            toolbarButtonsXS: ['bold', 'italic', 'underline', 'strikethrough', 'paragraphFormat', 'emoticons'],
            paragraphFormat: {
                N: 'Normal',
                PRE: 'Code'
              }
        });
        $('#new-comment').on('froalaEditor.focus', function () {
            $('#add-comment').addClass('is-focussed');
        });
        $('#new-comment').on('froalaEditor.blur', function () {
            $('#add-comment').removeClass('is-focussed');
        });
        $('#add-comment').find('.fr-element').focus();

        $('#new-comment').addClass('u-hidden');
        $('#show-btn').removeClass('u-hiddenvisually');
        $('#comments, #show-btn').addClass('moveDown');
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Posts a new comment to the server
     * @param event
     */
    add: function(e) {
        // Prevent the comment from submitting
    	e.preventDefault();

        var view = this;
        var newComment = $('#new-comment').froalaEditor('html.get');
        if (newComment !== '') {
            // Disable button and show animation
            $('#submit-comment').addClass('button--loading is-disabled').attr('disabled', true);

            // Post comment to server
            new this.model({ 'articleId': view.articleId }).save({ 'contents': newComment, 'is_html': true }, {
                success: function(model, response) {
                    // Set value of the newest comment
                    view.newestId = response.data.id;
                    view.newestContent = response.data.contents;
                    view.append(view.newestId, view.newestContent);

                    // Empty comment input
                    $('#new-comment').froalaEditor('html.set', '');

                    // Re-enalble submit button
                    $('#submit-comment').removeClass('button--loading is-disabled').attr('disabled', false);
                },
                error: function(model, xhr, response) {
                    redirect('article', view.options.articleId, xhr.status);
                }
            });
        } else {
            $('#add-comment').addClass('is-error');
            $('#new-comment').on('froalaEditor.keyup', function () {
                $('#add-comment').removeClass('is-error');
            });
        }
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Appends a new comment immediately (faded)
     * @param {String} newComment
     */
    append: function(id, newComment) {
        var commentTpl = _.template($('#tpl-comment').html());
        var date = new Date();
        $('#comments').prepend(commentTpl({ comment: newComment, timestamp: date.toISOString() }))
        .find('.js-comment').first().addClass('js-comment--editable').attr('data-comment-id', id)
        .find(".js-timeago").timeago();
        $('#comments').children('.comments__item').first().slideDown(120);

        // Runs a timer for n minutes to make comment editable
        this.isEditable(id);
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Toggles comment menu
     * @param  {Object} e
     */
    toggleOptions: function(e) {
        e.stopPropagation();
        $(e.target).closest('.js-comment').toggleClass('u-z1');
        $(e.target).closest('.js-moderate-comment').toggleClass('is-active');
        $(document).click(function() {
            $('.js-moderate-comment').removeClass('is-active');
            $(e.target).closest('.js-comment').removeClass('u-z1');
        });
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Shows confirmation box to delete a comment
     * @param {Obeject} e
     */
    prompt: function(e) {
        e.stopPropagation();
        var view = this;
        var commentId = $(e.target).closest('.js-comment').attr('data-comment-id');

        // Show confirmation dialog
        var promptTpl = _.template($('#tpl-delete-comment').html());
        $('#article').append(promptTpl({ commentId: commentId}));
        $('#delete-comment-modal').fadeIn(300);

        // Bind events to hide dialog
        $(document).click(function() {
            view.cancelDelete();
        });
        $(document).keyup(function(e) {
             if (e.keyCode == 27) {
                view.cancelDelete();
            }
        });
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Manually publish comment by admin/agent
     * @param  {Object} e
     */
    publish: function(e) {
        var view = this;
        var comment = $(e.target).closest('.js-comment');
        var commentId = comment.attr('data-comment-id');

        new this.model({ 'articleId': view.articleId, 'commentId': commentId, 'update': true, 'publish': true }).save({}, {
            success: function() {
                comment.find('.js-spam-mark').fadeOut(150, function() {
                    $(this).remove();
                });
                comment.removeClass('comments__item--spam');
                comment.find('.js-comment--publish').remove();
            }
        });
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Removes delete confimation dialog
     * @param  {Object} e Delete button
     */
    cancelDelete: function() {
        $('#delete-comment-modal').fadeOut(200, function() {
            $(this).remove();
        });
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Deletes a comment if it has been published not more than n minutes ago
     * @param event
     */
    delete: function(e) {
        e.stopPropagation();
        var deleteBtn = $('#delete-comment');

        // Show loading animation
        deleteBtn.addClass('is-disabled button--loading').prop('disabled', true);

        // Delete comment from DB
    	var commentId = deleteBtn.attr('data-id');
    	new this.model({ 'articleId': this.articleId, 'commentId': commentId, 'update': true }).destroy({
	        success: function(model, response) {
                // Remove comment
                $('[data-comment-id=' + commentId + ']').remove();

                // Remove delete modal
                $('#delete-comment-modal').fadeOut(200, function() {
                    $(this).remove();
                });
	        },
            error: function(model, xhr) {
                if (xhr.status === 404) {
                    $(e.target).closest('.js-comment').fadeOut(400, function() {
                        $(this).remove();
                    });
                }
            }
    	});
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Makes a comment editable by showing textarea
     * @param event
     */
    edit: function(e) {
        var view = this;
        $(e.target).addClass('is-disabled');
        var comment = $(e.target).closest('.js-comment');
        var commentId = comment.attr('data-comment-id');

        // Set comment content into editing textarea
        if (commentId == view.newestId) {
            view.editMode(comment, view.newestContent);
        } else {
            // Fetch markdown from database
            new this.model({ 'articleId': view.articleId, 'commentId': commentId, 'update': true }).fetch({
                success: function(response) {
                    view.editMode(comment, response.attributes.data.contents);
                },
                error: function() {
                    $(e.target).removeClass('is-disabled');
                }
            });
        }
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Changes the comment view to editing mode
     * @param  {String} content
     */
    editMode: function(comment, content) {
        var commentContent = comment.find('.js-comment__content');
        var commentEditor = comment.find('.js-comment-editor');

        // Hide comment and show editor
        commentContent.addClass('u-hiddenvisually');
        commentEditor.removeClass('u-hidden');

        // Instantiate HTML editor
        commentEditor.froalaEditor({
            key: 'GIBEVFBOHF1c1UNYVM==',
            placeholderText: 'Add a comment',
            charCounterCount: false,
            toolbarButtons: ['bold', 'italic', 'underline', 'strikethrough', 'paragraphFormat', 'emoticons'],
            toolbarButtonsMD: ['bold', 'italic', 'underline', 'strikethrough', 'paragraphFormat', 'emoticons'],
            toolbarButtonsSM: ['bold', 'italic', 'underline', 'strikethrough', 'paragraphFormat', 'emoticons'],
            toolbarButtonsXS: ['bold', 'italic', 'underline', 'strikethrough', 'paragraphFormat', 'emoticons'],
            paragraphFormat: {
                N: 'Normal',
                PRE: 'Code'
              }
        });
        commentEditor.on('froalaEditor.focus', function () {
            commentEditor.addClass('is-focussed');
        });
        commentEditor.on('froalaEditor.blur', function () {
            commentEditor.removeClass('is-focussed');
        });

        // Set comment value in HTML editor
        commentEditor.froalaEditor('html.set', commentContent.html());

        // Show save button
        comment.find('.js-comment--save').removeClass('is-disabled');

        // Remove other options
        comment.addClass('js-editing');
        comment.find('.js-moderate-comment').addClass('u-hiddenvisually');
        comment.find('.js-save-comment').removeClass('u-hidden');
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Updates the content of an existing comment
     * @param event
     */
    update: function(e) {
    	var comment = $(e.target).closest('.js-comment');
    	var commentId = comment.attr('data-comment-id');
    	var newContent = comment.find('.js-comment-editor').froalaEditor('html.get', true);

        if (newContent) {
            // Fade "Save" button
            $(e.target).addClass('is-disabled');

            // Update comment on server
            new this.model({ 'articleId': this.articleId, 'commentId': commentId, 'update': true }).save({ 'contents': newContent }, {
                    success: function() {
                        // Hide editor and show only content
                        comment.find('.js-comment__content').html(newContent).removeClass('u-hiddenvisually');
                        comment.find('.js-save-comment').addClass('u-hidden');
                        comment.find('.js-moderate-comment').removeClass('u-hiddenvisually');
                        comment.removeClass('js-editing');
                        comment.find('.js-comment--edit').removeClass('is-disabled');
                        comment.find('.js-comment-editor').empty().froalaEditor('destroy');
                    }
            });
        } else {
            comment.find('.js-comment-editor').addClass('is-error');
        }
    },

    cancel: function(e) {
        var comment = $(e.target).closest('.js-comment');
        comment.removeClass('js-editing');
        comment.find('.js-comment__content').removeClass('u-hiddenvisually');
        comment.find('.js-comment-editor').empty().froalaEditor('destroy');
        comment.find('.js-save-comment').addClass('u-hidden');
        comment.find('.js-moderate-comment').removeClass('u-hiddenvisually');
        comment.find('.js-comment--edit').removeClass('is-disabled');
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Makes a comment non-editable after n minutes of publishing
     */
    isEditable: function(id) {
        setInterval(function() {
            var comment = $('[data-comment-id=' + id + ']');
            // checks all editable comments
            var publishDate = new Date(comment.find('.js-timeago').attr('data-time'));
            var currentDate = new Date();
            var diff = (currentDate - publishDate) / 60000; // in minutes
            if (Math.round(diff) > 9) { // editable upto 5 minutes after publish
                comment.removeClass('js-comment--editable');
            }
        }, 60000); // check every minute
    }
});

};


modules['helpcenter/primary/plugins/froala/emoticons'] = function(require, exports, module, modules) {
/*!
 * froala_editor v2.3.4 (https://www.froala.com/wysiwyg-editor)
 * License https://froala.com/wysiwyg-editor/terms/
 * Copyright 2014-2016 Froala Labs
 */

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof module === 'object' && module.exports) {
        // Node/CommonJS
        module.exports = function( root, jQuery ) {
            if ( jQuery === undefined ) {
                // require('jQuery') returns a factory that requires window to
                // build a jQuery instance, we normalize how we use modules
                // that require this pattern but the window provided is a noop
                // if it's defined (how jquery works)
                if ( typeof window !== 'undefined' ) {
                    jQuery = require('jquery');
                }
                else {
                    jQuery = require('jquery')(root);
                }
            }
            factory(jQuery);
            return jQuery;
        };
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {

  'use strict';

  $.extend($.FE.POPUP_TEMPLATES, {
    emoticons: '[_BUTTONS_][_EMOTICONS_]'
  })

  // Extend defaults.
  $.extend($.FE.DEFAULTS, {
    emoticonsStep: 8,
    // @modified: emojis
    emoticonsSet: [
      { code: '1f600', desc: 'Grinning face' },
      { code: '1f601', desc: 'Grinning face with smiling eyes' },
      { code: '1f602', desc: 'Face with tears of joy' },
      { code: '1f606', desc: 'Smiling face with open mouth and tightly-closed eyes' },
      { code: '1f609', desc: 'Winking face' },
      { code: '1f60b', desc: 'Face savoring delicious food' },
      { code: '1f60d', desc: 'Smiling face with heart-shaped eyes' },
      { code: '1f60e', desc: 'Smiling face with sunglasses' },

      { code: '1f60f', desc: 'Smirking face' },
      { code: '1f610', desc: 'Neutral face' },
      { code: '1f612', desc: 'Unamused face' },
      { code: '1f614', desc: 'Pensive face' },
      { code: '1f615', desc: 'Confused face' },
      { code: '1f616', desc: 'Confounded face' },
      { code: '1f618', desc: 'Face throwing a kiss' },
      { code: '1f61b', desc: 'Face with stuck out tongue' },

      { code: '1f61c', desc: 'Face with stuck out tongue and winking eye' },
      { code: '1f620', desc: 'Angry face' },
      { code: '1f621', desc: 'Pouting face' },
      { code: '1f622', desc: 'Crying face' },
      { code: '1f62d', desc: 'Loudly crying face' },
      { code: '1f625', desc: 'Disappointed but relieved face' },
      { code: '1f627', desc: 'Anguished face' },
      { code: '1f62e', desc: 'Face with open mouth' },

      { code: '1f631', desc: 'Face screaming in fear' },
      { code: '1f634', desc: 'Sleeping face' },
      { code: '2764', desc: 'Heart' },
      { code: '1f494', desc: 'Broken heart' },
      { code: '274e', desc: 'Cross mark' },
      { code: '2705', desc: 'Check mark' },
      { code: '1f4af', desc: '100 points' },
      { code: '1f64f', desc: 'Folded hands' },

      { code: '1f64c', desc: 'Raised hands' },
      { code: '1f44f', desc: 'Clapping hands' },
      { code: '1f44d', desc: 'Thumbs up' },
      { code: '1f44e', desc: 'Thumbs down' },
      { code: '270c', desc: 'Victory hand' },
      { code: '1f44c', desc: 'OK hand' },
      { code: '270b', desc: 'Raised hand' },
      { code: '270d', desc: 'Writing hand' }
    ],
    emoticonsButtons: ['emoticonsBack', '|'],
    emoticonsUseImage: true
  });

  $.FE.PLUGINS.emoticons = function (editor) {
    /*
     * Show the emoticons popup.
     */
    function _showEmoticonsPopup () {
      var $btn = editor.$tb.find('.fr-command[data-cmd="emoticons"]');

      var $popup = editor.popups.get('emoticons');
      if (!$popup) $popup = _initEmoticonsPopup();

      if (!$popup.hasClass('fr-active')) {
        // Colors popup
        editor.popups.refresh('emoticons');
        editor.popups.setContainer('emoticons', editor.$tb);

        // Colors popup left and top position.
        var left = $btn.offset().left + $btn.outerWidth() / 2;
        var top = $btn.offset().top + (editor.opts.toolbarBottom ? 10 : $btn.outerHeight() - 10);

        editor.popups.show('emoticons', left, top, $btn.outerHeight());
      }
    }

    /*
     * Hide emoticons popup.
     */
    function _hideEmoticonsPopup () {
      // Hide popup.
      editor.popups.hide('emoticons');
    }

    /**
     * Init the emoticons popup.
     */
    function _initEmoticonsPopup () {
      var emoticons_buttons = '';

      if (editor.opts.toolbarInline) {
        // Colors buttons.
        if (editor.opts.emoticonsButtons.length > 0) {
          emoticons_buttons = '<div class="fr-buttons fr-emoticons-buttons">' + editor.button.buildList(editor.opts.emoticonsButtons) + '</div>';
        }
      }

      var template = {
        buttons: emoticons_buttons,
        emoticons: _emoticonsHTML()
      };

      // Create popup.
      var $popup = editor.popups.create('emoticons', template);

      // Assing tooltips to buttons.
      editor.tooltip.bind($popup, '.fr-emoticon');

      return $popup;
    }

    /*
     * HTML for the emoticons popup.
     */
    function _emoticonsHTML () {
      // Create emoticons html.
      var emoticons_html = '<div style="text-align: center">';

      // Add emoticons.
      for (var i = 0; i < editor.opts.emoticonsSet.length; i++) {
        if (i !== 0 && i % editor.opts.emoticonsStep === 0) {
          emoticons_html += '<br>';
        }

        emoticons_html += '<span class="fr-command fr-emoticon" data-cmd="insertEmoticon" title="' + editor.language.translate(editor.opts.emoticonsSet[i].desc) + '" data-param1="' + editor.opts.emoticonsSet[i].code + '">' + (editor.opts.emoticonsUseImage ? '<img src="' + 'https://cdnjs.cloudflare.com/ajax/libs/emojione/2.0.1/assets/svg/' + editor.opts.emoticonsSet[i].code + '.svg' + '"/>' : '&#x' + editor.opts.emoticonsSet[i].code + ';') + '</span>';
      }

      emoticons_html += '</div>';

      return emoticons_html;
    }

    /*
     * Insert emoticon.
     */
    // @modified
    function insert (code, emoticon, img) {
      // Insert emoticon.
      editor.html.insert('<span class="fr-emoticon fr-deletable fr-emoticon-img emoji-' + code + '">&nbsp;</span><span>&nbsp;</span>' + $.FE.MARKERS, true);
    }

    /*
     * Go back to the inline editor.
     */
    function back () {
      editor.popups.hide('emoticons');
      editor.toolbar.showInline();
    }

    /*
     * Init emoticons.
     */
    function _init () {
      var setDeletable = function () {
        var emtcs = editor.$el.get(0).querySelectorAll('.fr-emoticon:not(.fr-deletable)');
        for (var i = 0; i < emtcs.length; i++) {
          emtcs[i].className += ' fr-deletable';
        }
      }
      setDeletable();

      editor.events.on('html.set', setDeletable);

      // Replace emoticons with unicode.
      editor.events.on('html.get', function (html) {
        for (var i = 0; i < editor.opts.emoticonsSet.length; i++) {
          var em = editor.opts.emoticonsSet[i];
          var text = $('<div>').html(em.code).text();
          html = html.split(text).join(em.code);
        }

        return html;
      });

      var inEmoticon = function () {
        if (!editor.selection.isCollapsed()) return false;

        var s_el = editor.selection.element();
        var e_el = editor.selection.endElement();

        if ($(s_el).hasClass('fr-emoticon')) return s_el;
        if ($(e_el).hasClass('fr-emoticon')) return e_el;

        var range = editor.selection.ranges(0);
        var container = range.startContainer;
        if (container.nodeType == Node.ELEMENT_NODE) {
          if (container.childNodes.length > 0 && range.startOffset > 0) {
            var node = container.childNodes[range.startOffset - 1];
            if ($(node).hasClass('fr-emoticon')) {
              return node;
            }
          }
        }

        return false;
      }

      editor.events.on('keydown', function (e) {
        if (editor.keys.isCharacter(e.which) && editor.selection.inEditor()) {
          var range = editor.selection.ranges(0);
          var el = inEmoticon();
          if (el) {
            if (range.startOffset === 0 && editor.selection.element() === el) {
              $(el).before($.FE.MARKERS + $.FE.INVISIBLE_SPACE);
            }
            else {
              $(el).after($.FE.INVISIBLE_SPACE + $.FE.MARKERS);
            }
            editor.selection.restore();
          }
        }
      });

      editor.events.on('keyup', function (e) {
        var emtcs = editor.$el.get(0).querySelectorAll('.fr-emoticon');

        for (var i = 0; i < emtcs.length; i++) {
          if (typeof emtcs[i].textContent != 'undefined' && emtcs[i].textContent.replace(/\u200B/gi, '').length === 0) {
            $(emtcs[i]).remove();
          }
        }

        if (!(e.which >= 37 && e.which <= 40)) {
          var el = inEmoticon();
          if (el && (el.className || '').indexOf('fr-emoticon-img')) {
            $(el).append($.FE.MARKERS);
            editor.selection.restore();
          }
        }
      });
    }

    return {
      _init: _init,
      insert: insert,
      showEmoticonsPopup: _showEmoticonsPopup,
      hideEmoticonsPopup: _hideEmoticonsPopup,
      back: back
    }
  }

  // Toolbar emoticons button.
  $.FE.DefineIcon('emoticons', { NAME: 'smile-o' });
  $.FE.RegisterCommand('emoticons', {
    title: 'Emoticons',
    undo: false,
    focus: true,
    refreshOnCallback: false,
    popup: true,
    callback: function () {
      if (!this.popups.isVisible('emoticons')) {
        this.emoticons.showEmoticonsPopup();
      }
      else {
        if (this.$el.find('.fr-marker')) {
          this.events.disableBlur();
          this.selection.restore();
        }
        this.popups.hide('emoticons');
      }
    },
    plugin: 'emoticons'
  });

  // Insert emoticon command.
  $.FE.RegisterCommand('insertEmoticon', {
    callback: function (cmd, code) {
      // Insert emoticon.
      // @modified: Added code param
      this.emoticons.insert(code, '&#x' + code + ';', this.opts.emoticonsUseImage ? 'https://cdnjs.cloudflare.com/ajax/libs/emojione/2.0.1/assets/svg/' + code + '.svg' : null);

      // Hide emoticons popup.
      this.emoticons.hideEmoticonsPopup();
    }
  });

  // Emoticons back.
  $.FE.DefineIcon('emoticonsBack', { NAME: 'arrow-left' });
  $.FE.RegisterCommand('emoticonsBack', {
    title: 'Back',
    undo: false,
    focus: false,
    back: true,
    refreshAfterCallback: false,
    callback: function () {
      this.emoticons.back();
    }
  });

}));

};


modules['helpcenter/primary/views/drilldown'] = function(require, exports, module, modules) {
module.exports = Backbone.View.extend({
	model: require('helpcenter/primary/models/drilldown'),

	events: {
		'click .js-drill-selected, .drill .icon': 'toggle',
		'click .js-dd-set': 'set',
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Initialize drilldown plugin and intantiate fetch()
	 */
	initialize: function(options) {
		this.options = options || {};
		$('.drilldown').drilldown();
		$(document).click(function() {
		    $('.drilldown').removeClass('dd--open');
		    $('.drill-default').removeClass('drill-focus');
		});
		this.fetch();
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Fetches drilldown data from server
	 */
	fetch: function() {
		var self = this;
		var counter = $('.drilldown-id').length;
		var field = $('.drilldown-id');
		if (counter) {
			var drillDownIds = [];
			var drillDownRefs = [];
			$('.drilldown-id').each(function() {
				drillDownIds.push($(this).val());
				drillDownRefs.push($(this));
			});
			new self.model({ 'id': field.val() }, { 'fieldId': drillDownIds.join(','), 'endpoint': self.options.endpoint, 'count': counter }).fetch({
				success: function(response) {
					if(counter === 1) {
						self.render(drillDownRefs[0], response.attributes.data.options, response.attributes.data.title);
					} else {
						_.each(response.attributes.data, function (item, index) {
							self.render(drillDownRefs[index], item.options, item.title);
						});
					}
					// Pre-select a value if it is saved
					self.preSelect();
				}
			});
		}
	},

	/**
	 * @author Harminder Virk <harminder.virk@kayako.com>
	 * @description Renders drilldown menu options recieved from fetch()
	 * @param {Object} data
	 */
	render: function(elem, data){
		var target = elem.parent('.drill-wrap').find('.drill-data');

		function getTranslations(hash) {
		    return _.flatten(_.reduce(hash, function (result, item) {
				var values = _.filter(item.values, function(item){ return item.locale == _Payload.activelocale; });
				var translation = values.map(function (child) {
					return { id: item.id, translation: child.translation };
				});

				var translation_string = translation[0].translation;
				if (translation_string) {
					result.push(translation);
					return result;
				}

				// No translation for active locale, lookup for brand default locale
				var values = _.filter(item.values, function(item){ return item.locale == _Payload.defaultlocale; });
				var translation = values.map(function (child) {
					return { id: item.id, translation: child.translation };
				});

				var translation_string = translation[0].translation;
				if (translation_string) {
					result.push(translation);
					return result;
				}

				// Still no translation for active & brand default locale, lookup for instance default locale
				var values = _.filter(item.values, function(item){ return item.locale == _Payload.instancelocale; });
				var translation = values.map(function (child) {
					return { id: item.id, translation: child.translation };
				});

				result.push(translation);
				return result;

		    }, []));
		}

		function getDefaultTranslations(hash) {
				return _.flatten(_.reduce(hash, function (result, item) {
						 var values = _.filter(item.values, function(item){ return item.locale == _Payload.defaultlocale; });
						 var translation = values.map(function (child) {
								return { id: item.id, translation: child.translation };
						});
						result.push(translation);

						return result;
				}, []));
		}

		function mapToCategories(translations) {
		    var result = {};
		    _.each(translations, function (translation) {
					if (translation.translation) {
		        parseChild(translation.translation.split('\\'), translation.id, result);
					} else {
						var defaultTranslation = _.findWhere(getDefaultTranslations(data), {id: translation.id});
						parseChild(defaultTranslation.translation.split('\\'), translation.id, result);
					}
		    });
		    return result;
		}

		function parseChild(childs, id, result) {
		    if (!childs.length) {
		        return;
		    }
		    var child = childs[0];
		    result[child] = result[child] || { id: id, data: child, childs: {} };
		    parseChild(_.rest(childs), id, result[child].childs);
		}

		var menuTemplate = _.template($('#tpl-drillmenu').html());

		function makeMenu(categories, isSubMenu) {
		    var html = menuTemplate({ isSubMenu: isSubMenu });
		    _.each(categories, function (category) {
		        if (_.size(category.childs)) {
		        	html += '<li><span class="dd__link js-dd__link js-dd-set"><i class="icon icon-chevron-right"></i>' + category.data + '</span>';
		        	html += makeMenu(category.childs, true);
		        } else {
		        	html += '<li><span class="dd__link js-dd__link js-dd-set" id="' + category.id + '">' + category.data + '</span>';
		        }
		        html += '</li>';
		    });
		    html += '</ul>';
		    return html;
		}

		target.html(makeMenu(mapToCategories(getTranslations(data)), false));

	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Pre-selects drill down value
	 */
	preSelect: function() {
		$('.js-drill-input').each(function(index, el) {
			if ($(this).data('selected-option')) {
				var selectedId = $(this).data('selected-option');
				var selectedValue = $(this).parents('.drill-wrap').find('#'+selectedId).text();
				$(this).parents('.drill-wrap').find('.js-drill-selected').text(selectedValue);
			}
		});
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Show/hide drilldown menu options
	 * @param {Object} e
	 */
	toggle: function(e) {
    e.stopPropagation();
    var $elem = $(e.target).parent('.drill-default');
    if ($elem.hasClass('drill-focus')) {
      $elem.removeClass('drill-focus').next('.drilldown').removeClass('dd--open');
      return;
    }
    setTimeout(function () {
      $elem.addClass('drill-focus').next('.drilldown').addClass('dd--open');
    });
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Sets the selected drilldown option to input
	 * @param {Object} e
	 */
	set: function(e) {
		var elem = $(e.target);
		var root = elem.parents('.drill-wrap');
		root.find('.js-drill-selected').text(elem.text());
		root.find('.js-drill-input').val(elem.attr('id'));
		root.find('.drilldown').removeClass('dd--open');
		root.find('.drill-default').removeClass('drill-focus');
	}
});
};


modules['helpcenter/primary/models/vote'] = function(require, exports, module, modules) {
module.exports = Backbone.Model.extend({
	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @param  {Object} options
	 */
    initialize: function (options) {
    	this.articleId = options.articleId;
        this.vote = options.vote;
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @return {Object}
     */
    url: function() {
    	return _Payload.portalpath + 'api/v1/articles/' + this.articleId + '/votes/' + this.vote + '?include=*';
    }
});
};


modules['main'] = function(require, exports, module, modules) {
// TODO this seems rather superfluous
};


modules['helpcenter/primary/plugins/sortable'] = function(require, exports, module, modules) {
/*! Sortable 1.6.0 - MIT | git://github.com/rubaxa/Sortable.git */
!function(a){"use strict";"function"==typeof define&&define.amd?define(a):"undefined"!=typeof module&&"undefined"!=typeof module.exports?module.exports=a():window.Sortable=a()}(function(){"use strict";function a(a,b){if(!a||!a.nodeType||1!==a.nodeType)throw"Sortable: `el` must be HTMLElement, and not "+{}.toString.call(a);this.el=a,this.options=b=t({},b),a[T]=this;var c={group:Math.random(),sort:!0,disabled:!1,store:null,handle:null,scroll:!0,scrollSensitivity:30,scrollSpeed:10,draggable:/[uo]l/i.test(a.nodeName)?"li":">*",ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",ignore:"a, img",filter:null,preventOnFilter:!0,animation:0,setData:function(a,b){a.setData("Text",b.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0,forceFallback:!1,fallbackClass:"sortable-fallback",fallbackOnBody:!1,fallbackTolerance:0,fallbackOffset:{x:0,y:0}};for(var d in c)!(d in b)&&(b[d]=c[d]);ga(b);for(var e in this)"_"===e.charAt(0)&&"function"==typeof this[e]&&(this[e]=this[e].bind(this));this.nativeDraggable=!b.forceFallback&&$,f(a,"mousedown",this._onTapStart),f(a,"touchstart",this._onTapStart),f(a,"pointerdown",this._onTapStart),this.nativeDraggable&&(f(a,"dragover",this),f(a,"dragenter",this)),ea.push(this._onDragOver),b.store&&this.sort(b.store.get(this))}function b(a,b){"clone"!==a.lastPullMode&&(b=!0),z&&z.state!==b&&(i(z,"display",b?"none":""),b||z.state&&(a.options.group.revertClone?(A.insertBefore(z,B),a._animate(w,z)):A.insertBefore(z,w)),z.state=b)}function c(a,b,c){if(a){c=c||V;do if(">*"===b&&a.parentNode===c||r(a,b))return a;while(a=d(a))}return null}function d(a){var b=a.host;return b&&b.nodeType?b:a.parentNode}function e(a){a.dataTransfer&&(a.dataTransfer.dropEffect="move"),a.preventDefault()}function f(a,b,c){a.addEventListener(b,c,Z)}function g(a,b,c){a.removeEventListener(b,c,Z)}function h(a,b,c){if(a)if(a.classList)a.classList[c?"add":"remove"](b);else{var d=(" "+a.className+" ").replace(R," ").replace(" "+b+" "," ");a.className=(d+(c?" "+b:"")).replace(R," ")}}function i(a,b,c){var d=a&&a.style;if(d){if(void 0===c)return V.defaultView&&V.defaultView.getComputedStyle?c=V.defaultView.getComputedStyle(a,""):a.currentStyle&&(c=a.currentStyle),void 0===b?c:c[b];b in d||(b="-webkit-"+b),d[b]=c+("string"==typeof c?"":"px")}}function j(a,b,c){if(a){var d=a.getElementsByTagName(b),e=0,f=d.length;if(c)for(;e<f;e++)c(d[e],e);return d}return[]}function k(a,b,c,d,e,f,g){a=a||b[T];var h=V.createEvent("Event"),i=a.options,j="on"+c.charAt(0).toUpperCase()+c.substr(1);h.initEvent(c,!0,!0),h.to=b,h.from=e||b,h.item=d||b,h.clone=z,h.oldIndex=f,h.newIndex=g,b.dispatchEvent(h),i[j]&&i[j].call(a,h)}function l(a,b,c,d,e,f,g,h){var i,j,k=a[T],l=k.options.onMove;return i=V.createEvent("Event"),i.initEvent("move",!0,!0),i.to=b,i.from=a,i.dragged=c,i.draggedRect=d,i.related=e||b,i.relatedRect=f||b.getBoundingClientRect(),i.willInsertAfter=h,a.dispatchEvent(i),l&&(j=l.call(k,i,g)),j}function m(a){a.draggable=!1}function n(){aa=!1}function o(a,b){var c=a.lastElementChild,d=c.getBoundingClientRect();return b.clientY-(d.top+d.height)>5||b.clientX-(d.left+d.width)>5}function p(a){for(var b=a.tagName+a.className+a.src+a.href+a.textContent,c=b.length,d=0;c--;)d+=b.charCodeAt(c);return d.toString(36)}function q(a,b){var c=0;if(!a||!a.parentNode)return-1;for(;a&&(a=a.previousElementSibling);)"TEMPLATE"===a.nodeName.toUpperCase()||">*"!==b&&!r(a,b)||c++;return c}function r(a,b){if(a){b=b.split(".");var c=b.shift().toUpperCase(),d=new RegExp("\\s("+b.join("|")+")(?=\\s)","g");return!(""!==c&&a.nodeName.toUpperCase()!=c||b.length&&((" "+a.className+" ").match(d)||[]).length!=b.length)}return!1}function s(a,b){var c,d;return function(){void 0===c&&(c=arguments,d=this,setTimeout(function(){1===c.length?a.call(d,c[0]):a.apply(d,c),c=void 0},b))}}function t(a,b){if(a&&b)for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a}function u(a){return X?X(a).clone(!0)[0]:Y&&Y.dom?Y.dom(a).cloneNode(!0):a.cloneNode(!0)}function v(a){for(var b=a.getElementsByTagName("input"),c=b.length;c--;){var d=b[c];d.checked&&da.push(d)}}if("undefined"==typeof window||!window.document)return function(){throw new Error("Sortable.js requires a window with a document")};var w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q={},R=/\s+/g,S=/left|right|inline/,T="Sortable"+(new Date).getTime(),U=window,V=U.document,W=U.parseInt,X=U.jQuery||U.Zepto,Y=U.Polymer,Z=!1,$=!!("draggable"in V.createElement("div")),_=function(a){return!navigator.userAgent.match(/Trident.*rv[ :]?11\./)&&(a=V.createElement("x"),a.style.cssText="pointer-events:auto","auto"===a.style.pointerEvents)}(),aa=!1,ba=Math.abs,ca=Math.min,da=[],ea=[],fa=s(function(a,b,c){if(c&&b.scroll){var d,e,f,g,h,i,j=c[T],k=b.scrollSensitivity,l=b.scrollSpeed,m=a.clientX,n=a.clientY,o=window.innerWidth,p=window.innerHeight;if(E!==c&&(D=b.scroll,E=c,F=b.scrollFn,D===!0)){D=c;do if(D.offsetWidth<D.scrollWidth||D.offsetHeight<D.scrollHeight)break;while(D=D.parentNode)}D&&(d=D,e=D.getBoundingClientRect(),f=(ba(e.right-m)<=k)-(ba(e.left-m)<=k),g=(ba(e.bottom-n)<=k)-(ba(e.top-n)<=k)),f||g||(f=(o-m<=k)-(m<=k),g=(p-n<=k)-(n<=k),(f||g)&&(d=U)),Q.vx===f&&Q.vy===g&&Q.el===d||(Q.el=d,Q.vx=f,Q.vy=g,clearInterval(Q.pid),d&&(Q.pid=setInterval(function(){return i=g?g*l:0,h=f?f*l:0,"function"==typeof F?F.call(j,h,i,a):void(d===U?U.scrollTo(U.pageXOffset+h,U.pageYOffset+i):(d.scrollTop+=i,d.scrollLeft+=h))},24)))}},30),ga=function(a){function b(a,b){return void 0!==a&&a!==!0||(a=c.name),"function"==typeof a?a:function(c,d){var e=d.options.group.name;return b?a:a&&(a.join?a.indexOf(e)>-1:e==a)}}var c={},d=a.group;d&&"object"==typeof d||(d={name:d}),c.name=d.name,c.checkPull=b(d.pull,!0),c.checkPut=b(d.put),c.revertClone=d.revertClone,a.group=c};a.prototype={constructor:a,_onTapStart:function(a){var b,d=this,e=this.el,f=this.options,g=f.preventOnFilter,h=a.type,i=a.touches&&a.touches[0],j=(i||a).target,l=a.target.shadowRoot&&a.path[0]||j,m=f.filter;if(v(e),!w&&!("mousedown"===h&&0!==a.button||f.disabled)&&(j=c(j,f.draggable,e),j&&C!==j)){if(b=q(j,f.draggable),"function"==typeof m){if(m.call(this,a,j,this))return k(d,l,"filter",j,e,b),void(g&&a.preventDefault())}else if(m&&(m=m.split(",").some(function(a){if(a=c(l,a.trim(),e))return k(d,a,"filter",j,e,b),!0})))return void(g&&a.preventDefault());f.handle&&!c(l,f.handle,e)||this._prepareDragStart(a,i,j,b)}},_prepareDragStart:function(a,b,c,d){var e,g=this,i=g.el,l=g.options,n=i.ownerDocument;c&&!w&&c.parentNode===i&&(N=a,A=i,w=c,x=w.parentNode,B=w.nextSibling,C=c,L=l.group,J=d,this._lastX=(b||a).clientX,this._lastY=(b||a).clientY,w.style["will-change"]="transform",e=function(){g._disableDelayedDrag(),w.draggable=g.nativeDraggable,h(w,l.chosenClass,!0),g._triggerDragStart(a,b),k(g,A,"choose",w,A,J)},l.ignore.split(",").forEach(function(a){j(w,a.trim(),m)}),f(n,"mouseup",g._onDrop),f(n,"touchend",g._onDrop),f(n,"touchcancel",g._onDrop),f(n,"pointercancel",g._onDrop),f(n,"selectstart",g),l.delay?(f(n,"mouseup",g._disableDelayedDrag),f(n,"touchend",g._disableDelayedDrag),f(n,"touchcancel",g._disableDelayedDrag),f(n,"mousemove",g._disableDelayedDrag),f(n,"touchmove",g._disableDelayedDrag),f(n,"pointermove",g._disableDelayedDrag),g._dragStartTimer=setTimeout(e,l.delay)):e())},_disableDelayedDrag:function(){var a=this.el.ownerDocument;clearTimeout(this._dragStartTimer),g(a,"mouseup",this._disableDelayedDrag),g(a,"touchend",this._disableDelayedDrag),g(a,"touchcancel",this._disableDelayedDrag),g(a,"mousemove",this._disableDelayedDrag),g(a,"touchmove",this._disableDelayedDrag),g(a,"pointermove",this._disableDelayedDrag)},_triggerDragStart:function(a,b){b=b||("touch"==a.pointerType?a:null),b?(N={target:w,clientX:b.clientX,clientY:b.clientY},this._onDragStart(N,"touch")):this.nativeDraggable?(f(w,"dragend",this),f(A,"dragstart",this._onDragStart)):this._onDragStart(N,!0);try{V.selection?setTimeout(function(){V.selection.empty()}):window.getSelection().removeAllRanges()}catch(a){}},_dragStarted:function(){if(A&&w){var b=this.options;h(w,b.ghostClass,!0),h(w,b.dragClass,!1),a.active=this,k(this,A,"start",w,A,J)}else this._nulling()},_emulateDragOver:function(){if(O){if(this._lastX===O.clientX&&this._lastY===O.clientY)return;this._lastX=O.clientX,this._lastY=O.clientY,_||i(y,"display","none");var a=V.elementFromPoint(O.clientX,O.clientY),b=a,c=ea.length;if(b)do{if(b[T]){for(;c--;)ea[c]({clientX:O.clientX,clientY:O.clientY,target:a,rootEl:b});break}a=b}while(b=b.parentNode);_||i(y,"display","")}},_onTouchMove:function(b){if(N){var c=this.options,d=c.fallbackTolerance,e=c.fallbackOffset,f=b.touches?b.touches[0]:b,g=f.clientX-N.clientX+e.x,h=f.clientY-N.clientY+e.y,j=b.touches?"translate3d("+g+"px,"+h+"px,0)":"translate("+g+"px,"+h+"px)";if(!a.active){if(d&&ca(ba(f.clientX-this._lastX),ba(f.clientY-this._lastY))<d)return;this._dragStarted()}this._appendGhost(),P=!0,O=f,i(y,"webkitTransform",j),i(y,"mozTransform",j),i(y,"msTransform",j),i(y,"transform",j),b.preventDefault()}},_appendGhost:function(){if(!y){var a,b=w.getBoundingClientRect(),c=i(w),d=this.options;y=w.cloneNode(!0),h(y,d.ghostClass,!1),h(y,d.fallbackClass,!0),h(y,d.dragClass,!0),i(y,"top",b.top-W(c.marginTop,10)),i(y,"left",b.left-W(c.marginLeft,10)),i(y,"width",b.width),i(y,"height",b.height),i(y,"opacity","0.8"),i(y,"position","fixed"),i(y,"zIndex","100000"),i(y,"pointerEvents","none"),d.fallbackOnBody&&V.body.appendChild(y)||A.appendChild(y),a=y.getBoundingClientRect(),i(y,"width",2*b.width-a.width),i(y,"height",2*b.height-a.height)}},_onDragStart:function(a,b){var c=a.dataTransfer,d=this.options;this._offUpEvents(),L.checkPull(this,this,w,a)&&(z=u(w),z.draggable=!1,z.style["will-change"]="",i(z,"display","none"),h(z,this.options.chosenClass,!1),A.insertBefore(z,w),k(this,A,"clone",w)),h(w,d.dragClass,!0),b?("touch"===b?(f(V,"touchmove",this._onTouchMove),f(V,"touchend",this._onDrop),f(V,"touchcancel",this._onDrop),f(V,"pointermove",this._onTouchMove),f(V,"pointerup",this._onDrop)):(f(V,"mousemove",this._onTouchMove),f(V,"mouseup",this._onDrop)),this._loopId=setInterval(this._emulateDragOver,50)):(c&&(c.effectAllowed="move",d.setData&&d.setData.call(this,c,w)),f(V,"drop",this),setTimeout(this._dragStarted,0))},_onDragOver:function(d){var e,f,g,h,j=this.el,k=this.options,m=k.group,p=a.active,q=L===m,r=!1,s=k.sort;if(void 0!==d.preventDefault&&(d.preventDefault(),!k.dragoverBubble&&d.stopPropagation()),!w.animated&&(P=!0,p&&!k.disabled&&(q?s||(h=!A.contains(w)):M===this||(p.lastPullMode=L.checkPull(this,p,w,d))&&m.checkPut(this,p,w,d))&&(void 0===d.rootEl||d.rootEl===this.el))){if(fa(d,k,this.el),aa)return;if(e=c(d.target,k.draggable,j),f=w.getBoundingClientRect(),M!==this&&(M=this,r=!0),h)return b(p,!0),x=A,void(z||B?A.insertBefore(w,z||B):s||A.appendChild(w));if(0===j.children.length||j.children[0]===y||j===d.target&&o(j,d)){if(0!==j.children.length&&j.children[0]!==y&&j===d.target&&(e=j.lastElementChild),e){if(e.animated)return;g=e.getBoundingClientRect()}b(p,q),l(A,j,w,f,e,g,d)!==!1&&(w.contains(j)||(j.appendChild(w),x=j),this._animate(f,w),e&&this._animate(g,e))}else if(e&&!e.animated&&e!==w&&void 0!==e.parentNode[T]){G!==e&&(G=e,H=i(e),I=i(e.parentNode)),g=e.getBoundingClientRect();var t=g.right-g.left,u=g.bottom-g.top,v=S.test(H.cssFloat+H.display)||"flex"==I.display&&0===I["flex-direction"].indexOf("row"),C=e.offsetWidth>w.offsetWidth,D=e.offsetHeight>w.offsetHeight,E=(v?(d.clientX-g.left)/t:(d.clientY-g.top)/u)>.5,F=e.nextElementSibling,J=!1;if(v){var K=w.offsetTop,N=e.offsetTop;J=K===N?e.previousElementSibling===w&&!C||E&&C:e.previousElementSibling===w||w.previousElementSibling===e?(d.clientY-g.top)/u>.5:N>K}else r||(J=F!==w&&!D||E&&D);var O=l(A,j,w,f,e,g,d,J);O!==!1&&(1!==O&&O!==-1||(J=1===O),aa=!0,setTimeout(n,30),b(p,q),w.contains(j)||(J&&!F?j.appendChild(w):e.parentNode.insertBefore(w,J?F:e)),x=w.parentNode,this._animate(f,w),this._animate(g,e))}}},_animate:function(a,b){var c=this.options.animation;if(c){var d=b.getBoundingClientRect();1===a.nodeType&&(a=a.getBoundingClientRect()),i(b,"transition","none"),i(b,"transform","translate3d("+(a.left-d.left)+"px,"+(a.top-d.top)+"px,0)"),b.offsetWidth,i(b,"transition","all "+c+"ms"),i(b,"transform","translate3d(0,0,0)"),clearTimeout(b.animated),b.animated=setTimeout(function(){i(b,"transition",""),i(b,"transform",""),b.animated=!1},c)}},_offUpEvents:function(){var a=this.el.ownerDocument;g(V,"touchmove",this._onTouchMove),g(V,"pointermove",this._onTouchMove),g(a,"mouseup",this._onDrop),g(a,"touchend",this._onDrop),g(a,"pointerup",this._onDrop),g(a,"touchcancel",this._onDrop),g(a,"pointercancel",this._onDrop),g(a,"selectstart",this)},_onDrop:function(b){var c=this.el,d=this.options;clearInterval(this._loopId),clearInterval(Q.pid),clearTimeout(this._dragStartTimer),g(V,"mousemove",this._onTouchMove),this.nativeDraggable&&(g(V,"drop",this),g(c,"dragstart",this._onDragStart)),this._offUpEvents(),b&&(P&&(b.preventDefault(),!d.dropBubble&&b.stopPropagation()),y&&y.parentNode&&y.parentNode.removeChild(y),A!==x&&"clone"===a.active.lastPullMode||z&&z.parentNode&&z.parentNode.removeChild(z),w&&(this.nativeDraggable&&g(w,"dragend",this),m(w),w.style["will-change"]="",h(w,this.options.ghostClass,!1),h(w,this.options.chosenClass,!1),k(this,A,"unchoose",w,A,J),A!==x?(K=q(w,d.draggable),K>=0&&(k(null,x,"add",w,A,J,K),k(this,A,"remove",w,A,J,K),k(null,x,"sort",w,A,J,K),k(this,A,"sort",w,A,J,K))):w.nextSibling!==B&&(K=q(w,d.draggable),K>=0&&(k(this,A,"update",w,A,J,K),k(this,A,"sort",w,A,J,K))),a.active&&(null!=K&&K!==-1||(K=J),k(this,A,"end",w,A,J,K),this.save()))),this._nulling()},_nulling:function(){A=w=x=y=B=z=C=D=E=N=O=P=K=G=H=M=L=a.active=null,da.forEach(function(a){a.checked=!0}),da.length=0},handleEvent:function(a){switch(a.type){case"drop":case"dragend":this._onDrop(a);break;case"dragover":case"dragenter":w&&(this._onDragOver(a),e(a));break;case"selectstart":a.preventDefault()}},toArray:function(){for(var a,b=[],d=this.el.children,e=0,f=d.length,g=this.options;e<f;e++)a=d[e],c(a,g.draggable,this.el)&&b.push(a.getAttribute(g.dataIdAttr)||p(a));return b},sort:function(a){var b={},d=this.el;this.toArray().forEach(function(a,e){var f=d.children[e];c(f,this.options.draggable,d)&&(b[a]=f)},this),a.forEach(function(a){b[a]&&(d.removeChild(b[a]),d.appendChild(b[a]))})},save:function(){var a=this.options.store;a&&a.set(this)},closest:function(a,b){return c(a,b||this.options.draggable,this.el)},option:function(a,b){var c=this.options;return void 0===b?c[a]:(c[a]=b,void("group"===a&&ga(c)))},destroy:function(){var a=this.el;a[T]=null,g(a,"mousedown",this._onTapStart),g(a,"touchstart",this._onTapStart),g(a,"pointerdown",this._onTapStart),this.nativeDraggable&&(g(a,"dragover",this),g(a,"dragenter",this)),Array.prototype.forEach.call(a.querySelectorAll("[draggable]"),function(a){a.removeAttribute("draggable")}),ea.splice(ea.indexOf(this._onDragOver),1),this._onDrop(),this.el=a=null}},f(V,"touchmove",function(b){a.active&&b.preventDefault()});try{window.addEventListener("test",null,Object.defineProperty({},"passive",{get:function(){Z={capture:!1,passive:!1}}}))}catch(a){}return a.utils={on:f,off:g,css:i,find:j,is:function(a,b){return!!c(a,b,a)},extend:t,throttle:s,closest:c,toggleClass:h,clone:u,index:q},a.create=function(b,c){return new a(b,c)},a.version="1.6.0",a});


};


modules['helpcenter/primary/models/relatedarticles'] = function(require, exports, module, modules) {
module.exports = Backbone.Model.extend({
	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @param  {Object} options
	 */
    initialize: function (options) {
			this.brandId = options.brandId;
      this.query = options.query;
			this.locale = options.locale;
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @return {Object}
     */
    url: function() {
    	return _Payload.portalpath + 'api/v1/helpcenter/search' + this.locale + '&brand_id=' + this.brandId + '&in=articles&tracking=false&query=' + this.query + '+&include=*';
    }
});
};


modules['helpcenter/primary/views/relatedarticles'] = function(require, exports, module, modules) {
/**
 * @fileoverview Shows article suggestions on typing
 */

module.exports = Backbone.View.extend({
	model: require('helpcenter/primary/models/relatedarticles'),

	el: $('#new-conversation-view'),

	events: {
		'keyup #request-subject, #request-msg': 'typing'
	},

	timer: '',

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Checks if suggestions block exists,
	 *	sets active locale if Help Center is localized
	 */
	initialize: function() {
		this.isEnabled = $('#related-articles').length ? true : false;
		this.locale = _Payload.is_localized ? '&locale=' + _Payload.activelocale : '';
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Triggers fetch() with a delay (400 ms)
	 */
	typing: function() {
		var view = this;
		if (view.isEnabled) {
			var query = $('#request-subject').val() + ' ' + $('#request-msg').val().replace(/\n/g, ' ');

			if (query.length > 3) {
				clearTimeout(view.timer);
		    view.timer = setTimeout(function() {
					view.fetch(query);
		    }, 400);
			} else {
				$('.autocomplete--aside').hide();
			}
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Gets related articles hitting search API
	 * @param {String} query
	 */
	fetch: function(query) {
		var view = this;

		// Show loading animation
		$('.autocomplete--aside')
			.show()
			.addClass('searching')
			.removeClass('no-results');

		// Get results
		new view.model({
			'brandId': _Payload.brand_id,
			'query': query,
			'locale': view.locale
		}).fetch({
			success: function(response) {
				view.render(response.attributes.data);
			},
			error: function(response) {
				view.clearSuggestions();
			}
		});
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Renders suggestions in the sidebar
	 * @param {Object} data
	 */
	render: function(data) {
		if (!_.isEmpty(data)) {
			var liTpl = _.template($('#tpl-related-article-item').html());

			// Create a list of results
			var list = '';
			_.each(data, function(item) {
				list += liTpl({ link: item.link, title: item.title });
			});

			// Hide loading animation and append the list
			$('.autocomplete--aside').removeClass('searching');
			$('#related-articles-list').empty().append(list);
		} else {
			this.clearSuggestions();
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Clears sidebar suggestions
	 */
	clearSuggestions: function() {
		$('.autocomplete--aside').removeClass('searching').addClass('no-results');
		$('#related-articles-list').empty();
	}
});
};


modules['helpcenter/primary/views/focusfield'] = function(require, exports, module, modules) {
module.exports = Backbone.View.extend({
	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Focuses the first required field of a form
	 * @param {Object} options
	 */
	initialize: function(options) {
		this.options = options || {};
		$(this.options.el).find('.js-required-field').first().focus();
	}
});
};


modules['helpcenter/primary/views/toggle'] = function(require, exports, module, modules) {
module.exports = Backbone.View.extend({

    events: {
        'change .toggle': 'change'
    },

    change: function(e) {
        var elem = $(e.target);
        if (elem.is(':checked')) {
            elem.prev('.toggle-value').val('yes');
        } else {
            elem.prev('.toggle-value').val('no');
        }
    }
});

};


modules['helpcenter/primary/views/vote'] = function(require, exports, module, modules) {
var redirect = require('helpcenter/primary/views/redirect');

module.exports = Backbone.View.extend({
  model: require('helpcenter/primary/models/vote'),

  el: $('#vote-view'),

  events: {
    'click .js-vote-option:not(.is-selected)': 'vote',
    'mousedown .js-vote-option:not(.is-selected)': 'iconPress'
  },

  /**
   * @author Prasanjit Singh <prasanjit.singh@kayako.com>
   * @description Sets article ID recieved from router
   * @param  {Object} options
   */
  initialize: function(options) {
    this.options = options;
  },

  /**
   * @author Prasanjit Singh <prasanjit.singh@kayako.com>
   * @description Animates the vote icon
   * @param {Object} e
   */
  iconPress: function(e) {
    var $icon = $(e.target).closest('.js-vote-option');
    $icon.addClass('is-pressed');
    setTimeout(function() {
      $icon.removeClass('is-pressed');
    }, 100);
  },

  /**
   * @author Prasanjit Singh <prasanjit.singh@kayako.com>
   * @description Saves user's vote if user is logged in
   * otherwise shows error message
   * @param  {Object} e
   */
  vote: function(e) {
    var view = this;

    // Show error toast if user is not logged in
    if ($('#is-loggedin').val() != 1) {
      view.toast('error');
      return false;
    } else {
      var $elem = $(e.target).closest('.js-vote-option');

      // Save user vote
      new view.model({
        'articleId': view.options.articleId.split('-')[0],
        'vote': $elem.data('value')
      }).save({}, {
        error: function(model, xhr) {
          redirect('article', view.options.articleId, xhr.status);
        }
      });

      // Highlight selected option
      $('.js-vote-option').removeClass('is-selected');
      $elem.addClass('is-selected');

      // Show thanking message
      view.toast('success');
    }
  },

  /**
   * @author Prasanjit Singh <prasanjit.singh@kayako.com>
   * @description Shows success/error message
   * @param {String} msgType
   */
  toast: function(msgType) {
    var delayTime = (msgType === 'success') ? 150 : 0;

    // Show toast
    setTimeout(function() {
      var msgTpl = _.template($('#tpl-vote-msg').html());
      var msg = msgTpl({ msgType: msgType});
      $('#vote-options').fadeOut(250, function() {
        $('#vote-msg').html(msg).fadeIn(250);
      });
    }, delayTime);

    // Hide toast
    setTimeout(function() {
      $('#vote-msg').fadeOut(250, function() {
        $('#vote-options').fadeIn(250);
      });
    }, 3000);
  }
});
};


modules['helpcenter/primary/plugins/tags/autocomplete'] = function(require, exports, module, modules) {
var jQuery = require('jquery');
/**
 * jQuery TextExt Plugin
 * http://textextjs.com
 *
 * @version 1.3.1
 * @copyright Copyright (C) 2011 Alex Gorbatchev. All rights reserved.
 * @license MIT License
 */
(function($)
{
	/**
	 * Autocomplete plugin brings the classic autocomplete functionality to the TextExt ecosystem.
	 * The gist of functionality is when user starts typing in, for example a term or a tag, a
	 * dropdown would be presented with possible suggestions to complete the input quicker.
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete
	 */
	function TextExtAutocomplete() {};

	$.fn.textext.TextExtAutocomplete = TextExtAutocomplete;
	$.fn.textext.addPlugin('autocomplete', TextExtAutocomplete);

	var p = TextExtAutocomplete.prototype,
		
		CSS_DOT            = '.',
		CSS_SELECTED       = 'text-selected',
		CSS_DOT_SELECTED   = CSS_DOT + CSS_SELECTED,
		CSS_SUGGESTION     = 'text-suggestion',
		CSS_DOT_SUGGESTION = CSS_DOT + CSS_SUGGESTION,
		CSS_LABEL          = 'text-label',
		CSS_DOT_LABEL      = CSS_DOT + CSS_LABEL,

		/**
		 * Autocomplete plugin options are grouped under `autocomplete` when passed to the 
		 * `$().textext()` function. For example:
		 *
		 *     $('textarea').textext({
		 *         plugins: 'autocomplete',
		 *         autocomplete: {
		 *             dropdownPosition: 'above'
		 *         }
		 *     })
		 *
		 * @author agorbatchev
		 * @date 2011/08/17
		 * @id TextExtAutocomplete.options
		 */

		/**
		 * This is a toggle switch to enable or disable the Autucomplete plugin. The value is checked
		 * each time at the top level which allows you to toggle this setting on the fly.
		 *
		 * @name autocomplete.enabled
		 * @default true
		 * @author agorbatchev
		 * @date 2011/08/17
		 * @id TextExtAutocomplete.options.autocomplete.enabled
		 */
		OPT_ENABLED = 'autocomplete.enabled',

		/**
		 * This option allows to specify position of the dropdown. The two possible values
		 * are `above` and `below`.
		 *
		 * @name autocomplete.dropdown.position
		 * @default "below"
		 * @author agorbatchev
		 * @date 2011/08/17
		 * @id TextExtAutocomplete.options.autocomplete.dropdown.position
		 */
		OPT_POSITION = 'autocomplete.dropdown.position',

		/**
		 * This option allows to specify maximum height of the dropdown. Value is taken directly, so
		 * if desired height is 200 pixels, value must be `200px`.
		 *
		 * @name autocomplete.dropdown.maxHeight
		 * @default "100px"
		 * @author agorbatchev
		 * @date 2011/12/29
		 * @id TextExtAutocomplete.options.autocomplete.dropdown.maxHeight
		 * @version 1.1
		 */
		OPT_MAX_HEIGHT = 'autocomplete.dropdown.maxHeight',

		/**
		 * This option allows to override how a suggestion item is rendered. The value should be
		 * a function, the first argument of which is suggestion to be rendered and `this` context
		 * is the current instance of `TextExtAutocomplete`. 
		 *
		 * [Click here](/manual/examples/autocomplete-with-custom-render.html) to see a demo.
		 *
		 * For example:
		 *
		 *     $('textarea').textext({
		 *         plugins: 'autocomplete',
		 *         autocomplete: {
		 *             render: function(suggestion)
		 *             {
		 *                 return '<b>' + suggestion + '</b>';
		 *             }
		 *         }
		 *     })
		 *
		 * @name autocomplete.render
		 * @default null
		 * @author agorbatchev
		 * @date 2011/12/23
		 * @id TextExtAutocomplete.options.autocomplete.render
		 * @version 1.1
		 */
		OPT_RENDER = 'autocomplete.render',

		/**
		 * HTML source that is used to generate the dropdown.
		 *
		 * @name html.dropdown
		 * @default '<div class="text-dropdown"><div class="text-list"/></div>'
		 * @author agorbatchev
		 * @date 2011/08/17
		 * @id TextExtAutocomplete.options.html.dropdown
		 */
		OPT_HTML_DROPDOWN = 'html.dropdown',

		/**
		 * HTML source that is used to generate each suggestion.
		 *
		 * @name html.suggestion
		 * @default '<div class="text-suggestion"><span class="text-label"/></div>'
		 * @author agorbatchev
		 * @date 2011/08/17
		 * @id TextExtAutocomplete.options.html.suggestion
		 */
		OPT_HTML_SUGGESTION = 'html.suggestion',

		/**
		 * Autocomplete plugin triggers or reacts to the following events.
		 *
		 * @author agorbatchev
		 * @date 2011/08/17
		 * @id TextExtAutocomplete.events
		 */
	
		/**
		 * Autocomplete plugin triggers and reacts to the `hideDropdown` to hide the dropdown if it's 
		 * already visible.
		 *
		 * @name hideDropdown
		 * @author agorbatchev
		 * @date 2011/08/17
		 * @id TextExtAutocomplete.events.hideDropdown
		 */
		EVENT_HIDE_DROPDOWN = 'hideDropdown',

		/**
		 * Autocomplete plugin triggers and reacts to the `showDropdown` to show the dropdown if it's 
		 * not already visible.
		 *
		 * It's possible to pass a render callback function which will be called instead of the
		 * default `TextExtAutocomplete.renderSuggestions()`. 
		 *
		 * Here's how another plugin should trigger this event with the optional render callback:
		 *
		 *     this.trigger('showDropdown', function(autocomplete)
		 *     {
		 *         autocomplete.clearItems();
		 *         var node = autocomplete.addDropdownItem('<b>Item</b>');
		 *         node.addClass('new-look');
		 *     });
		 *
		 * @name showDropdown
		 * @author agorbatchev
		 * @date 2011/08/17
		 * @id TextExtAutocomplete.events.showDropdown
		 */
		EVENT_SHOW_DROPDOWN = 'showDropdown',

		/**
		 * Autocomplete plugin reacts to the `setSuggestions` event triggered by other plugins which
		 * wish to populate the suggestion items. Suggestions should be passed as event argument in the 
		 * following format: `{ data : [ ... ] }`. 
		 *
		 * Here's how another plugin should trigger this event:
		 *
		 *     this.trigger('setSuggestions', { data : [ "item1", "item2" ] });
		 *
		 * @name setSuggestions
		 * @author agorbatchev
		 * @date 2011/08/17
		 * @id TextExtAutocomplete.events.setSuggestions
		 */

		/**
		 * Autocomplete plugin triggers the `getSuggestions` event and expects to get results by listening for
		 * the `setSuggestions` event.
		 *
		 * @name getSuggestions
		 * @author agorbatchev
		 * @date 2011/08/17
		 * @id TextExtAutocomplete.events.getSuggestions
		 */
		EVENT_GET_SUGGESTIONS = 'getSuggestions',

		/**
		 * Autocomplete plugin triggers `getFormData` event with the current suggestion so that the the core
		 * will be updated with serialized data to be submitted with the HTML form.
		 * 
		 * @name getFormData
		 * @author agorbatchev
		 * @date 2011/08/18
		 * @id TextExtAutocomplete.events.getFormData
		 */
		EVENT_GET_FORM_DATA = 'getFormData',

		/**
		 * Autocomplete plugin reacts to `toggleDropdown` event and either shows or hides the dropdown
		 * depending if it's currently hidden or visible.
		 * 
		 * @name toggleDropdown
		 * @author agorbatchev
		 * @date 2011/12/27
		 * @id TextExtAutocomplete.events.toggleDropdown
		 * @version 1.1
		 */
		EVENT_TOGGLE_DROPDOWN = 'toggleDropdown',

		POSITION_ABOVE = 'above',
		POSITION_BELOW = 'below',
		
		DATA_MOUSEDOWN_ON_AUTOCOMPLETE = 'mousedownOnAutocomplete',

		DEFAULT_OPTS = {
			autocomplete : {
				enabled : true,
				dropdown : {
					position : POSITION_BELOW,
					maxHeight : '100px'
				}
			},

			html : {
				dropdown   : '<div class="text-dropdown"><div class="text-list"/></div>',
				suggestion : '<div class="text-suggestion"><span class="text-label"/></div>'
			}
		}
		;

	/**
	 * Initialization method called by the core during plugin instantiation.
	 *
	 * @signature TextExtAutocomplete.init(core)
	 *
	 * @param core {TextExt} Instance of the TextExt core class.
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.init
	 */
	p.init = function(core)
	{
		var self = this;

		self.baseInit(core, DEFAULT_OPTS);

		var input = self.input(),
			container
			;

		if(self.opts(OPT_ENABLED) === true)
		{
			self.on({
				blur              : self.onBlur,
				anyKeyUp          : self.onAnyKeyUp,
				anyKeyDown        : self.onAnyKeyDown,
				deleteKeyUp       : self.onAnyKeyUp,
				backspaceKeyPress : self.onBackspaceKeyPress,
				enterKeyPress     : self.onEnterKeyPress,
				escapeKeyPress    : self.onEscapeKeyPress,
				setSuggestions    : self.onSetSuggestions,
				showDropdown      : self.onShowDropdown,
				hideDropdown      : self.onHideDropdown,
				toggleDropdown    : self.onToggleDropdown,
				postInvalidate    : self.positionDropdown,
				getFormData       : self.onGetFormData,

				// using keyDown for up/down keys so that repeat events are
				// captured and user can scroll up/down by holding the keys
				downKeyDown : self.onDownKeyDown,
				upKeyDown   : self.onUpKeyDown
			});

			container = $(self.opts(OPT_HTML_DROPDOWN));
			container.insertAfter(input);

			self.on(container, {
				mouseover : self.onMouseOver,
				mousedown : self.onMouseDown,
				click     : self.onClick
			});

			container
				.css('maxHeight', self.opts(OPT_MAX_HEIGHT))
				.addClass('text-position-' + self.opts(OPT_POSITION))
				;

			$(self).data('container', container);
			
			$(document.body).click(function(e) 
			{
				if (self.isDropdownVisible() && !self.withinWrapElement(e.target))
					self.trigger(EVENT_HIDE_DROPDOWN);
			});

			self.positionDropdown();
		}
	};

	/**
	 * Returns top level dropdown container HTML element.
	 * 
	 * @signature TextExtAutocomplete.containerElement()
	 * 
	 * @author agorbatchev
	 * @date 2011/08/15
	 * @id TextExtAutocomplete.containerElement
	 */
	p.containerElement = function()
	{
		return $(this).data('container');
	};

	//--------------------------------------------------------------------------------
	// User mouse/keyboard input
	
	/**
	 * Reacts to the `mouseOver` event triggered by the TextExt core.
	 *
	 * @signature TextExtAutocomplete.onMouseOver(e)
	 *
	 * @param e {Object} jQuery event.
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.onMouseOver
	 */
	p.onMouseOver = function(e)
	{
		var self   = this,
			target = $(e.target)
			;

		if(target.is(CSS_DOT_SUGGESTION))
		{
			self.clearSelected();
			target.addClass(CSS_SELECTED);
		}
	};
	
	/**
	 * Reacts to the `mouseDown` event triggered by the TextExt core.
	 *
	 * @signature TextExtAutocomplete.onMouseDown(e)
	 *
	 * @param e {Object} jQuery event.
	 *
	 * @author adamayres
	 * @date 2012/01/13
	 * @id TextExtAutocomplete.onMouseDown
	 */
	p.onMouseDown = function(e)
	{
		this.containerElement().data(DATA_MOUSEDOWN_ON_AUTOCOMPLETE, true);
	};
	
	/**
	 * Reacts to the `click` event triggered by the TextExt core.
	 *
	 * @signature TextExtAutocomplete.onClick(e)
	 *
	 * @param e {Object} jQuery event.
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.onClick
	 */
	p.onClick = function(e)
	{
		var self   = this,
			target = $(e.target)
			;

		if(target.is(CSS_DOT_SUGGESTION) || target.is(CSS_DOT_LABEL))
			self.trigger('enterKeyPress');
		
		if (self.core().hasPlugin('tags'))
			self.val('');
	};

	/**
	 * Reacts to the `blur` event triggered by the TextExt core.
	 *
	 * @signature TextExtAutocomplete.onBlur(e)
	 *
	 * @param e {Object} jQuery event.
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.onBlur
	 */
	p.onBlur = function(e)
	{
		var self              = this,
			container         = self.containerElement(),
			isBlurByMousedown = container.data(DATA_MOUSEDOWN_ON_AUTOCOMPLETE) === true
			;

		// only trigger a close event if the blur event was 
		// not triggered by a mousedown event on the autocomplete
		// otherwise set focus back back on the input
		if(self.isDropdownVisible())
			isBlurByMousedown ? self.core().focusInput() : self.trigger(EVENT_HIDE_DROPDOWN);
				
		container.removeData(DATA_MOUSEDOWN_ON_AUTOCOMPLETE);
	};

	/**
	 * Reacts to the `backspaceKeyPress` event triggered by the TextExt core. 
	 *
	 * @signature TextExtAutocomplete.onBackspaceKeyPress(e)
	 *
	 * @param e {Object} jQuery event.
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.onBackspaceKeyPress
	 */
	p.onBackspaceKeyPress = function(e)
	{
		var self    = this,
			isEmpty = self.val().length > 0
			;

		if(isEmpty || self.isDropdownVisible())
			self.getSuggestions();
	};

	/**
	 * Reacts to the `anyKeyUp` event triggered by the TextExt core.
	 *
	 * @signature TextExtAutocomplete.onAnyKeyUp(e)
	 *
	 * @param e {Object} jQuery event.
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.onAnyKeyUp
	 */
	p.onAnyKeyUp = function(e, keyCode)
	{	var self = this;
		var isFunctionKey = self.opts('keys.' + keyCode) != null;

		if(self.val().length > 0 && !isFunctionKey && keyCode !== 13 && keyCode !== 32)
			self.getSuggestions();

		if ($('#artcle-tags').val()) {
			appendTags($('#artcle-tags'), self);
		}
		if ($('#section-tags').val()) {
			appendTags($('#section-tags'), self);
		}

		function appendTags(elm, self) {
			if (keyCode == 32 && elm.val().trim().length) {
				var tagValue = self.val().trim();
				if(self.isDropdownVisible()) {
					self.selectFromDropdown();
					tagValue = self.val().trim();
				}
				elm.val('').textext()[0]
					.tags().addTags([tagValue]);
					self.onBlur();
			}
		}
	};

	/**
	 * Reacts to the `anyKeyDown` event triggered by the TextExt core.
	 *
	 * @signature TextExtAutocomplete.onAnyKeyDown(e)
	 *
	 * @param e {Object} jQuery event.
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.onAnyKeyDown
	 */
	p.onAnyKeyDown = function(e, keyCode)
	{

	};

	/**
	 * Reacts to the `downKeyDown` event triggered by the TextExt core.
	 *
	 * @signature TextExtAutocomplete.onDownKeyDown(e)
	 *
	 * @param e {Object} jQuery event.
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.onDownKeyDown
	 */
	p.onDownKeyDown = function(e)
	{
		var self = this;

		self.isDropdownVisible()
			? self.toggleNextSuggestion() 
			: self.getSuggestions()
			;
	};

	/**
	 * Reacts to the `upKeyDown` event triggered by the TextExt core.
	 *
	 * @signature TextExtAutocomplete.onUpKeyDown(e)
	 *
	 * @param e {Object} jQuery event.
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.onUpKeyDown
	 */
	p.onUpKeyDown = function(e)
	{
		this.togglePreviousSuggestion();
	};

	/**
	 * Reacts to the `enterKeyPress` event triggered by the TextExt core.
	 *
	 * @signature TextExtAutocomplete.onEnterKeyPress(e)
	 *
	 * @param e {Object} jQuery event.
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.onEnterKeyPress
	 */
	p.onEnterKeyPress = function(e)
	{
		var self = this;

		if(self.isDropdownVisible())
			self.selectFromDropdown();
	};

	/**
	 * Reacts to the `escapeKeyPress` event triggered by the TextExt core. Hides the dropdown
	 * if it's currently visible.
	 *
	 * @signature TextExtAutocomplete.onEscapeKeyPress(e)
	 *
	 * @param e {Object} jQuery event.
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.onEscapeKeyPress
	 */
	p.onEscapeKeyPress = function(e)
	{
		var self = this;

		if(self.isDropdownVisible())
			self.trigger(EVENT_HIDE_DROPDOWN);
	};

	//--------------------------------------------------------------------------------
	// Core functionality

	/**
	 * Positions dropdown either below or above the input based on the `autocomplete.dropdown.position`
	 * option specified, which could be either `above` or `below`.
	 *
	 * @signature TextExtAutocomplete.positionDropdown()
	 *
	 * @author agorbatchev
	 * @date 2011/08/15
	 * @id TextExtAutocomplete.positionDropdown
	 */
	p.positionDropdown = function()
	{
		var self      = this,
			container = self.containerElement(),
			direction = self.opts(OPT_POSITION),
			height    = self.core().wrapElement().outerHeight(),
			css       = {}
			;

		css[direction === POSITION_ABOVE ? 'bottom' : 'top'] = height + 'px';
		container.css(css);
	};

	/**
	 * Returns list of all the suggestion HTML elements in the dropdown.
	 *
	 * @signature TextExtAutocomplete.suggestionElements()
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.suggestionElements
	 */
	p.suggestionElements = function()
	{
		return this.containerElement().find(CSS_DOT_SUGGESTION);
	};


	/**
	 * Highlights specified suggestion as selected in the dropdown.
	 *
	 * @signature TextExtAutocomplete.setSelectedSuggestion(suggestion)
	 *
	 * @param suggestion {Object} Suggestion object. With the default `ItemManager` this
	 * is expected to be a string, anything else with custom implementations.
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.setSelectedSuggestion
	 */
	p.setSelectedSuggestion = function(suggestion)
	{
		if(!suggestion)
			return;

		var self   = this,
			all    = self.suggestionElements(),
			target = all.first(),
			item, i
			;

		self.clearSelected();

		for(i = 0; i < all.length; i++)
		{
			item = $(all[i]);

			if(self.itemManager().compareItems(item.data(CSS_SUGGESTION), suggestion))
			{
				target = item.addClass(CSS_SELECTED);
				break;
			}
		}

		target.addClass(CSS_SELECTED);
		self.scrollSuggestionIntoView(target);
	};

	/**
	 * Returns the first suggestion HTML element from the dropdown that is highlighted as selected.
	 *
	 * @signature TextExtAutocomplete.selectedSuggestionElement()
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.selectedSuggestionElement
	 */
	p.selectedSuggestionElement = function()
	{
		return this.suggestionElements().filter(CSS_DOT_SELECTED).first();
	};

	/**
	 * Returns `true` if dropdown is currently visible, `false` otherwise.
	 *
	 * @signature TextExtAutocomplete.isDropdownVisible()
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.isDropdownVisible
	 */
	p.isDropdownVisible = function()
	{
		return this.containerElement().is(':visible') === true;
	};

	/**
	 * Reacts to the `getFormData` event triggered by the core. Returns data with the
	 * weight of 100 to be *less than the Tags plugin* data weight. The weights system is
	 * covered in greater detail in the [`getFormData`][1] event documentation.
	 *
	 * [1]: /manual/textext.html#getformdata
	 *
	 * @signature TextExtAutocomplete.onGetFormData(e, data, keyCode)
	 *
	 * @param e {Object} jQuery event.
	 * @param data {Object} Data object to be populated.
	 * @param keyCode {Number} Key code that triggered the original update request.
	 *
	 * @author agorbatchev
	 * @date 2011/08/22
	 * @id TextExtAutocomplete.onGetFormData
	 */
	p.onGetFormData = function(e, data, keyCode)
	{
		var self       = this,
			val        = self.val(),
			inputValue = val,
			formValue  = val
			;
		data[100] = self.formDataObject(inputValue, formValue);
	};

	/**
	 * Returns initialization priority of the Autocomplete plugin which is expected to be
	 * *greater than the Tags plugin* because of the dependencies. The value is 200.
	 *
	 * @signature TextExtAutocomplete.initPriority()
	 *
	 * @author agorbatchev
	 * @date 2011/08/22
	 * @id TextExtAutocomplete.initPriority
	 */
	p.initPriority = function()
	{
		return 200;
	};

	/**
	 * Reacts to the `hideDropdown` event and hides the dropdown if it's already visible.
	 *
	 * @signature TextExtAutocomplete.onHideDropdown(e)
	 *
	 * @param e {Object} jQuery event.
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.onHideDropdown
	 */
	p.onHideDropdown = function(e)
	{
		this.hideDropdown();
	};

	/**
	 * Reacts to the 'toggleDropdown` event and shows or hides the dropdown depending if
	 * it's currently hidden or visible.
	 *
	 * @signature TextExtAutocomplete.onToggleDropdown(e)
	 *
	 * @param e {Object} jQuery event.
	 *
	 * @author agorbatchev
	 * @date 2011/12/27
	 * @id TextExtAutocomplete.onToggleDropdown
	 * @version 1.1.0
	 */
	p.onToggleDropdown = function(e)
	{
		var self = this;
		self.trigger(self.containerElement().is(':visible') ? EVENT_HIDE_DROPDOWN : EVENT_SHOW_DROPDOWN);
	};

	/**
	 * Reacts to the `showDropdown` event and shows the dropdown if it's not already visible.
	 * It's possible to pass a render callback function which will be called instead of the
	 * default `TextExtAutocomplete.renderSuggestions()`.
	 *
	 * If no suggestion were previously loaded, it will fire `getSuggestions` event and exit.
	 *
	 * Here's how another plugin should trigger this event with the optional render callback:
	 *
	 *     this.trigger('showDropdown', function(autocomplete)
	 *     {
	 *         autocomplete.clearItems();
	 *         var node = autocomplete.addDropdownItem('<b>Item</b>');
	 *         node.addClass('new-look');
	 *     });
	 *
	 * @signature TextExtAutocomplete.onShowDropdown(e, renderCallback)
	 *
	 * @param e {Object} jQuery event.
	 * @param renderCallback {Function} Optional callback function which would be used to 
	 * render dropdown items. As a first argument, reference to the current instance of 
	 * Autocomplete plugin will be supplied. It's assumed, that if this callback is provided
	 * rendering will be handled completely manually.
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.onShowDropdown
	 */
	p.onShowDropdown = function(e, renderCallback)
	{
		var self        = this,
			current     = self.selectedSuggestionElement().data(CSS_SUGGESTION),
			suggestions = self._suggestions
			;

		if(!suggestions)
			return self.trigger(EVENT_GET_SUGGESTIONS);

		if($.isFunction(renderCallback))
		{
			renderCallback(self);
		}
		else
		{
			self.renderSuggestions(self._suggestions);
			self.toggleNextSuggestion();
		}
		
		self.showDropdown(self.containerElement());
		self.setSelectedSuggestion(current);
	};

	/**
	 * Reacts to the `setSuggestions` event. Expects to recieve the payload as the second argument
	 * in the following structure:
	 *
	 *     {
	 *         result : [ "item1", "item2" ],
	 *         showHideDropdown : false
	 *     }
	 *
	 * Notice the optional `showHideDropdown` option. By default, ie without the `showHideDropdown` 
	 * value the method will trigger either `showDropdown` or `hideDropdown` depending if there are
	 * suggestions. If set to `false`, no event is triggered.
	 *
	 * @signature TextExtAutocomplete.onSetSuggestions(e, data)
	 *
	 * @param data {Object} Data payload.
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.onSetSuggestions
	 */
	p.onSetSuggestions = function(e, data)
	{
		var self        = this,
			suggestions = self._suggestions = data.result
			;

		if(data.showHideDropdown !== false)
			self.trigger(suggestions === null || suggestions.length === 0 ? EVENT_HIDE_DROPDOWN : EVENT_SHOW_DROPDOWN);
	};

	/**
	 * Prepears for and triggers the `getSuggestions` event with the `{ query : {String} }` as second
	 * argument.
	 *
	 * @signature TextExtAutocomplete.getSuggestions()
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.getSuggestions
	 */
	p.getSuggestions = function()
	{
		var self = this,
			val  = self.val()
			;

		if(self._previousInputValue == val)
			return;

		// if user clears input, then we want to select first suggestion
		// instead of the last one
		if(val == '')
			current = null;

		self._previousInputValue = val;
		self.trigger(EVENT_GET_SUGGESTIONS, { query : val });
	};

	/**
	 * Removes all HTML suggestion items from the dropdown.
	 *
	 * @signature TextExtAutocomplete.clearItems()
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.clearItems
	 */
	p.clearItems = function()
	{
		this.containerElement().find('.text-list').children().remove();
	};

	/**
	 * Clears all and renders passed suggestions.
	 *
	 * @signature TextExtAutocomplete.renderSuggestions(suggestions)
	 *
	 * @param suggestions {Array} List of suggestions to render.
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.renderSuggestions
	 */
	p.renderSuggestions = function(suggestions)
	{
		var self = this;

		self.clearItems();

		var existingTags = $('#tags-array').val();

		$.each(suggestions || [], function(index, item) {
			if (existingTags.match(new RegExp("(?:^|,)" + item + "(?:,|$)")) === null) {
				self.addSuggestion(item);
			}
		});
	};

	/**
	 * Shows the dropdown.
	 *
	 * @signature TextExtAutocomplete.showDropdown()
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.showDropdown
	 */
	p.showDropdown = function()
	{
		this.containerElement().show();
	};

	/**
	 * Hides the dropdown.
	 *
	 * @signature TextExtAutocomplete.hideDropdown()
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.hideDropdown
	 */
	p.hideDropdown = function()
	{
		var self     = this,
			dropdown = self.containerElement()
			;

		self._previousInputValue = null;
		dropdown.hide();
	};

	/**
	 * Adds single suggestion to the bottom of the dropdown. Uses `ItemManager.itemToString()` to
	 * serialize provided suggestion to string.
	 *
	 * @signature TextExtAutocomplete.addSuggestion(suggestion)
	 *
	 * @param suggestion {Object} Suggestion item. By default expected to be a string.
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.addSuggestion
	 */
	p.addSuggestion = function(suggestion)
	{
		var self     = this,
			renderer = self.opts(OPT_RENDER),
			node     = self.addDropdownItem(renderer ? renderer.call(self, suggestion) : self.itemManager().itemToString(suggestion))
			;

		node.data(CSS_SUGGESTION, suggestion);
	};

	/**
	 * Adds and returns HTML node to the bottom of the dropdown.
	 *
	 * @signature TextExtAutocomplete.addDropdownItem(html)
	 *
	 * @param html {String} HTML to be inserted into the item.
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.addDropdownItem
	 */
	p.addDropdownItem = function(html)
	{
		var self      = this,
			container = self.containerElement().find('.text-list'),
			node      = $(self.opts(OPT_HTML_SUGGESTION))
			;

		node.find('.text-label').html(html);
		container.append(node);
		return node;
	};

	/**
	 * Removes selection highlight from all suggestion elements.
	 *
	 * @signature TextExtAutocomplete.clearSelected()
	 *
	 * @author agorbatchev
	 * @date 2011/08/02
	 * @id TextExtAutocomplete.clearSelected
	 */
	p.clearSelected = function()
	{
		this.suggestionElements().removeClass(CSS_SELECTED);
	};

	/**
	 * Selects next suggestion relative to the current one. If there's no
	 * currently selected suggestion, it will select the first one. Selected
	 * suggestion will always be scrolled into view.
	 *
	 * @signature TextExtAutocomplete.toggleNextSuggestion()
	 *
	 * @author agorbatchev
	 * @date 2011/08/02
	 * @id TextExtAutocomplete.toggleNextSuggestion
	 */
	p.toggleNextSuggestion = function()
	{
		var self     = this,
			selected = self.selectedSuggestionElement(),
			next
			;

		if(selected.length > 0)
		{
			next = selected.next();

			if(next.length > 0)
				selected.removeClass(CSS_SELECTED);
		}
		else
		{
			next = self.suggestionElements().first();
		}

		next.addClass(CSS_SELECTED);
		self.scrollSuggestionIntoView(next);
	};

	/**
	 * Selects previous suggestion relative to the current one. Selected
	 * suggestion will always be scrolled into view.
	 *
	 * @signature TextExtAutocomplete.togglePreviousSuggestion()
	 *
	 * @author agorbatchev
	 * @date 2011/08/02
	 * @id TextExtAutocomplete.togglePreviousSuggestion
	 */
	p.togglePreviousSuggestion = function()
	{
		var self     = this,
			selected = self.selectedSuggestionElement(),
			prev     = selected.prev()
			;

		if(prev.length == 0)
			return;

		self.clearSelected();
		prev.addClass(CSS_SELECTED);
		self.scrollSuggestionIntoView(prev);
	};

	/**
	 * Scrolls specified HTML suggestion element into the view.
	 *
	 * @signature TextExtAutocomplete.scrollSuggestionIntoView(item)
	 *
	 * @param item {HTMLElement} jQuery HTML suggestion element which needs to
	 * scrolled into view.
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.scrollSuggestionIntoView
	 */
	p.scrollSuggestionIntoView = function(item)
	{
		var itemHeight     = item.outerHeight(),
			dropdown       = this.containerElement(),
			dropdownHeight = dropdown.innerHeight(),
			scrollPos      = dropdown.scrollTop(),
			itemTop        = (item.position() || {}).top,
			scrollTo       = null,
			paddingTop     = parseInt(dropdown.css('paddingTop'))
			;

		if(itemTop == null)
			return;

		// if scrolling down and item is below the bottom fold
		if(itemTop + itemHeight > dropdownHeight)
			scrollTo = itemTop + scrollPos + itemHeight - dropdownHeight + paddingTop;

		// if scrolling up and item is above the top fold
		if(itemTop < 0)
			scrollTo = itemTop + scrollPos - paddingTop;

		if(scrollTo != null)
			dropdown.scrollTop(scrollTo);
	};

	/**
	 * Uses the value from the text input to finish autocomplete action. Currently selected
	 * suggestion from the dropdown will be used to complete the action. Triggers `hideDropdown`
	 * event.
	 *
	 * @signature TextExtAutocomplete.selectFromDropdown()
	 *
	 * @author agorbatchev
	 * @date 2011/08/17
	 * @id TextExtAutocomplete.selectFromDropdown
	 */
	p.selectFromDropdown = function()
	{
		var self       = this,
			suggestion = self.selectedSuggestionElement().data(CSS_SUGGESTION)
			;

		if(suggestion)
		{
			self.val(self.itemManager().itemToString(suggestion));
			self.core().getFormData();	
		}

		self.trigger(EVENT_HIDE_DROPDOWN);
	};
	
	/**
	 * Determines if the specified HTML element is within the TextExt core wrap HTML element.
	 *
	 * @signature TextExtAutocomplete.withinWrapElement(element)
	 *
	 * @param element {HTMLElement} element to check if contained by wrap element
	 *
	 * @author adamayres
	 * @version 1.3.0
	 * @date 2012/01/15
	 * @id TextExtAutocomplete.withinWrapElement
	 */
	p.withinWrapElement = function(element) 
	{
		return this.core().wrapElement().find(element).size() > 0;
	}
})(jQuery);

};


modules['helpcenter/primary/plugins/froala/colors'] = function(require, exports, module, modules) {
/*!
 * froala_editor v2.3.4 (https://www.froala.com/wysiwyg-editor)
 * License https://froala.com/wysiwyg-editor/terms/
 * Copyright 2014-2016 Froala Labs
 */

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof module === 'object' && module.exports) {
        // Node/CommonJS
        module.exports = function( root, jQuery ) {
            if ( jQuery === undefined ) {
                // require('jQuery') returns a factory that requires window to
                // build a jQuery instance, we normalize how we use modules
                // that require this pattern but the window provided is a noop
                // if it's defined (how jquery works)
                if ( typeof window !== 'undefined' ) {
                    jQuery = require('jquery');
                }
                else {
                    jQuery = require('jquery')(root);
                }
            }
            factory(jQuery);
            return jQuery;
        };
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {

  'use strict';

  $.extend($.FE.POPUP_TEMPLATES, {
    'colors.picker': '[_BUTTONS_][_TEXT_COLORS_][_BACKGROUND_COLORS_]'
  })

  // Extend defaults.
  $.extend($.FE.DEFAULTS, {
    colorsText: [
      '#61BD6D', '#1ABC9C', '#54ACD2', '#2C82C9', '#9365B8', '#475577', '#CCCCCC',
      '#41A85F', '#00A885', '#3D8EB9', '#2969B0', '#553982', '#28324E', '#000000',
      '#F7DA64', '#FBA026', '#EB6B56', '#E25041', '#A38F84', '#EFEFEF', '#FFFFFF',
      '#FAC51C', '#F37934', '#D14841', '#B8312F', '#7C706B', '#D1D5D8', 'REMOVE'
    ],
    colorsBackground: [
      '#61BD6D', '#1ABC9C', '#54ACD2', '#2C82C9', '#9365B8', '#475577', '#CCCCCC',
      '#41A85F', '#00A885', '#3D8EB9', '#2969B0', '#553982', '#28324E', '#000000',
      '#F7DA64', '#FBA026', '#EB6B56', '#E25041', '#A38F84', '#EFEFEF', '#FFFFFF',
      '#FAC51C', '#F37934', '#D14841', '#B8312F', '#7C706B', '#D1D5D8', 'REMOVE'
    ],
    colorsStep: 7,
    colorsDefaultTab: 'text',
    colorsButtons: ['colorsBack', '|', '-']
  });

  $.FE.PLUGINS.colors = function (editor) {
    /*
     * Show the colors popup.
     */
    function _showColorsPopup () {
      var $btn = editor.$tb.find('.fr-command[data-cmd="color"]');

      var $popup = editor.popups.get('colors.picker');
      if (!$popup) $popup = _initColorsPopup();

      if (!$popup.hasClass('fr-active')) {
        // Colors popup
        editor.popups.setContainer('colors.picker', editor.$tb);

        // Refresh selected color.
        _refreshColor($popup.find('.fr-selected-tab').attr('data-param1'));

        // Colors popup left and top position.
        if ($btn.is(':visible')) {
          var left = $btn.offset().left + $btn.outerWidth() / 2;
          var top = $btn.offset().top + (editor.opts.toolbarBottom ? 10 : $btn.outerHeight() - 10);
          editor.popups.show('colors.picker', left, top, $btn.outerHeight());
        }
        else {
          editor.position.forSelection($popup);
          editor.popups.show('colors.picker');
        }
      }
    }

    /*
     * Hide colors popup.
     */
    function _hideColorsPopup () {
      // Hide popup.
      editor.popups.hide('colors.picker');
    }

    /**
     * Init the colors popup.
     */
    function _initColorsPopup () {
      var colors_buttons = '<div class="fr-buttons fr-colors-buttons">';

      if (editor.opts.toolbarInline) {
        // Colors buttons.
        if (editor.opts.colorsButtons.length > 0) {
          colors_buttons += editor.button.buildList(editor.opts.colorsButtons)
        }
      }

      colors_buttons += _colorsTabsHTML() + '</div>';

      var template = {
        buttons: colors_buttons,
        text_colors: _colorPickerHTML('text'),
        background_colors: _colorPickerHTML('background')
      };

      // Create popup.
      var $popup = editor.popups.create('colors.picker', template);

      return $popup;
    }

    /*
     * HTML for the color picker text and background tabs.
     */
    function _colorsTabsHTML () {
      var tabs_html = '<div class="fr-colors-tabs">';

      // Text tab.
      tabs_html += '<span class="fr-colors-tab ' + (editor.opts.colorsDefaultTab == 'background' ? '' : 'fr-selected-tab ') + 'fr-command" data-param1="text" data-cmd="colorChangeSet" title="' + editor.language.translate('Text') + '">' + editor.language.translate('Text') + '</span>';

      // Background tab.
      tabs_html += '<span class="fr-colors-tab ' + (editor.opts.colorsDefaultTab == 'background' ? 'fr-selected-tab ' : '') + 'fr-command" data-param1="background" data-cmd="colorChangeSet" title="' + editor.language.translate('Background') + '">' + editor.language.translate('Background') + '</span>';

      return tabs_html + '</div>';
    }

    /*
     * HTML for the color picker colors.
     */
    function _colorPickerHTML (tab) {
      // Get colors according to tab name.
      var colors = (tab == 'text' ? editor.opts.colorsText : editor.opts.colorsBackground);

      // Create colors html.
      var colors_html = '<div class="fr-color-set fr-' + tab + '-color' + ((editor.opts.colorsDefaultTab == tab || (editor.opts.colorsDefaultTab != 'text' && editor.opts.colorsDefaultTab != 'background' && tab == 'text')) ? ' fr-selected-set' : '') + '">';

      // Add colors.
      for (var i = 0; i < colors.length; i++) {
        if (i !== 0 && i % editor.opts.colorsStep === 0) {
          colors_html += '<br>';
        }

        if (colors[i] != 'REMOVE') {
          colors_html += '<span class="fr-command fr-select-color" style="background: ' + colors[i] + ';" data-cmd="' + tab + 'Color" data-param1="' + colors[i] + '"></span>';
        }

        else {
          colors_html += '<span class="fr-command fr-select-color" data-cmd="' + tab + 'Color" data-param1="REMOVE" title="' + editor.language.translate('Clear Formatting') + '">' + editor.icon.create('remove') + '</span>';
        }
      }

      return colors_html + '</div>';
    }

    /*
     * Show the current selected color.
     */
    function _refreshColor (tab) {
      var $popup = editor.popups.get('colors.picker');
      var $element = $(editor.selection.element());

      // The color css property.
      var color_type;
      if (tab == 'background') {
        color_type = 'background-color';
      }
      else {
        color_type = 'color';
      }

      // Remove current color selection.
      $popup.find('.fr-' + tab + '-color .fr-select-color').removeClass('fr-selected-color');

      // Find the selected color.
      while ($element.get(0) != editor.$el.get(0)) {
        // Transparent or black.
        if ($element.css(color_type) == 'transparent' || $element.css(color_type) == 'rgba(0, 0, 0, 0)') {
          $element = $element.parent();
        }

        // Select the correct color.
        else {
          $popup.find('.fr-' + tab + '-color .fr-select-color[data-param1="' + editor.helpers.RGBToHex($element.css(color_type)) + '"]').addClass('fr-selected-color');
          break;
        }
      }
    }

    /*
     * Change the colors' tab.
     */
    function _changeSet ($tab, val) {
      // Only on the tab that is not selected yet. On left click only.
      if (!$tab.hasClass('fr-selected-tab')) {
        // Switch selected tab.
        $tab.siblings().removeClass('fr-selected-tab');
        $tab.addClass('fr-selected-tab');

        // Switch the color set.
        $tab.parents('.fr-popup').find('.fr-color-set').removeClass('fr-selected-set');
        $tab.parents('.fr-popup').find('.fr-color-set.fr-' + val + '-color').addClass('fr-selected-set');

        // Refresh selected color.
        _refreshColor(val);
      }
    }

    /*
     * Change background color.
     */
    function background (val) {
      // Set background  color.
      if (val != 'REMOVE') {
        editor.format.applyStyle('background-color', editor.helpers.HEXtoRGB(val));
      }

      // Remove background color.
      else {
        editor.format.removeStyle('background-color');
      }

      _hideColorsPopup();
    }

    /*
     * Change text color.
     */
    function text (val) {
      // Set text color.
      if (val != 'REMOVE') {
        editor.format.applyStyle('color', editor.helpers.HEXtoRGB(val));
      }

      // Remove text color.
      else {
        editor.format.removeStyle('color');
      }

      _hideColorsPopup();
    }

    /*
     * Go back to the inline editor.
     */
    function back () {
      editor.popups.hide('colors.picker');
      editor.toolbar.showInline();
    }

    return {
      showColorsPopup: _showColorsPopup,
      hideColorsPopup: _hideColorsPopup,
      changeSet: _changeSet,
      background: background,
      text: text,
      back: back
    }
  }

  // Toolbar colors button.
  $.FE.DefineIcon('colors', { NAME: 'tint' });
  $.FE.RegisterCommand('color', {
    title: 'Colors',
    undo: false,
    focus: true,
    refreshOnCallback: false,
    popup: true,
    callback: function () {
      if (!this.popups.isVisible('colors.picker')) {
        this.colors.showColorsPopup();
      }
      else {
        if (this.$el.find('.fr-marker')) {
          this.events.disableBlur();
          this.selection.restore();
        }
        this.popups.hide('colors.picker');
      }
    },
    plugin: 'colors'
  });

  // Select text color command.
  $.FE.RegisterCommand('textColor', {
    undo: true,
    callback: function (cmd, val) {
      this.colors.text(val);
    }
  });

  // Select background color command.
  $.FE.RegisterCommand('backgroundColor', {
    undo: true,
    callback: function (cmd, val) {
      this.colors.background(val);
    }
  });

  $.FE.RegisterCommand('colorChangeSet', {
    undo: false,
    focus: false,
    callback: function (cmd, val) {
      var $tab = this.popups.get('colors.picker').find('.fr-command[data-cmd="' + cmd + '"][data-param1="' + val + '"]');
      this.colors.changeSet($tab, val);
    }
  });

  // Colors back.
  $.FE.DefineIcon('colorsBack', { NAME: 'arrow-left' });
  $.FE.RegisterCommand('colorsBack', {
    title: 'Back',
    undo: false,
    focus: false,
    back: true,
    refreshAfterCallback: false,
    callback: function () {
      this.colors.back();
    }
  });

  $.FE.DefineIcon('remove', { NAME: 'eraser' });

}));

};


modules['helpcenter/primary/views/search'] = function(require, exports, module, modules) {
module.exports = Backbone.View.extend({
    el: $('#search-filter-view'),

    events: {
        'change #search-filter': 'filter',
        'change #search-limit': 'filter'
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Gets options from router while instantiating view
     * @param  {Object}
     * @return {Object}
     */
    initialize: function (options) {
        this.options = options || {};

        $('#search-filter').val(this.options.filter);
		
		const urlSearchParams = new URLSearchParams(window.location.search);
        const searchlimit = urlSearchParams.get('searchlimit');
        $('#search-limit').val(searchlimit || 100);
        
        // Focus input field
        var field = $('.input--search');
        field.val(field.val()).focus();
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Redirects the search page to selected filter
     */
    filter: function() {
		const urlSearchParams = new URLSearchParams(window.location.search);
		const query = urlSearchParams.get('query') || "";
		window.location.href = _Payload.url + 'search/' + $('#search-filter').val() + '?searchlimit=' + $('#search-limit').val() + '&query=' + query.replace(/&page=\d/, '');
	}
});
};


modules['underscore'] = function(require, exports, module, modules) {
//     Underscore.js 1.7.0
//     http://underscorejs.org
//     (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
//     Underscore may be freely distributed under the MIT license.
(function(){var n=this,t=n._,r=Array.prototype,e=Object.prototype,u=Function.prototype,i=r.push,a=r.slice,o=r.concat,l=e.toString,c=e.hasOwnProperty,f=Array.isArray,s=Object.keys,p=u.bind,h=function(n){return n instanceof h?n:this instanceof h?void(this._wrapped=n):new h(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=h),exports._=h):n._=h,h.VERSION="1.7.0";var g=function(n,t,r){if(t===void 0)return n;switch(null==r?3:r){case 1:return function(r){return n.call(t,r)};case 2:return function(r,e){return n.call(t,r,e)};case 3:return function(r,e,u){return n.call(t,r,e,u)};case 4:return function(r,e,u,i){return n.call(t,r,e,u,i)}}return function(){return n.apply(t,arguments)}};h.iteratee=function(n,t,r){return null==n?h.identity:h.isFunction(n)?g(n,t,r):h.isObject(n)?h.matches(n):h.property(n)},h.each=h.forEach=function(n,t,r){if(null==n)return n;t=g(t,r);var e,u=n.length;if(u===+u)for(e=0;u>e;e++)t(n[e],e,n);else{var i=h.keys(n);for(e=0,u=i.length;u>e;e++)t(n[i[e]],i[e],n)}return n},h.map=h.collect=function(n,t,r){if(null==n)return[];t=h.iteratee(t,r);for(var e,u=n.length!==+n.length&&h.keys(n),i=(u||n).length,a=Array(i),o=0;i>o;o++)e=u?u[o]:o,a[o]=t(n[e],e,n);return a};var v="Reduce of empty array with no initial value";h.reduce=h.foldl=h.inject=function(n,t,r,e){null==n&&(n=[]),t=g(t,e,4);var u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length,o=0;if(arguments.length<3){if(!a)throw new TypeError(v);r=n[i?i[o++]:o++]}for(;a>o;o++)u=i?i[o]:o,r=t(r,n[u],u,n);return r},h.reduceRight=h.foldr=function(n,t,r,e){null==n&&(n=[]),t=g(t,e,4);var u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length;if(arguments.length<3){if(!a)throw new TypeError(v);r=n[i?i[--a]:--a]}for(;a--;)u=i?i[a]:a,r=t(r,n[u],u,n);return r},h.find=h.detect=function(n,t,r){var e;return t=h.iteratee(t,r),h.some(n,function(n,r,u){return t(n,r,u)?(e=n,!0):void 0}),e},h.filter=h.select=function(n,t,r){var e=[];return null==n?e:(t=h.iteratee(t,r),h.each(n,function(n,r,u){t(n,r,u)&&e.push(n)}),e)},h.reject=function(n,t,r){return h.filter(n,h.negate(h.iteratee(t)),r)},h.every=h.all=function(n,t,r){if(null==n)return!0;t=h.iteratee(t,r);var e,u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length;for(e=0;a>e;e++)if(u=i?i[e]:e,!t(n[u],u,n))return!1;return!0},h.some=h.any=function(n,t,r){if(null==n)return!1;t=h.iteratee(t,r);var e,u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length;for(e=0;a>e;e++)if(u=i?i[e]:e,t(n[u],u,n))return!0;return!1},h.contains=h.include=function(n,t){return null==n?!1:(n.length!==+n.length&&(n=h.values(n)),h.indexOf(n,t)>=0)},h.invoke=function(n,t){var r=a.call(arguments,2),e=h.isFunction(t);return h.map(n,function(n){return(e?t:n[t]).apply(n,r)})},h.pluck=function(n,t){return h.map(n,h.property(t))},h.where=function(n,t){return h.filter(n,h.matches(t))},h.findWhere=function(n,t){return h.find(n,h.matches(t))},h.max=function(n,t,r){var e,u,i=-1/0,a=-1/0;if(null==t&&null!=n){n=n.length===+n.length?n:h.values(n);for(var o=0,l=n.length;l>o;o++)e=n[o],e>i&&(i=e)}else t=h.iteratee(t,r),h.each(n,function(n,r,e){u=t(n,r,e),(u>a||u===-1/0&&i===-1/0)&&(i=n,a=u)});return i},h.min=function(n,t,r){var e,u,i=1/0,a=1/0;if(null==t&&null!=n){n=n.length===+n.length?n:h.values(n);for(var o=0,l=n.length;l>o;o++)e=n[o],i>e&&(i=e)}else t=h.iteratee(t,r),h.each(n,function(n,r,e){u=t(n,r,e),(a>u||1/0===u&&1/0===i)&&(i=n,a=u)});return i},h.shuffle=function(n){for(var t,r=n&&n.length===+n.length?n:h.values(n),e=r.length,u=Array(e),i=0;e>i;i++)t=h.random(0,i),t!==i&&(u[i]=u[t]),u[t]=r[i];return u},h.sample=function(n,t,r){return null==t||r?(n.length!==+n.length&&(n=h.values(n)),n[h.random(n.length-1)]):h.shuffle(n).slice(0,Math.max(0,t))},h.sortBy=function(n,t,r){return t=h.iteratee(t,r),h.pluck(h.map(n,function(n,r,e){return{value:n,index:r,criteria:t(n,r,e)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var m=function(n){return function(t,r,e){var u={};return r=h.iteratee(r,e),h.each(t,function(e,i){var a=r(e,i,t);n(u,e,a)}),u}};h.groupBy=m(function(n,t,r){h.has(n,r)?n[r].push(t):n[r]=[t]}),h.indexBy=m(function(n,t,r){n[r]=t}),h.countBy=m(function(n,t,r){h.has(n,r)?n[r]++:n[r]=1}),h.sortedIndex=function(n,t,r,e){r=h.iteratee(r,e,1);for(var u=r(t),i=0,a=n.length;a>i;){var o=i+a>>>1;r(n[o])<u?i=o+1:a=o}return i},h.toArray=function(n){return n?h.isArray(n)?a.call(n):n.length===+n.length?h.map(n,h.identity):h.values(n):[]},h.size=function(n){return null==n?0:n.length===+n.length?n.length:h.keys(n).length},h.partition=function(n,t,r){t=h.iteratee(t,r);var e=[],u=[];return h.each(n,function(n,r,i){(t(n,r,i)?e:u).push(n)}),[e,u]},h.first=h.head=h.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:0>t?[]:a.call(n,0,t)},h.initial=function(n,t,r){return a.call(n,0,Math.max(0,n.length-(null==t||r?1:t)))},h.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:a.call(n,Math.max(n.length-t,0))},h.rest=h.tail=h.drop=function(n,t,r){return a.call(n,null==t||r?1:t)},h.compact=function(n){return h.filter(n,h.identity)};var y=function(n,t,r,e){if(t&&h.every(n,h.isArray))return o.apply(e,n);for(var u=0,a=n.length;a>u;u++){var l=n[u];h.isArray(l)||h.isArguments(l)?t?i.apply(e,l):y(l,t,r,e):r||e.push(l)}return e};h.flatten=function(n,t){return y(n,t,!1,[])},h.without=function(n){return h.difference(n,a.call(arguments,1))},h.uniq=h.unique=function(n,t,r,e){if(null==n)return[];h.isBoolean(t)||(e=r,r=t,t=!1),null!=r&&(r=h.iteratee(r,e));for(var u=[],i=[],a=0,o=n.length;o>a;a++){var l=n[a];if(t)a&&i===l||u.push(l),i=l;else if(r){var c=r(l,a,n);h.indexOf(i,c)<0&&(i.push(c),u.push(l))}else h.indexOf(u,l)<0&&u.push(l)}return u},h.union=function(){return h.uniq(y(arguments,!0,!0,[]))},h.intersection=function(n){if(null==n)return[];for(var t=[],r=arguments.length,e=0,u=n.length;u>e;e++){var i=n[e];if(!h.contains(t,i)){for(var a=1;r>a&&h.contains(arguments[a],i);a++);a===r&&t.push(i)}}return t},h.difference=function(n){var t=y(a.call(arguments,1),!0,!0,[]);return h.filter(n,function(n){return!h.contains(t,n)})},h.zip=function(n){if(null==n)return[];for(var t=h.max(arguments,"length").length,r=Array(t),e=0;t>e;e++)r[e]=h.pluck(arguments,e);return r},h.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},h.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=h.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}for(;u>e;e++)if(n[e]===t)return e;return-1},h.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=n.length;for("number"==typeof r&&(e=0>r?e+r+1:Math.min(e,r+1));--e>=0;)if(n[e]===t)return e;return-1},h.range=function(n,t,r){arguments.length<=1&&(t=n||0,n=0),r=r||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=Array(e),i=0;e>i;i++,n+=r)u[i]=n;return u};var d=function(){};h.bind=function(n,t){var r,e;if(p&&n.bind===p)return p.apply(n,a.call(arguments,1));if(!h.isFunction(n))throw new TypeError("Bind must be called on a function");return r=a.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(a.call(arguments)));d.prototype=n.prototype;var u=new d;d.prototype=null;var i=n.apply(u,r.concat(a.call(arguments)));return h.isObject(i)?i:u}},h.partial=function(n){var t=a.call(arguments,1);return function(){for(var r=0,e=t.slice(),u=0,i=e.length;i>u;u++)e[u]===h&&(e[u]=arguments[r++]);for(;r<arguments.length;)e.push(arguments[r++]);return n.apply(this,e)}},h.bindAll=function(n){var t,r,e=arguments.length;if(1>=e)throw new Error("bindAll must be passed function names");for(t=1;e>t;t++)r=arguments[t],n[r]=h.bind(n[r],n);return n},h.memoize=function(n,t){var r=function(e){var u=r.cache,i=t?t.apply(this,arguments):e;return h.has(u,i)||(u[i]=n.apply(this,arguments)),u[i]};return r.cache={},r},h.delay=function(n,t){var r=a.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},h.defer=function(n){return h.delay.apply(h,[n,1].concat(a.call(arguments,1)))},h.throttle=function(n,t,r){var e,u,i,a=null,o=0;r||(r={});var l=function(){o=r.leading===!1?0:h.now(),a=null,i=n.apply(e,u),a||(e=u=null)};return function(){var c=h.now();o||r.leading!==!1||(o=c);var f=t-(c-o);return e=this,u=arguments,0>=f||f>t?(clearTimeout(a),a=null,o=c,i=n.apply(e,u),a||(e=u=null)):a||r.trailing===!1||(a=setTimeout(l,f)),i}},h.debounce=function(n,t,r){var e,u,i,a,o,l=function(){var c=h.now()-a;t>c&&c>0?e=setTimeout(l,t-c):(e=null,r||(o=n.apply(i,u),e||(i=u=null)))};return function(){i=this,u=arguments,a=h.now();var c=r&&!e;return e||(e=setTimeout(l,t)),c&&(o=n.apply(i,u),i=u=null),o}},h.wrap=function(n,t){return h.partial(t,n)},h.negate=function(n){return function(){return!n.apply(this,arguments)}},h.compose=function(){var n=arguments,t=n.length-1;return function(){for(var r=t,e=n[t].apply(this,arguments);r--;)e=n[r].call(this,e);return e}},h.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},h.before=function(n,t){var r;return function(){return--n>0?r=t.apply(this,arguments):t=null,r}},h.once=h.partial(h.before,2),h.keys=function(n){if(!h.isObject(n))return[];if(s)return s(n);var t=[];for(var r in n)h.has(n,r)&&t.push(r);return t},h.values=function(n){for(var t=h.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},h.pairs=function(n){for(var t=h.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},h.invert=function(n){for(var t={},r=h.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},h.functions=h.methods=function(n){var t=[];for(var r in n)h.isFunction(n[r])&&t.push(r);return t.sort()},h.extend=function(n){if(!h.isObject(n))return n;for(var t,r,e=1,u=arguments.length;u>e;e++){t=arguments[e];for(r in t)c.call(t,r)&&(n[r]=t[r])}return n},h.pick=function(n,t,r){var e,u={};if(null==n)return u;if(h.isFunction(t)){t=g(t,r);for(e in n){var i=n[e];t(i,e,n)&&(u[e]=i)}}else{var l=o.apply([],a.call(arguments,1));n=new Object(n);for(var c=0,f=l.length;f>c;c++)e=l[c],e in n&&(u[e]=n[e])}return u},h.omit=function(n,t,r){if(h.isFunction(t))t=h.negate(t);else{var e=h.map(o.apply([],a.call(arguments,1)),String);t=function(n,t){return!h.contains(e,t)}}return h.pick(n,t,r)},h.defaults=function(n){if(!h.isObject(n))return n;for(var t=1,r=arguments.length;r>t;t++){var e=arguments[t];for(var u in e)n[u]===void 0&&(n[u]=e[u])}return n},h.clone=function(n){return h.isObject(n)?h.isArray(n)?n.slice():h.extend({},n):n},h.tap=function(n,t){return t(n),n};var b=function(n,t,r,e){if(n===t)return 0!==n||1/n===1/t;if(null==n||null==t)return n===t;n instanceof h&&(n=n._wrapped),t instanceof h&&(t=t._wrapped);var u=l.call(n);if(u!==l.call(t))return!1;switch(u){case"[object RegExp]":case"[object String]":return""+n==""+t;case"[object Number]":return+n!==+n?+t!==+t:0===+n?1/+n===1/t:+n===+t;case"[object Date]":case"[object Boolean]":return+n===+t}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]===n)return e[i]===t;var a=n.constructor,o=t.constructor;if(a!==o&&"constructor"in n&&"constructor"in t&&!(h.isFunction(a)&&a instanceof a&&h.isFunction(o)&&o instanceof o))return!1;r.push(n),e.push(t);var c,f;if("[object Array]"===u){if(c=n.length,f=c===t.length)for(;c--&&(f=b(n[c],t[c],r,e)););}else{var s,p=h.keys(n);if(c=p.length,f=h.keys(t).length===c)for(;c--&&(s=p[c],f=h.has(t,s)&&b(n[s],t[s],r,e)););}return r.pop(),e.pop(),f};h.isEqual=function(n,t){return b(n,t,[],[])},h.isEmpty=function(n){if(null==n)return!0;if(h.isArray(n)||h.isString(n)||h.isArguments(n))return 0===n.length;for(var t in n)if(h.has(n,t))return!1;return!0},h.isElement=function(n){return!(!n||1!==n.nodeType)},h.isArray=f||function(n){return"[object Array]"===l.call(n)},h.isObject=function(n){var t=typeof n;return"function"===t||"object"===t&&!!n},h.each(["Arguments","Function","String","Number","Date","RegExp"],function(n){h["is"+n]=function(t){return l.call(t)==="[object "+n+"]"}}),h.isArguments(arguments)||(h.isArguments=function(n){return h.has(n,"callee")}),"function"!=typeof/./&&(h.isFunction=function(n){return"function"==typeof n||!1}),h.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},h.isNaN=function(n){return h.isNumber(n)&&n!==+n},h.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"===l.call(n)},h.isNull=function(n){return null===n},h.isUndefined=function(n){return n===void 0},h.has=function(n,t){return null!=n&&c.call(n,t)},h.noConflict=function(){return n._=t,this},h.identity=function(n){return n},h.constant=function(n){return function(){return n}},h.noop=function(){},h.property=function(n){return function(t){return t[n]}},h.matches=function(n){var t=h.pairs(n),r=t.length;return function(n){if(null==n)return!r;n=new Object(n);for(var e=0;r>e;e++){var u=t[e],i=u[0];if(u[1]!==n[i]||!(i in n))return!1}return!0}},h.times=function(n,t,r){var e=Array(Math.max(0,n));t=g(t,r,1);for(var u=0;n>u;u++)e[u]=t(u);return e},h.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},h.now=Date.now||function(){return(new Date).getTime()};var _={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","`":"&#x60;"},w=h.invert(_),j=function(n){var t=function(t){return n[t]},r="(?:"+h.keys(n).join("|")+")",e=RegExp(r),u=RegExp(r,"g");return function(n){return n=null==n?"":""+n,e.test(n)?n.replace(u,t):n}};h.escape=j(_),h.unescape=j(w),h.result=function(n,t){if(null==n)return void 0;var r=n[t];return h.isFunction(r)?n[t]():r};var x=0;h.uniqueId=function(n){var t=++x+"";return n?n+t:t},h.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var A=/(.)^/,k={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},O=/\\|'|\r|\n|\u2028|\u2029/g,F=function(n){return"\\"+k[n]};h.template=function(n,t,r){!t&&r&&(t=r),t=h.defaults({},t,h.templateSettings);var e=RegExp([(t.escape||A).source,(t.interpolate||A).source,(t.evaluate||A).source].join("|")+"|$","g"),u=0,i="__p+='";n.replace(e,function(t,r,e,a,o){return i+=n.slice(u,o).replace(O,F),u=o+t.length,r?i+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'":e?i+="'+\n((__t=("+e+"))==null?'':__t)+\n'":a&&(i+="';\n"+a+"\n__p+='"),t}),i+="';\n",t.variable||(i="with(obj||{}){\n"+i+"}\n"),i="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+i+"return __p;\n";try{var a=new Function(t.variable||"obj","_",i)}catch(o){throw o.source=i,o}var l=function(n){return a.call(this,n,h)},c=t.variable||"obj";return l.source="function("+c+"){\n"+i+"}",l},h.chain=function(n){var t=h(n);return t._chain=!0,t};var E=function(n){return this._chain?h(n).chain():n};h.mixin=function(n){h.each(h.functions(n),function(t){var r=h[t]=n[t];h.prototype[t]=function(){var n=[this._wrapped];return i.apply(n,arguments),E.call(this,r.apply(h,n))}})},h.mixin(h),h.each(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=r[n];h.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!==n&&"splice"!==n||0!==r.length||delete r[0],E.call(this,r)}}),h.each(["concat","join","slice"],function(n){var t=r[n];h.prototype[n]=function(){return E.call(this,t.apply(this._wrapped,arguments))}}),h.prototype.value=function(){return this._wrapped},"function"==typeof define&&define.amd&&define("underscore",[],function(){return h})}).call(this);
// sourceMappingURL=underscore-min.map

};


modules['helpcenter/primary/plugins/froala/image'] = function(require, exports, module, modules) {
/*!
 * froala_editor v2.3.4 (https://www.froala.com/wysiwyg-editor)
 * License https://froala.com/wysiwyg-editor/terms/
 * Copyright 2014-2016 Froala Labs
 */

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof module === 'object' && module.exports) {
        // Node/CommonJS
        module.exports = function( root, jQuery ) {
            if ( jQuery === undefined ) {
                // require('jQuery') returns a factory that requires window to
                // build a jQuery instance, we normalize how we use modules
                // that require this pattern but the window provided is a noop
                // if it's defined (how jquery works)
                if ( typeof window !== 'undefined' ) {
                    jQuery = require('jquery');
                }
                else {
                    jQuery = require('jquery')(root);
                }
            }
            factory(jQuery);
            return jQuery;
        };
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {

  'use strict';

  $.extend($.FE.POPUP_TEMPLATES, {
    'image.insert': '[_BUTTONS_][_UPLOAD_LAYER_][_BY_URL_LAYER_][_PROGRESS_BAR_]',
    'image.edit': '[_BUTTONS_]',
    'image.alt': '[_BUTTONS_][_ALT_LAYER_]',
    'image.size': '[_BUTTONS_][_SIZE_LAYER_]'
  })

  $.extend($.FE.DEFAULTS, {
    imageInsertButtons: ['imageBack', '|', 'imageUpload', 'imageByURL'],
    imageEditButtons: ['imageReplace', 'imageAlign', 'imageRemove', '|', 'imageLink', 'linkOpen', 'linkEdit', 'linkRemove', '-', 'imageDisplay', 'imageStyle', 'imageAlt', 'imageSize'],
    imageAltButtons: ['imageBack', '|'],
    imageSizeButtons: ['imageBack', '|'],
    imageUploadURL: 'https://i.froala.com/upload',
    imageUploadParam: 'file',
    imageUploadParams: {},
    imageUploadToS3: false,
    imageUploadMethod: 'POST',
    imageMaxSize: 10 * 1024 * 1024,
    imageAllowedTypes: ['jpeg', 'jpg', 'png', 'gif', 'svg+xml'],
    imageResize: true,
    imageResizeWithPercent: false,
    imageRoundPercent: false,
    imageDefaultWidth: 300,
    imageDefaultAlign: 'center',
    imageDefaultDisplay: 'block',
    imageSplitHTML: false,
    imageStyles: {
      'fr-rounded': 'Rounded',
      'fr-bordered': 'Bordered'
    },
    imageMove: true,
    imageMultipleStyles: true,
    imageTextNear: true,
    imagePaste: true,
    imagePasteProcess: false,
    imageMinWidth: 16,
    imageOutputSize: false
  });

  $.FE.PLUGINS.image = function (editor) {
    var $current_image;
    var $image_resizer;
    var $handler;
    var $overlay;
    var mousedown = false;

    var BAD_LINK = 1;
    var MISSING_LINK = 2;
    var ERROR_DURING_UPLOAD = 3;
    var BAD_RESPONSE = 4;
    var MAX_SIZE_EXCEEDED = 5;
    var BAD_FILE_TYPE = 6;
    var NO_CORS_IE = 7;

    var error_messages = {};
    error_messages[BAD_LINK] = 'Image cannot be loaded from the passed link.',
    error_messages[MISSING_LINK] = 'No link in upload response.',
    error_messages[ERROR_DURING_UPLOAD] = 'Error during file upload.',
    error_messages[BAD_RESPONSE] = 'Parsing response failed.',
    error_messages[MAX_SIZE_EXCEEDED] = 'File is too large.',
    error_messages[BAD_FILE_TYPE] = 'Image file type is invalid.',
    error_messages[NO_CORS_IE] = 'Files can be uploaded only to same domain in IE 8 and IE 9.'

    /**
     * Refresh the image insert popup.
     */

    function _refreshInsertPopup () {
      var $popup = editor.popups.get('image.insert');

      var $url_input = $popup.find('.fr-image-by-url-layer input');
      $url_input.val('');

      if ($current_image) {
        $url_input.val($current_image.attr('src'));
      }

      $url_input.trigger('change');
    }

    /**
     * Show the image upload popup.
     */

    function showInsertPopup () {
      var $btn = editor.$tb.find('.fr-command[data-cmd="insertImage"]');

      var $popup = editor.popups.get('image.insert');
      if (!$popup) $popup = _initInsertPopup();

      hideProgressBar();
      if (!$popup.hasClass('fr-active')) {
        editor.popups.refresh('image.insert');
        editor.popups.setContainer('image.insert', editor.$tb);

        if ($btn.is(':visible')) {
          var left = $btn.offset().left + $btn.outerWidth() / 2;
          var top = $btn.offset().top + (editor.opts.toolbarBottom ? 10 : $btn.outerHeight() - 10);
          editor.popups.show('image.insert', left, top, $btn.outerHeight());
        } else {
          editor.position.forSelection($popup);
          editor.popups.show('image.insert');
        }
      }
    }

    /**
     * Show the image edit popup.
     */

    function _showEditPopup () {
      var $popup = editor.popups.get('image.edit');
      if (!$popup) $popup = _initEditPopup();

      editor.popups.setContainer('image.edit', $(editor.opts.scrollableContainer));
      editor.popups.refresh('image.edit');
      var left = $current_image.offset().left + $current_image.outerWidth() / 2;
      var top = $current_image.offset().top + $current_image.outerHeight();

      editor.popups.show('image.edit', left, top, $current_image.outerHeight());
    }

    /**
     * Hide image upload popup.
     */

    function _hideInsertPopup () {
      hideProgressBar();
    }

    /**
     * Convert style to classes.
     */

    function _convertStyleToClasses ($img) {
      if (!$img.hasClass('fr-dii') && !$img.hasClass('fr-dib')) {
        // Set float to none.
        var flt = $img.css('float');
        $img.css('float', 'none');

        // Image has display block.
        if ($img.css('display') == 'block') {
          // Set float to the initial value.
          $img.css('float', flt);

          if (editor.opts.imageEditButtons.indexOf('imageAlign') >= 0) {
            // Margin left is 0.
            // Margin right is auto.
            if (parseInt($img.css('margin-left'), 10) === 0 && ($img.attr('style') || '').indexOf('margin-right: auto') >= 0) {
              $img.addClass('fr-fil');
            }

            // Margin left is auto.
            // Margin right is 0.
            else if (parseInt($img.css('margin-right'), 10) === 0 && ($img.attr('style') || '').indexOf('margin-left: auto') >= 0) {
              $img.addClass('fr-fir');
            }
          }

          $img.addClass('fr-dib');
        }

        // Display inline.
        else {
          // Set float.
          $img.css('float', flt);

          if (editor.opts.imageEditButtons.indexOf('imageAlign') >= 0) {
            // Float left.
            if ($img.css('float') == 'left') {
              $img.addClass('fr-fil');
            }

            // Float right.
            else if ($img.css('float') == 'right') {
              $img.addClass('fr-fir');
            }
          }

          $img.addClass('fr-dii');
        }

        // Reset inline style.
        $img.css('margin', '');
        $img.css('float', '');
        $img.css('display', '');
        $img.css('z-index', '');
        $img.css('position', '');
        $img.css('overflow', '');
        $img.css('vertical-align', '');
      }
    }

    /**
     * Refresh the image list.
     */

    function _refreshImageList () {
      var images = editor.$el.get(0).tagName == 'IMG' ? [editor.$el.get(0)] : editor.$el.get(0).querySelectorAll('img');

      for (var i = 0; i < images.length; i++) {
        var $img = $(images[i]);

        if (editor.opts.imageEditButtons.indexOf('imageAlign') >= 0 || editor.opts.imageEditButtons.indexOf('imageDisplay') >= 0) {
          _convertStyleToClasses($img);
        }

        // Set width if it has width.
        if ($img.attr('width')) {
          $img.css('width', $img.width());
          $img.removeAttr('width');
        }

        // Do not allow text near image.
        if (!editor.opts.imageTextNear) {
          $img.removeClass('fr-dii').addClass('fr-dib');
        }

        if (editor.opts.iframe) {
          $img.on('load', editor.size.syncIframe);
        }
      }
    }

    /**
     * Keep images in sync when content changed.
     */
    var images;

    function _syncImages () {
      // Get current images.
      var c_images = Array.prototype.slice.call(editor.$el.get(0).querySelectorAll('img'));

      // Current images src.
      var image_srcs = [];
      var i;
      for (i = 0; i < c_images.length; i++) {
        image_srcs.push(c_images[i].getAttribute('src'));

        $(c_images[i]).toggleClass('fr-draggable', editor.opts.imageMove);
        if (c_images[i].className === '') c_images[i].removeAttribute('class');
        if (c_images[i].getAttribute('style') === '') c_images[i].removeAttribute('style');
      }

      // Loop previous images and check their src.
      if (images) {
        for (i = 0; i < images.length; i++) {
          if (image_srcs.indexOf(images[i].getAttribute('src')) < 0) {
            editor.events.trigger('image.removed', [$(images[i])]);
          }
        }
      }

      // Current images are the old ones.
      images = c_images;
    }

    /**
     * Reposition resizer.
     */

    function _repositionResizer () {
      if (!$image_resizer) _initImageResizer();

      var $container = editor.$wp || $(editor.opts.scrollableContainer);

      $container.append($image_resizer);
      $image_resizer.data('instance', editor);

      var wrap_correction_top = $container.scrollTop() - (($container.css('position') != 'static' ? $container.offset().top : 0));
      var wrap_correction_left = $container.scrollLeft() - (($container.css('position') != 'static' ? $container.offset().left : 0));

      wrap_correction_left -= editor.helpers.getPX($container.css('border-left-width'));
      wrap_correction_top -= editor.helpers.getPX($container.css('border-top-width'));

      $image_resizer
        .css('top', (editor.opts.iframe ? $current_image.offset().top : $current_image.offset().top + wrap_correction_top) - 1)
        .css('left', (editor.opts.iframe ? $current_image.offset().left : $current_image.offset().left + wrap_correction_left) - 1)
        .css('width', $current_image.get(0).getBoundingClientRect().width)
        .css('height', $current_image.get(0).getBoundingClientRect().height)
        .addClass('fr-active')
    }

    /**
     * Create resize handler.
     */

    function _getHandler (pos) {
      return '<div class="fr-handler fr-h' + pos + '"></div>';
    }

    /**
     * Mouse down to start resize.
     */
    function _handlerMousedown (e) {
      // Check if resizer belongs to current instance.
      if (!editor.core.sameInstance($image_resizer)) return true;

      e.preventDefault();
      e.stopPropagation();

      if (editor.$el.find('img.fr-error').left) return false;

      if (!editor.undo.canDo()) editor.undo.saveStep();

      $handler = $(this);
      $handler.data('start-x', e.pageX || e.originalEvent.touches[0].pageX);
      $handler.data('start-width', $current_image.width());
      $handler.data('start-height', $current_image.height());

      // Set current width.
      var width = $current_image.width();
      if (editor.opts.imageResizeWithPercent) {
        var p_node = $current_image.parentsUntil(editor.$el, editor.html.blockTagsQuery()).get(0) || editor.$el.get(0);

        $current_image.css('width', (width / $(p_node).outerWidth() * 100).toFixed(2) + '%');
      } else {
        $current_image.css('width', width);
      }

      $overlay.show();

      editor.popups.hideAll();

      _unmarkExit();
    }

    /**
     * Do resize.
     */

    function _handlerMousemove (e) {
      // Check if resizer belongs to current instance.
      if (!editor.core.sameInstance($image_resizer)) return true;

      if ($handler && $current_image) {
        e.preventDefault()

        if (editor.$el.find('img.fr-error').left) return false;

        var c_x = e.pageX || (e.originalEvent.touches ? e.originalEvent.touches[0].pageX : null);

        if (!c_x) {
          return false;
        }

        var s_x = $handler.data('start-x');

        var diff_x = c_x - s_x;

        var width = $handler.data('start-width');
        if ($handler.hasClass('fr-hnw') || $handler.hasClass('fr-hsw')) {
          diff_x = 0 - diff_x;
        }

        if (editor.opts.imageResizeWithPercent) {
          var p_node = $current_image.parentsUntil(editor.$el, editor.html.blockTagsQuery()).get(0) || editor.$el.get(0);

          width = ((width + diff_x) / $(p_node).outerWidth() * 100).toFixed(2);
          if (editor.opts.imageRoundPercent) width = Math.round(width);

          $current_image.css('width', width + '%');
          $current_image.css('height', '').removeAttr('height');
        } else {
          if (width + diff_x >= editor.opts.imageMinWidth) {
            $current_image.css('width', width + diff_x);
          }

          $current_image.css('height', $handler.data('start-height') * $current_image.width() / $handler.data('start-width'));
        }

        _repositionResizer();

        editor.events.trigger('image.resize', [get()]);
      }
    }

    /**
     * Stop resize.
     */

    function _handlerMouseup (e) {
      // Check if resizer belongs to current instance.
      if (!editor.core.sameInstance($image_resizer)) return true;

      if ($handler && $current_image) {
        if (e) e.stopPropagation();

        if (editor.$el.find('img.fr-error').left) return false;

        $handler = null;
        $overlay.hide();
        _repositionResizer();
        _showEditPopup();

        editor.undo.saveStep();

        editor.events.trigger('image.resizeEnd', [get()]);
      }
    }

    /**
     * Throw an image error.
     */

    function _throwError (code, response) {
      editor.edit.on();
      if ($current_image) $current_image.addClass('fr-error');
      _showErrorMessage(editor.language.translate('Something went wrong. Please try again.'));

      editor.events.trigger('image.error', [{
          code: code,
          message: error_messages[code]
        },
        response
      ]);
    }

    /**
     * Init the image edit popup.
     */

    function _initEditPopup (delayed) {
      if (delayed) {
        if (editor.$wp) {
          editor.events.$on(editor.$wp, 'scroll', function () {
            if ($current_image && editor.popups.isVisible('image.edit')) {
              _showEditPopup();
            }
          });
        }

        return true;
      }

      // Image buttons.
      var image_buttons = '';
      if (editor.opts.imageEditButtons.length > 0) {
        image_buttons += '<div class="fr-buttons">';
        image_buttons += editor.button.buildList(editor.opts.imageEditButtons);
        image_buttons += '</div>';
      }

      var template = {
        buttons: image_buttons
      };

      var $popup = editor.popups.create('image.edit', template);

      return $popup;
    }

    /**
     * Show progress bar.
     */

    function showProgressBar (no_message) {
      var $popup = editor.popups.get('image.insert');
      if (!$popup) $popup = _initInsertPopup();

      $popup.find('.fr-layer.fr-active').removeClass('fr-active').addClass('fr-pactive');
      $popup.find('.fr-image-progress-bar-layer').addClass('fr-active');
      $popup.find('.fr-buttons').hide();

      if ($current_image) {
        editor.popups.setContainer('image.insert', $(editor.opts.scrollableContainer));
        var left = $current_image.offset().left + $current_image.width() / 2;
        var top = $current_image.offset().top + $current_image.height();

        editor.popups.show('image.insert', left, top, $current_image.outerHeight());
      }

      if (typeof no_message == 'undefined') {
        _setProgressMessage('Uploading', 0);
      }
    }

    /**
     * Hide progress bar.
     */
    function hideProgressBar (dismiss) {
      var $popup = editor.popups.get('image.insert');

      if ($popup) {
        $popup.find('.fr-layer.fr-pactive').addClass('fr-active').removeClass('fr-pactive');
        $popup.find('.fr-image-progress-bar-layer').removeClass('fr-active');
        $popup.find('.fr-buttons').show();

        // Dismiss error message.
        if (dismiss || editor.$el.find('img.fr-error').length) {
          editor.events.focus();
          editor.$el.find('img.fr-error').remove();
          editor.undo.saveStep();
          editor.undo.run();
          editor.undo.dropRedo();
        }
      }
    }

    /**
     * Set a progress message.
     */

    function _setProgressMessage (message, progress) {
      var $popup = editor.popups.get('image.insert');

      if ($popup) {
        var $layer = $popup.find('.fr-image-progress-bar-layer');
        $layer.find('h3').text(message + (progress ? ' ' + progress + '%' : ''));

        $layer.removeClass('fr-error');

        if (progress) {
          $layer.find('div').removeClass('fr-indeterminate');
          $layer.find('div > span').css('width', progress + '%');
        } else {
          $layer.find('div').addClass('fr-indeterminate');
        }
      }
    }

    /**
     * Show error message to the user.
     */

    function _showErrorMessage (message) {
      showProgressBar();
      var $popup = editor.popups.get('image.insert');
      var $layer = $popup.find('.fr-image-progress-bar-layer');
      $layer.addClass('fr-error')
      $layer.find('h3').text(message);
    }

    /**
     * Insert image using URL callback.
     */

    function insertByURL () {
      var $popup = editor.popups.get('image.insert');
      var $input = $popup.find('.fr-image-by-url-layer input');

      if ($input.val().length > 0) {
        showProgressBar();
        _setProgressMessage('Loading image');
        insert($input.val(), true, [], $current_image);
        $input.val('');
        $input.blur();
      }
    }

    function _editImg ($img) {
      _edit.call($img.get(0));
    }

    function _loadedCallback () {
      var $img = $(this);

      editor.popups.hide('image.insert');

      $img.removeClass('fr-uploading');

      // Select the image.
      if ($img.next().is('br')) {
        $img.next().remove();
      }

      _editImg($img);

      editor.events.trigger('image.loaded', [$img]);
    }

    /**
     * Insert image into the editor.
     */

    function insert (link, sanitize, data, $existing_img, response) {
      editor.edit.off();
      _setProgressMessage('Loading image');

      if (sanitize) link = editor.helpers.sanitizeURL(link);

      var image = new Image();
      image.onload = function () {
        var $img;
        var attr;

        if ($existing_img) {
          var old_src = $existing_img.data('fr-old-src');

          if (editor.$wp) {
            // Clone existing image.
            $img = $existing_img.clone().removeData('fr-old-src').removeClass('fr-uploading');

            // Remove load event.
            $img.off('load');

            // Set new SRC.
            if (old_src) $existing_img.attr('src', old_src);

            // Replace existing image with its clone.
            $existing_img.replaceWith($img);
          } else {
            $img = $existing_img;
          }

          // Remove old data.
          var atts = $img.get(0).attributes;
          for (var i = 0; i < atts.length; i++) {
            var att = atts[i];
            if (att.nodeName.indexOf('data-') === 0) {
              $img.removeAttr(att.nodeName);
            }
          }

          // Set new data.
          if (typeof data != 'undefined') {
            for (attr in data) {
              if (data.hasOwnProperty(attr)) {
                if (attr != 'link') {
                  $img.attr('data-' + attr, data[attr]);
                }
              }
            }
          }

          $img.on('load', _loadedCallback);
          $img.attr('src', link);
          editor.edit.on();
          _syncImages();
          editor.undo.saveStep();
          editor.events.trigger(old_src ? 'image.replaced' : 'image.inserted', [$img, response]);
        } else {
          $img = _addImage(link, data, _loadedCallback);
          _syncImages();
          editor.undo.saveStep();
          editor.events.trigger('image.inserted', [$img, response]);
        }
      }

      image.onerror = function () {
        _throwError(BAD_LINK);
      }

      image.src = link;
    }

    /**
     * Parse image response.
     */

    function _parseResponse (response) {
      try {
        if (editor.events.trigger('image.uploaded', [response], true) === false) {
          editor.edit.on();
          return false;
        }

        var resp = $.parseJSON(response);
        if (resp.data) {
          return resp;
        } else {
          // No link in upload request.
          _throwError(MISSING_LINK, response);
          return false;
        }
      } catch (ex) {
        // Bad response.
        _throwError(BAD_RESPONSE, response);
        return false;
      }
    }

    /**
     * Parse image response.
     */

    function _parseXMLResponse (response) {
      try {
        var link = $(response).find('Location').text();
        var key = $(response).find('Key').text();

        if (editor.events.trigger('image.uploadedToS3', [link, key, response], true) === false) {
          editor.edit.on();
          return false;
        }

        return link;
      } catch (ex) {
        // Bad response.
        _throwError(BAD_RESPONSE, response);
        return false;
      }
    }

    /**
     * Image was uploaded to the server and we have a response.
     */

    function _imageUploaded ($img) {
      _setProgressMessage('Loading image');

      var status = this.status;
      var response = this.response;
      var responseXML = this.responseXML;
      var responseText = this.responseText;

      try {
        if (editor.opts.imageUploadToS3) {
          if (status == 201) {
            var link = _parseXMLResponse(responseXML);
            if (link) {
              insert(link, false, [], $img, response || responseXML);
            }
          } else {
            _throwError(BAD_RESPONSE, response || responseXML);
          }
        } else {
          if (status >= 200 && status < 300) {
            var resp = _parseResponse(responseText);
            if (resp) {
              // @todo Temporarily modifying URL due to API issue
              var falseUrl = resp.data[0].attachment.url;
              var correctUrl = falseUrl.replace('/media/','/base/media/');
              insert(correctUrl, false, resp, $img, response || responseText);
            }
          } else {
            _throwError(ERROR_DURING_UPLOAD, response || responseText);
          }
        }
      } catch (ex) {
        // Bad response.
        _throwError(BAD_RESPONSE, response || responseText);
      }
    }

    /**
     * Image upload error.
     */

    function _imageUploadError () {
      _throwError(BAD_RESPONSE, this.response || this.responseText || this.responseXML);
    }

    /**
     * Image upload progress.
     */

    function _imageUploadProgress (e) {
      if (e.lengthComputable) {
        var complete = (e.loaded / e.total * 100 | 0);
        _setProgressMessage('Uploading', complete);
      }
    }

    function _addImage (link, data, loadCallback) {
      // Build image data string.
      var data_str = '';
      var attr;
      if (data && typeof data != 'undefined') {
        for (attr in data) {
          if (data.hasOwnProperty(attr)) {
            if (attr != 'link') {
              data_str += ' data-' + attr + '="' + data[attr] + '"';
            }
          }
        }
      }

      var width = editor.opts.imageDefaultWidth;
      if (width && width != 'auto') {
        width = width + (editor.opts.imageResizeWithPercent ? '%' : 'px');
      }

      // Create image object and set the load event.
      var $img = $('<img class="' + (editor.opts.imageDefaultDisplay ? 'fr-di' + editor.opts.imageDefaultDisplay[0] : '') + (editor.opts.imageDefaultAlign ? (editor.opts.imageDefaultAlign != 'center' ? ' fr-fi' + editor.opts.imageDefaultAlign[0] : '') : '') + '" src="' + link + '"' + data_str + (width ? ' style="width: ' + width + ';"' : '') + '>');

      $img.on('load', loadCallback);

      // Make sure we have focus.
      // Call the event.
      editor.edit.on();
      editor.events.focus(true);
      editor.selection.restore();

      editor.undo.saveStep();

      // Insert marker and then replace it with the image.
      if (editor.opts.imageSplitHTML) {
        editor.markers.split();
      } else {
        editor.markers.insert();
      }

      var $marker = editor.$el.find('.fr-marker');
      $marker.replaceWith($img);

      editor.html.wrap();
      editor.selection.clear();

      return $img;
    }

    /**
     * Image upload aborted.
     */
    function _imageUploadAborted () {
      editor.edit.on();
      hideProgressBar(true);
    }

    /**
     * Start the uploading process.
     */
    function _startUpload (xhr, form_data, image) {
      function _sendRequest () {
        var $img = $(this);

        $img.off('load');

        $img.addClass('fr-uploading');

        if ($img.next().is('br')) {
          $img.next().remove();
        }

        editor.placeholder.refresh();

        // Select the image.
        if (!$img.is($current_image)) _editImg($img);

        _repositionResizer();
        showProgressBar();

        editor.edit.off();

        // Set upload events.
        xhr.onload = function () {
          _imageUploaded.call(xhr, $img);
        };
        xhr.onerror = _imageUploadError;
        xhr.upload.onprogress = _imageUploadProgress;
        xhr.onabort = _imageUploadAborted;

        // Set abort event.
        $img.off('abortUpload').on('abortUpload', function () {
          if (xhr.readyState != 4) {
            xhr.abort();
          }
        });

        // Send data.
        xhr.send(form_data);
      }

      var reader = new FileReader();
      var $img;
      reader.addEventListener('load', function () {
        var link = reader.result;

        if (reader.result.indexOf('svg+xml') < 0) {
          // Convert image to local blob.
          var binary = atob(reader.result.split(',')[1]);
          var array = [];
          for (var i = 0; i < binary.length; i++) {
            array.push(binary.charCodeAt(i));
          }

          // Get local image link.
          link = window.URL.createObjectURL(new Blob([new Uint8Array(array)], {
            type: 'image/jpeg'
          }));
        }

        // No image.
        if (!$current_image) {
          $img = _addImage(link, null, _sendRequest);
        } else {
          $current_image.on('load', _sendRequest);
          editor.edit.on();
          editor.undo.saveStep();
          $current_image.data('fr-old-src', $current_image.attr('src'));
          $current_image.attr('src', link);
        }
      }, false);

      reader.readAsDataURL(image);
    }

    /**
     * Do image upload.
     */

    function upload (images) {
      // Check if we should cancel the image upload.
      if (editor.events.trigger('image.beforeUpload', [images]) === false) {
        return false;
      }

      // Make sure we have what to upload.
      if (typeof images != 'undefined' && images.length > 0) {
        var image = images[0];

        // Check image max size.
        if (image.size > editor.opts.imageMaxSize) {
          _throwError(MAX_SIZE_EXCEEDED);
          return false;
        }

        // Check image types.
        if (editor.opts.imageAllowedTypes.indexOf(image.type.replace(/image\//g, '')) < 0) {
          _throwError(BAD_FILE_TYPE);
          return false;
        }

        // Create form Data.
        var form_data;
        if (editor.drag_support.formdata) {
          form_data = editor.drag_support.formdata ? new FormData() : null;
        }

        // Prepare form data for request.
        if (form_data) {
          var key;

          // Upload to S3.
          if (editor.opts.imageUploadToS3 !== false) {
            form_data.append('key', editor.opts.imageUploadToS3.keyStart + (new Date()).getTime() + '-' + (image.name || 'untitled'));
            form_data.append('success_action_status', '201');
            form_data.append('X-Requested-With', 'xhr');
            form_data.append('Content-Type', image.type);

            for (key in editor.opts.imageUploadToS3.params) {
              if (editor.opts.imageUploadToS3.params.hasOwnProperty(key)) {
                form_data.append(key, editor.opts.imageUploadToS3.params[key]);
              }
            }
          }

          // Add upload params.
          for (key in editor.opts.imageUploadParams) {
            if (editor.opts.imageUploadParams.hasOwnProperty(key)) {
              form_data.append(key, editor.opts.imageUploadParams[key]);
            }
          }

          // Set the image in the request.
          form_data.append(editor.opts.imageUploadParam, image);

          // Create XHR request.
          var url = editor.opts.imageUploadURL;
          if (editor.opts.imageUploadToS3) {
            url = 'https://' + editor.opts.imageUploadToS3.region + '.amazonaws.com/' + editor.opts.imageUploadToS3.bucket;
          }
          var xhr = editor.core.getXHR(url, editor.opts.imageUploadMethod);

          _startUpload(xhr, form_data, image);
        }
      }
    }

    /**
     * Image drop inside the upload zone.
     */

    function _bindInsertEvents ($popup) {
      // Drag over the dropable area.
      editor.events.$on($popup, 'dragover dragenter', '.fr-image-upload-layer', function () {
        $(this).addClass('fr-drop');
        return false;
      });

      // Drag end.
      editor.events.$on($popup, 'dragleave dragend', '.fr-image-upload-layer', function () {
        $(this).removeClass('fr-drop');
        return false;
      });

      // Drop.
      editor.events.$on($popup, 'drop', '.fr-image-upload-layer', function (e) {
        e.preventDefault();
        e.stopPropagation();

        $(this).removeClass('fr-drop');

        var dt = e.originalEvent.dataTransfer;
        if (dt && dt.files) {
          var inst = $popup.data('instance') || editor;
          inst.events.disableBlur();
          inst.image.upload(dt.files);
          inst.events.enableBlur();
        }
      });

      editor.events.$on($popup, 'change', '.fr-image-upload-layer input[type="file"]', function () {
        if (this.files) {
          var inst = $popup.data('instance') || editor;
          inst.events.disableBlur();
          $popup.find('input:focus').blur();
          inst.events.enableBlur();
          inst.image.upload(this.files);
        }
        // Else IE 9 case.

        // Chrome fix.
        $(this).val('');
      });
    }

    function _drop (e) {
      // Check if we are dropping files.
      var dt = e.originalEvent.dataTransfer;
      if (dt && dt.files && dt.files.length) {
        var img = dt.files[0];
        if (img && img.type) {
          // Dropped file is an image that we allow.
          if (editor.opts.imageAllowedTypes.indexOf(img.type.replace(/image\//g, '')) >= 0) {
            editor.markers.remove();
            editor.markers.insertAtPoint(e.originalEvent);
            editor.$el.find('.fr-marker').replaceWith($.FE.MARKERS);

            // Hide popups.
            editor.popups.hideAll();

            // Show the image insert popup.
            var $popup = editor.popups.get('image.insert');
            if (!$popup) $popup = _initInsertPopup();

            editor.popups.setContainer('image.insert', $(editor.opts.scrollableContainer));
            editor.popups.show('image.insert', e.originalEvent.pageX, e.originalEvent.pageY);
            showProgressBar();

            // Upload images.
            upload(dt.files);

            // Cancel anything else.
            e.preventDefault();
            e.stopPropagation();

            return false;
          }
        }
      }
    }

    function _placeCursor () {
      var t;
      var p_node;
      var r = editor.selection.ranges(0);
      if (r.collapsed && r.startContainer.nodeType == Node.ELEMENT_NODE) {
        // Click after image.
        if (r.startContainer.childNodes.length == r.startOffset) {
          t = r.startContainer.childNodes[r.startOffset - 1];
          if (t && t.tagName == 'IMG' && $(t).css('display') == 'block') {
            // Check if image is last node.
            p_node = editor.node.blockParent(t);
            if (p_node && editor.html.defaultTag()) {
              if (!p_node.nextSibling) {
                if (['TD', 'TH'].indexOf(p_node.tagName) < 0) {
                  $(p_node).after('<' + editor.html.defaultTag() + '><br>' + $.FE.MARKERS + '</' + editor.html.defaultTag() + '>');
                }
                else {
                  $(img).after('<br>' + $.FE.MARKERS);
                }
                editor.selection.restore();
              }
            }
            else if (!p_node) {
              $(t).after('<br>' + $.FE.MARKERS);
              editor.selection.restore();
            }
          }
        }
        else if (r.startOffset === 0 && r.startContainer.childNodes.length > r.startOffset) {
          t = r.startContainer.childNodes[r.startOffset];
          if (t && t.tagName == 'IMG' && $(t).css('display') == 'block') {
            // Check if image is last node.
            p_node = editor.node.blockParent(t);
            if (p_node && editor.html.defaultTag()) {
              if (!p_node.previousSibling) {
                if (['TD', 'TH'].indexOf(p_node.tagName) < 0) {
                  $(p_node).before('<' + editor.html.defaultTag() + '><br>' + $.FE.MARKERS + '</' + editor.html.defaultTag() + '>');
                }
                else {
                  $(img).before('<br>' + $.FE.MARKERS);
                }
                editor.selection.restore();
              }
            }
            else if (!p_node) {
              $(t).before($.FE.MARKERS + '<br>');
              editor.selection.restore();
            }
          }
        }
      }
    }

    function _initEvents () {
      // Mouse down on image. It might start move.
      editor.events.$on(editor.$el, editor._mousedown, editor.$el.get(0).tagName == 'IMG' ? null : 'img:not([contenteditable="false"])', function (e) {
        if ($(this).parents('[contenteditable="false"]:not(.fr-element):not(body)').length) return true;

        editor.selection.clear();

        mousedown = true;

        // Prevent the image resizing.
        if (editor.browser.msie) {
          editor.events.disableBlur();
          editor.$el.attr('contenteditable', false);
        }

        if (!editor.draggable) e.preventDefault();

        e.stopPropagation();
      });

      // Mouse up on an image prevent move.
      editor.events.$on(editor.$el, editor._mouseup, editor.$el.get(0).tagName == 'IMG' ? null : 'img:not([contenteditable="false"])', function (e) {
        if ($(this).parents('[contenteditable="false"]:not(.fr-element):not(body)').length) return true;

        if (mousedown) {
          mousedown = false;

          // Remove moving class.
          e.stopPropagation();

          if (editor.browser.msie) {
            editor.$el.attr('contenteditable', true);
            editor.events.enableBlur();
          }
        }
      });

      // Show image popup when it was selected.
      editor.events.on('keyup', function (e) {
        if (e.shiftKey && editor.selection.text().replace(/\n/g, '') === '') {
          var s_el = editor.selection.element();
          var e_el = editor.selection.endElement();
          if (s_el && s_el.tagName == 'IMG') {
            _editImg($(s_el));
          }
          else if (e_el && e_el.tagName == 'IMG') {
            _editImg($(e_el));
          }
        }
      }, true);

      // Drop inside the editor.
      editor.events.on('drop', _drop);

      editor.events.on('mousedown window.mousedown', _markExit);
      editor.events.on('window.touchmove', _unmarkExit);

      editor.events.on('mouseup window.mouseup', function () {
        if ($current_image) {
          _exitEdit();
          return false;
        }
      });
      editor.events.on('commands.mousedown', function ($btn) {
        if ($btn.parents('.fr-toolbar').length > 0) {
          _exitEdit();
        }
      });

      editor.events.on('mouseup', _placeCursor);

      editor.events.on('blur image.hideResizer commands.undo commands.redo element.dropped', function () {
        mousedown = false;
        _exitEdit(true);
      });
    }

    /**
     * Init the image upload popup.
     */

    function _initInsertPopup (delayed) {
      if (delayed) {
        editor.popups.onRefresh('image.insert', _refreshInsertPopup);
        editor.popups.onHide('image.insert', _hideInsertPopup);

        return true;
      }

      var active;

      // Image buttons.
      var image_buttons = '';
      if (editor.opts.imageInsertButtons.length > 1) {
        image_buttons = '<div class="fr-buttons">' + editor.button.buildList(editor.opts.imageInsertButtons) + '</div>';
      }

      var uploadIndex = editor.opts.imageInsertButtons.indexOf('imageUpload');
      var urlIndex = editor.opts.imageInsertButtons.indexOf('imageByURL');

      // Image upload layer.
      var upload_layer = '';
      if (uploadIndex >= 0) {
        active = ' fr-active';
        if (urlIndex >= 0 && uploadIndex > urlIndex) {
          active = '';
        }

        upload_layer = '<div class="fr-image-upload-layer' + active + ' fr-layer" id="fr-image-upload-layer-' + editor.id + '"><strong>' + editor.language.translate('Drop image') + '</strong><br>(' + editor.language.translate('or click') + ')<div class="fr-form"><input type="file" accept="image/' + editor.opts.imageAllowedTypes.join(', image/').toLowerCase() + '" tabIndex="-1"></div></div>'
      }

      // Image by url layer.
      var by_url_layer = '';
      if (urlIndex >= 0) {
        active = ' fr-active';
        if (uploadIndex >= 0 && urlIndex > uploadIndex) {
          active = '';
        }

        by_url_layer = '<div class="fr-image-by-url-layer' + active + ' fr-layer" id="fr-image-by-url-layer-' + editor.id + '"><div class="fr-input-line"><input type="text" placeholder="http://" tabIndex="1"></div><div class="fr-action-buttons"><button type="button" class="fr-command fr-submit" data-cmd="imageInsertByURL" tabIndex="2">' + editor.language.translate('Insert') + '</button></div></div>'
      }

      // Progress bar.
      var progress_bar_layer = '<div class="fr-image-progress-bar-layer fr-layer"><h3 class="fr-message">Uploading</h3><div class="fr-loader"><span class="fr-progress"></span></div><div class="fr-action-buttons"><button type="button" class="fr-command fr-back" data-cmd="imageDismissError" tabIndex="2">OK</button></div></div>';

      var template = {
        buttons: image_buttons,
        upload_layer: upload_layer,
        by_url_layer: by_url_layer,
        progress_bar: progress_bar_layer
      }

      // Set the template in the popup.
      var $popup = editor.popups.create('image.insert', template);

      if (editor.$wp) {
        editor.events.$on(editor.$wp, 'scroll', function () {
          if ($current_image && editor.popups.isVisible('image.insert')) {
            replace();
          }
        });
      }

      _bindInsertEvents($popup);

      return $popup;
    }

    /**
     * Refresh the ALT popup.
     */

    function _refreshAltPopup () {
      if ($current_image) {
        var $popup = editor.popups.get('image.alt');
        $popup.find('input').val($current_image.attr('alt') || '').trigger('change');
      }
    }

    /**
     * Show the ALT popup.
     */

    function showAltPopup () {
      var $popup = editor.popups.get('image.alt');
      if (!$popup) $popup = _initAltPopup();

      hideProgressBar();
      editor.popups.refresh('image.alt');
      editor.popups.setContainer('image.alt', $(editor.opts.scrollableContainer));
      var left = $current_image.offset().left + $current_image.width() / 2;
      var top = $current_image.offset().top + $current_image.height();

      editor.popups.show('image.alt', left, top, $current_image.outerHeight());
    }

    /**
     * Init the image upload popup.
     */

    function _initAltPopup (delayed) {
      if (delayed) {
        editor.popups.onRefresh('image.alt', _refreshAltPopup);
        return true;
      }

      // Image buttons.
      var image_buttons = '';
      image_buttons = '<div class="fr-buttons">' + editor.button.buildList(editor.opts.imageAltButtons) + '</div>';

      // Image by url layer.
      var alt_layer = '';
      alt_layer = '<div class="fr-image-alt-layer fr-layer fr-active" id="fr-image-alt-layer-' + editor.id + '"><div class="fr-input-line"><input type="text" placeholder="' + editor.language.translate('Alternate Text') + '" tabIndex="1"></div><div class="fr-action-buttons"><button type="button" class="fr-command fr-submit" data-cmd="imageSetAlt" tabIndex="2">' + editor.language.translate('Update') + '</button></div></div>'

      var template = {
        buttons: image_buttons,
        alt_layer: alt_layer
      }

      // Set the template in the popup.
      var $popup = editor.popups.create('image.alt', template);

      if (editor.$wp) {
        editor.events.$on(editor.$wp, 'scroll.image-alt', function () {
          if ($current_image && editor.popups.isVisible('image.alt')) {
            showAltPopup();
          }
        });
      }

      return $popup;
    }

    /**
     * Set ALT based on the values from the popup.
     */

    function setAlt (alt) {
      if ($current_image) {
        var $popup = editor.popups.get('image.alt');
        $current_image.attr('alt', alt || $popup.find('input').val() || '');
        $popup.find('input:focus').blur();
        _editImg($current_image);
      }
    }

    /**
     * Refresh the size popup.
     */

    function _refreshSizePopup () {
      if ($current_image) {
        var $popup = editor.popups.get('image.size');
        $popup.find('input[name="width"]').val($current_image.get(0).style.width).trigger('change');
        $popup.find('input[name="height"]').val($current_image.get(0).style.height).trigger('change');
      }
    }

    /**
     * Show the size popup.
     */

    function showSizePopup () {
      var $popup = editor.popups.get('image.size');
      if (!$popup) $popup = _initSizePopup();

      hideProgressBar();
      editor.popups.refresh('image.size');
      editor.popups.setContainer('image.size', $(editor.opts.scrollableContainer));
      var left = $current_image.offset().left + $current_image.width() / 2;
      var top = $current_image.offset().top + $current_image.height();

      editor.popups.show('image.size', left, top, $current_image.outerHeight());
    }

    /**
     * Init the image upload popup.
     */

    function _initSizePopup (delayed) {
      if (delayed) {
        editor.popups.onRefresh('image.size', _refreshSizePopup);

        return true;
      }

      // Image buttons.
      var image_buttons = '';
      image_buttons = '<div class="fr-buttons">' + editor.button.buildList(editor.opts.imageSizeButtons) + '</div>';

      // Size layer.
      var size_layer = '';
      size_layer = '<div class="fr-image-size-layer fr-layer fr-active" id="fr-image-size-layer-' + editor.id + '"><div class="fr-image-group"><div class="fr-input-line"><input type="text" name="width" placeholder="' + editor.language.translate('Width') + '" tabIndex="1"></div><div class="fr-input-line"><input type="text" name="height" placeholder="' + editor.language.translate('Height') + '" tabIndex="1"></div></div><div class="fr-action-buttons"><button type="button" class="fr-command fr-submit" data-cmd="imageSetSize" tabIndex="2">' + editor.language.translate('Update') + '</button></div></div>'

      var template = {
        buttons: image_buttons,
        size_layer: size_layer
      };

      // Set the template in the popup.
      var $popup = editor.popups.create('image.size', template);

      if (editor.$wp) {
        editor.events.$on(editor.$wp, 'scroll.image-size', function () {
          if ($current_image && editor.popups.isVisible('image.size')) {
            showSizePopup();
          }
        });
      }

      return $popup;
    }

    /**
     * Set size based on the current image size.
     */

    function setSize (width, height) {
      if ($current_image) {
        var $popup = editor.popups.get('image.size');
        $current_image.css('width', width || $popup.find('input[name="width"]').val());
        $current_image.css('height', height || $popup.find('input[name="height"]').val());

        $popup.find('input:focus').blur();
        _editImg($current_image);
      }
    }

    /**
     * Show the image upload layer.
     */

    function showLayer (name) {
      var $popup = editor.popups.get('image.insert');

      var left;
      var top;

      // Click on the button from the toolbar without image selected.
      if (!$current_image && !editor.opts.toolbarInline) {
        var $btn = editor.$tb.find('.fr-command[data-cmd="insertImage"]');
        left = $btn.offset().left + $btn.outerWidth() / 2;
        top = $btn.offset().top + (editor.opts.toolbarBottom ? 10 : $btn.outerHeight() - 10);
      }

      // Image is selected.
      else if ($current_image) {
        // Set the top to the bottom of the image.
        top = $current_image.offset().top + $current_image.outerHeight();
      }

      // Image is selected and we are in inline mode.
      if (!$current_image && editor.opts.toolbarInline) {
        // Set top to the popup top.
        top = $popup.offset().top - editor.helpers.getPX($popup.css('margin-top'));

        // If the popup is above apply height correction.
        if ($popup.hasClass('fr-above')) {
          top += $popup.outerHeight();
        }
      }

      // Show the new layer.
      $popup.find('.fr-layer').removeClass('fr-active');
      $popup.find('.fr-' + name + '-layer').addClass('fr-active');

      editor.popups.show('image.insert', left, top, ($current_image ? $current_image.outerHeight() : 0));
    }

    /**
     * Refresh the upload image button.
     */

    function refreshUploadButton ($btn) {
      var $popup = editor.popups.get('image.insert');
      if ($popup.find('.fr-image-upload-layer').hasClass('fr-active')) {
        $btn.addClass('fr-active');
      }
    }

    /**
     * Refresh the insert by url button.
     */

    function refreshByURLButton ($btn) {
      var $popup = editor.popups.get('image.insert');
      if ($popup.find('.fr-image-by-url-layer').hasClass('fr-active')) {
        $btn.addClass('fr-active');
      }
    }

    /**
     * Init image resizer.
     */

    function _initImageResizer () {
      var doc;

      // No shared image resizer.
      if (!editor.shared.$image_resizer) {
        // Create shared image resizer.
        editor.shared.$image_resizer = $('<div class="fr-image-resizer"></div>');
        $image_resizer = editor.shared.$image_resizer;

        // Bind mousedown event shared.
        editor.events.$on($image_resizer, 'mousedown', function (e) {
          e.stopPropagation();
        }, true);

        // Image resize is enabled.
        if (editor.opts.imageResize) {
          $image_resizer.append(_getHandler('nw') + _getHandler('ne') + _getHandler('sw') + _getHandler('se'));

          // Add image resizer overlay and set it.
          editor.shared.$img_overlay = $('<div class="fr-image-overlay"></div>');
          $overlay = editor.shared.$img_overlay;
          doc = $image_resizer.get(0).ownerDocument;
          $(doc).find('body').append($overlay);
        }
      } else {
        $image_resizer = editor.shared.$image_resizer;
        $overlay = editor.shared.$img_overlay;

        editor.events.on('destroy', function () {
          $image_resizer.removeClass('fr-active').appendTo($('body'));
        }, true);
      }

      // Shared destroy.
      editor.events.on('shared.destroy', function () {
        $image_resizer.html('').removeData().remove();
        $image_resizer = null;

        if (editor.opts.imageResize) {
          $overlay.remove();
          $overlay = null;
        }
      }, true);

      // Window resize. Exit from edit.
      if (!editor.helpers.isMobile()) {
        editor.events.$on($(editor.o_win), 'resize', function () {
          if ($current_image && !$current_image.hasClass('fr-uploading')) {
            _exitEdit(true);
          }
          else if ($current_image) {
            _repositionResizer();
            replace();
            showProgressBar(false);
          }
        });
      }

      // Image resize is enabled.
      if (editor.opts.imageResize) {
        doc = $image_resizer.get(0).ownerDocument;

        editor.events.$on($image_resizer, editor._mousedown, '.fr-handler', _handlerMousedown);
        editor.events.$on($(doc), editor._mousemove, _handlerMousemove);
        editor.events.$on($(doc.defaultView || doc.parentWindow), editor._mouseup, _handlerMouseup);

        editor.events.$on($overlay, 'mouseleave', _handlerMouseup);
      }
    }

    /**
     * Remove the current image.
     */

    function remove ($img) {
      $img = $img || $current_image;
      if ($img) {
        if (editor.events.trigger('image.beforeRemove', [$img]) !== false) {
          editor.popups.hideAll();
          _exitEdit(true);

          if ($img.get(0) == editor.$el.get(0)) {
            $img.removeAttr('src');
          } else {
            if ($img.get(0).parentNode.tagName == 'A') {
              editor.selection.setBefore($img.get(0).parentNode) || editor.selection.setAfter($img.get(0).parentNode) || $img.parent().after($.FE.MARKERS);
              $($img.get(0).parentNode).remove();
            } else {
              editor.selection.setBefore($img.get(0)) || editor.selection.setAfter($img.get(0)) || $img.after($.FE.MARKERS);
              $img.remove();
            }

            editor.html.fillEmptyBlocks();
            editor.selection.restore();
          }

          editor.undo.saveStep();
        }
      }
    }

    /**
     * Initialization.
     */

    function _init () {
      _initEvents();

      // Init on image.
      if (editor.$el.get(0).tagName == 'IMG') {
        editor.$el.addClass('fr-view');
      }

      editor.events.$on(editor.$el, editor.helpers.isMobile() && !editor.helpers.isWindowsPhone() ? 'touchend' : 'click', editor.$el.get(0).tagName == 'IMG' ? null : 'img:not([contenteditable="false"])', _edit);

      if (editor.helpers.isMobile()) {
        editor.events.$on(editor.$el, 'touchstart', editor.$el.get(0).tagName == 'IMG' ? null : 'img:not([contenteditable="false"])', function () {
          touchScroll = false;
        })

        editor.events.$on(editor.$el, 'touchmove', function () {
          touchScroll = true;
        });
      }

      editor.events.on('window.keydown keydown', function (e) {
        var key_code = e.which;
        if ($current_image && (key_code == $.FE.KEYCODE.BACKSPACE || key_code == $.FE.KEYCODE.DELETE)) {
          e.preventDefault();
          e.stopPropagation();
          remove();
          return false;
        }

        if ($current_image && key_code == $.FE.KEYCODE.ESC) {
          var $img = $current_image;
          _exitEdit(true);
          editor.selection.setAfter($img.get(0));
          editor.selection.restore();
          e.preventDefault();
          return false;
        }

        if ($current_image && key_code == $.FE.KEYCODE.SPACE) {
          var $img = $current_image;
          _exitEdit(true);
          // Create a text node after the image if there isn't one
          if (!$img.get(0).nextSibling) {
            $img.after(document.createTextNode('\u200B'));
          }
          editor.selection.setAfter($img.get(0));
          editor.selection.restore();
          editor.edit.on();
          return false;
        }

        if ($current_image && !editor.keys.ctrlKey(e)) {
          e.preventDefault();
          return false;
        }
      }, true);

      // Copy/cut image.
      editor.events.on('window.cut window.copy', function (e) {
        if ($current_image) {
          _selectImage();
          $.FE.copied_text = '\n';
          $.FE.copied_html = $current_image.get(0).outerHTML;

          if (e.type == 'copy') {
            setTimeout(function () {
              _editImg($current_image);
            })
          }
          else {
            _exitEdit(true);
            editor.undo.saveStep();
            setTimeout(function () {
              editor.undo.saveStep();
            }, 0);
          }
        }
      }, true);

      // Do not leave page while uploading.
      editor.events.$on($(editor.o_win), 'keydown', function (e) {
        var key_code = e.which;
        if ($current_image && key_code == $.FE.KEYCODE.BACKSPACE) {
          e.preventDefault();
          return false;
        }
      });

      // Check if image is uploading to abort it.
      editor.events.$on(editor.$win, 'keydown', function (e) {
        var key_code = e.which;
        if ($current_image && $current_image.hasClass('fr-uploading') && key_code == $.FE.KEYCODE.ESC) {
          $current_image.trigger('abortUpload');
        }
      });

      editor.events.on('destroy', function () {
        if ($current_image && $current_image.hasClass('fr-uploading')) {
          $current_image.trigger('abortUpload');
        }
      });

      editor.events.on('paste.before', _clipboardPaste);
      editor.events.on('paste.beforeCleanup', _clipboardPasteCleanup);
      editor.events.on('paste.after', _uploadPastedImages);

      editor.events.on('html.set', _refreshImageList);
      editor.events.on('html.inserted', _refreshImageList);
      _refreshImageList();
      editor.events.on('destroy', function () {
        images = [];
      })

      editor.events.on('html.get', function (html) {
        html = html.replace(/<(img)((?:[\w\W]*?))class="([\w\W]*?)(fr-uploading|fr-error)([\w\W]*?)"((?:[\w\W]*?))>/g, '');

        return html;
      });

      if (editor.opts.imageOutputSize) {
        var imgs;

        editor.events.on('html.beforeGet', function () {
          imgs = editor.$el.get(0).querySelectorAll('img')
          for (var i = 0; i < imgs.length; i++) {
            imgs[i].setAttribute('width', $(imgs[i]).width());
            imgs[i].setAttribute('height', $(imgs[i]).height());
          }
        });

        editor.events.on('html.afterGet', function () {
          for (var i = 0; i < imgs.length; i++) {
            imgs[i].removeAttribute('width');
            imgs[i].removeAttribute('height');
          }
        });
      }

      if (editor.opts.iframe) {
        editor.events.on('image.loaded', editor.size.syncIframe);
      }

      if (editor.$wp) {
        _syncImages();
        editor.events.on('contentChanged', _syncImages);
      }

      editor.events.$on($(editor.o_win), 'orientationchange.image', function () {
        setTimeout(function () {
          var $current_image = get();
          if ($current_image) {
            _editImg($current_image);
          }
        }, 0);
      });

      _initEditPopup(true);
      _initInsertPopup(true);
      _initSizePopup(true);
      _initAltPopup(true);

      editor.events.on('node.remove', function ($node) {
        if ($node.get(0).tagName == 'IMG') {
          remove($node);
          return false;
        }
      });
    }

    function _uploadPastedImages () {
      if (!editor.opts.imagePaste) {
        editor.$el.find('img[data-fr-image-pasted]').remove();
      } else {
        // Safari won't work https://bugs.webkit.org/show_bug.cgi?id=49141
        editor.$el.find('img[data-fr-image-pasted]').each(function (index, img) {
          if (editor.opts.imagePasteProcess) {
            var width = editor.opts.imageDefaultWidth;
            if (width && width != 'auto') {
              width = width + (editor.opts.imageResizeWithPercent ? '%' : 'px');
            }
            $(img).css('width', width);

            $(img)
              .removeClass('fr-dii fr-dib fr-fir fr-fil')
              .addClass((editor.opts.imageDefaultDisplay ? 'fr-di' + editor.opts.imageDefaultDisplay[0] : '') + (editor.opts.imageDefaultAlign ? (editor.opts.imageDefaultAlign != 'center' ? ' fr-fi' + editor.opts.imageDefaultAlign[0] : '') : ''));
          }

          // Data images.
          if (img.src.indexOf('data:') === 0) {
            if (editor.events.trigger('image.beforePasteUpload', [img]) === false) {
              return false;
            }

            // Show the progress bar.
            $current_image = $(img);
            _repositionResizer();
            _showEditPopup();
            replace();
            showProgressBar();
            editor.edit.off();

            // Convert image to blob.
            var binary = atob($(img).attr('src').split(',')[1]);
            var array = [];
            for (var i = 0; i < binary.length; i++) {
              array.push(binary.charCodeAt(i));
            }
            var upload_img = new Blob([new Uint8Array(array)], {
              type: 'image/jpeg'
            });

            upload([upload_img]);

            $(img).removeAttr('data-fr-image-pasted');
          }

          // Images without http (Safari ones.).
          else if (img.src.indexOf('http') !== 0) {
            editor.selection.save();
            $(img).remove();
            editor.selection.restore();
          } else {
            $(img).removeAttr('data-fr-image-pasted');
          }
        });
      }
    }

    function _clipboardPaste (e) {
      if (e && e.clipboardData) {
        if (e.clipboardData.items && e.clipboardData.items[0]) {

          var file = e.clipboardData.items[0].getAsFile();

          if (file) {
            var reader = new FileReader();
            reader.onload = function (e) {
              var result = e.target.result;

              // Default width.
              var width = editor.opts.imageDefaultWidth;
              if (width && width != 'auto') {
                width = width + (editor.opts.imageResizeWithPercent ? '%' : 'px');
              }

              editor.html.insert('<img data-fr-image-pasted="true" class="' + (editor.opts.imageDefaultDisplay ? 'fr-di' + editor.opts.imageDefaultDisplay[0] : '') + (editor.opts.imageDefaultAlign ? (editor.opts.imageDefaultAlign != 'center' ? ' fr-fi' + editor.opts.imageDefaultAlign[0] : '') : '') + '" src="' + result + '"' + (width ? ' style="width: ' + width + ';"' : '') + '>');

              editor.events.trigger('paste.after');
            };

            reader.readAsDataURL(file);

            e.preventDefault();

            return false;
          }
        }
      }
    }

    function _clipboardPasteCleanup (clipboard_html) {
      clipboard_html = clipboard_html.replace(/<img /gi, '<img data-fr-image-pasted="true" ');
      return clipboard_html;
    }

    /**
     * Start edit.
     */
    var touchScroll;

    function _edit (e) {
      if ($(this).parents('[contenteditable="false"]:not(.fr-element):not(body)').length) return true;

      if (e && e.type == 'touchend' && touchScroll) {
        return true;
      }

      if (e && editor.edit.isDisabled()) {
        e.stopPropagation();
        e.preventDefault();
        return false;
      }

      // Hide resizer for other instances.
      for (var i = 0; i < $.FE.INSTANCES.length; i++) {
        if ($.FE.INSTANCES[i] != editor) {
          $.FE.INSTANCES[i].events.trigger('image.hideResizer');
        }
      }

      editor.toolbar.disable();

      if (e) {
        e.stopPropagation();
        e.preventDefault();
      }

      // Hide keyboard.
      if (editor.helpers.isMobile()) {
        editor.events.disableBlur();
        editor.$el.blur();
        editor.events.enableBlur();
      }

      if (editor.opts.iframe) {
        editor.size.syncIframe();
      }

      $current_image = $(this);
      _selectImage();
      _repositionResizer();
      _showEditPopup();

      editor.selection.clear();
      editor.button.bulkRefresh();

      editor.events.trigger('video.hideResizer');
    }

    /**
     * Exit edit.
     */

    function _exitEdit (force_exit) {
      if ($current_image && (_canExit() || force_exit === true)) {
        editor.toolbar.enable();

        $image_resizer.removeClass('fr-active');

        editor.popups.hide('image.edit');

        $current_image = null;

        _unmarkExit();
      }
    }

    var img_exit_flag = false;

    function _markExit () {
      img_exit_flag = true;
    }

    function _unmarkExit () {
      img_exit_flag = false;
    }

    function _canExit () {
      return img_exit_flag;
    }

    /**
     * Align image.
     */

    function align (val) {
      $current_image.removeClass('fr-fir fr-fil');
      if (val == 'left') {
        $current_image.addClass('fr-fil');
      } else if (val == 'right') {
        $current_image.addClass('fr-fir');
      }

      _repositionResizer();
      _showEditPopup();
    }

    /**
     * Refresh the align icon.
     */

    function refreshAlign ($btn) {
      if ($current_image) {
        if ($current_image.hasClass('fr-fil')) {
          $btn.find('> *:first').replaceWith(editor.icon.create('align-left'));
        } else if ($current_image.hasClass('fr-fir')) {
          $btn.find('> *:first').replaceWith(editor.icon.create('align-right'));
        } else {
          $btn.find('> *:first').replaceWith(editor.icon.create('align-justify'));
        }
      }
    }

    /**
     * Refresh the align option from the dropdown.
     */

    function refreshAlignOnShow ($btn, $dropdown) {
      if ($current_image) {
        var alignment = 'justify';

        if ($current_image.hasClass('fr-fil')) {
          alignment = 'left';
        } else if ($current_image.hasClass('fr-fir')) {
          alignment = 'right';
        }

        $dropdown.find('.fr-command[data-param1="' + alignment + '"]').addClass('fr-active');
      }
    }

    /**
     * Align image.
     */

    function display (val) {
      $current_image.removeClass('fr-dii fr-dib');
      if (val == 'inline') {
        $current_image.addClass('fr-dii');
      } else if (val == 'block') {
        $current_image.addClass('fr-dib');
      }

      _repositionResizer();
      _showEditPopup();
    }

    /**
     * Refresh the image display selected option.
     */

    function refreshDisplayOnShow ($btn, $dropdown) {
      var d = 'block';
      if ($current_image.hasClass('fr-dii')) {
        d = 'inline';
      }

      $dropdown.find('.fr-command[data-param1="' + d + '"]').addClass('fr-active');
    }

    /**
     * Show the replace popup.
     */

    function replace () {
      var $popup = editor.popups.get('image.insert');
      if (!$popup) $popup = _initInsertPopup();

      if (!editor.popups.isVisible('image.insert')) {
        hideProgressBar();
        editor.popups.refresh('image.insert');
        editor.popups.setContainer('image.insert', $(editor.opts.scrollableContainer));
      }

      var left = $current_image.offset().left + $current_image.width() / 2;
      var top = $current_image.offset().top + $current_image.height();

      editor.popups.show('image.insert', left, top, $current_image.outerHeight());
    }

    /**
     * Place selection around current image.
     */
    function _selectImage () {
      if ($current_image) {
        editor.selection.clear();
        var range = editor.doc.createRange();
        range.selectNode($current_image.get(0));
        var selection = editor.selection.get();
        selection.addRange(range);
      }
    }

    /**
     * Get back to the image main popup.
     */
    function back () {
      if ($current_image) {
        $('.fr-popup input:focus').blur();
        _editImg($current_image);
      } else {
        editor.events.disableBlur();
        editor.selection.restore();
        editor.events.enableBlur();

        editor.popups.hide('image.insert');
        editor.toolbar.showInline();
      }
    }

    /**
     * Get the current image.
     */

    function get () {
      return $current_image;
    }

    /**
     * Apply specific style.
     */

    function applyStyle (val, imageStyles, multipleStyles) {
      if (typeof imageStyles == 'undefined') imageStyles = editor.opts.imageStyles;
      if (typeof multipleStyles == 'undefined') multipleStyles = editor.opts.imageMultipleStyles;

      if (!$current_image) return false;

      // Remove multiple styles.
      if (!multipleStyles) {
        var styles = Object.keys(imageStyles);
        styles.splice(styles.indexOf(val), 1);
        $current_image.removeClass(styles.join(' '));
      }

      $current_image.toggleClass(val);

      _editImg($current_image);
    }

    return {
      _init: _init,
      showInsertPopup: showInsertPopup,
      showLayer: showLayer,
      refreshUploadButton: refreshUploadButton,
      refreshByURLButton: refreshByURLButton,
      upload: upload,
      insertByURL: insertByURL,
      align: align,
      refreshAlign: refreshAlign,
      refreshAlignOnShow: refreshAlignOnShow,
      display: display,
      refreshDisplayOnShow: refreshDisplayOnShow,
      replace: replace,
      back: back,
      get: get,
      insert: insert,
      showProgressBar: showProgressBar,
      remove: remove,
      hideProgressBar: hideProgressBar,
      applyStyle: applyStyle,
      showAltPopup: showAltPopup,
      showSizePopup: showSizePopup,
      setAlt: setAlt,
      setSize: setSize,
      exitEdit: _exitEdit,
      edit: _editImg
    }
  }

  // Insert image button.
  $.FE.DefineIcon('insertImage', {
    NAME: 'image'
  });
  $.FE.RegisterShortcut($.FE.KEYCODE.P, 'insertImage', null, 'P');
  $.FE.RegisterCommand('insertImage', {
    title: 'Insert Image',
    undo: false,
    focus: true,
    refreshAfterCallback: false,
    popup: true,
    callback: function () {
      if (!this.popups.isVisible('image.insert')) {
        this.image.showInsertPopup();
      } else {
        if (this.$el.find('.fr-marker')) {
          this.events.disableBlur();
          this.selection.restore();
        }
        this.popups.hide('image.insert');
      }
    },
    plugin: 'image'
  });

  // Image upload button inside the insert image popup.
  $.FE.DefineIcon('imageUpload', {
    NAME: 'upload'
  });
  $.FE.RegisterCommand('imageUpload', {
    title: 'Upload Image',
    undo: false,
    focus: false,
    callback: function () {
      this.image.showLayer('image-upload');
    },
    refresh: function ($btn) {
      this.image.refreshUploadButton($btn);
    }
  });

  // Image by URL button inside the insert image popup.
  $.FE.DefineIcon('imageByURL', {
    NAME: 'link'
  });
  $.FE.RegisterCommand('imageByURL', {
    title: 'By URL',
    undo: false,
    focus: false,
    callback: function () {
      this.image.showLayer('image-by-url');
    },
    refresh: function ($btn) {
      this.image.refreshByURLButton($btn);
    }
  })

  // Insert image button inside the insert by URL layer.
  $.FE.RegisterCommand('imageInsertByURL', {
    title: 'Insert Image',
    undo: true,
    refreshAfterCallback: false,
    callback: function () {
      this.image.insertByURL();
    },
    refresh: function ($btn) {
      var $current_image = this.image.get();
      if (!$current_image) {
        $btn.text(this.language.translate('Insert'));
      } else {
        $btn.text(this.language.translate('Replace'));
      }
    }
  })

  // Image display.
  $.FE.DefineIcon('imageDisplay', {
    NAME: 'star'
  })
  $.FE.RegisterCommand('imageDisplay', {
    title: 'Display',
    type: 'dropdown',
    options: {
      inline: 'Inline',
      block: 'Break Text'
    },
    callback: function (cmd, val) {
      this.image.display(val);
    },
    refresh: function ($btn) {
      if (!this.opts.imageTextNear) $btn.addClass('fr-hidden');
    },
    refreshOnShow: function ($btn, $dropdown) {
      this.image.refreshDisplayOnShow($btn, $dropdown);
    }
  })

  // Image align.
  if (!$.FE.ICONS.align) {
    $.FE.DefineIcon('align', { NAME: 'align-left' });
    $.FE.DefineIcon('align-left', { NAME: 'align-left' });
    $.FE.DefineIcon('align-right', { NAME: 'align-right' });
    $.FE.DefineIcon('align-center', { NAME: 'align-center' });
    $.FE.DefineIcon('align-justify', { NAME: 'align-justify' });
  }

  $.FE.DefineIcon('imageAlign', {
    NAME: 'align-center'
  })
  $.FE.RegisterCommand('imageAlign', {
    type: 'dropdown',
    title: 'Align',
    options: {
      left: 'Align Left',
      justify: 'None',
      right: 'Align Right'
    },
    html: function () {
      var c = '<ul class="fr-dropdown-list">';
      var options = $.FE.COMMANDS.imageAlign.options;
      for (var val in options) {
        if (options.hasOwnProperty(val)) {
          c += '<li><a class="fr-command fr-title" data-cmd="imageAlign" data-param1="' + val + '" title="' + this.language.translate(options[val]) + '">' + this.icon.create('align-' + val) + '</a></li>';
        }
      }
      c += '</ul>';

      return c;
    },
    callback: function (cmd, val) {
      this.image.align(val);
    },
    refresh: function ($btn) {
      this.image.refreshAlign($btn);
    },
    refreshOnShow: function ($btn, $dropdown) {
      this.image.refreshAlignOnShow($btn, $dropdown);
    }
  })

  // Image replace.
  $.FE.DefineIcon('imageReplace', {
    NAME: 'exchange'
  })
  $.FE.RegisterCommand('imageReplace', {
    title: 'Replace',
    undo: false,
    focus: false,
    refreshAfterCallback: false,
    callback: function () {
      this.image.replace();
    }
  })

  // Image remove.
  $.FE.DefineIcon('imageRemove', {
    NAME: 'trash'
  })
  $.FE.RegisterCommand('imageRemove', {
    title: 'Remove',
    callback: function () {
      this.image.remove();
    }
  })

  // Image back.
  $.FE.DefineIcon('imageBack', {
    NAME: 'arrow-left'
  });
  $.FE.RegisterCommand('imageBack', {
    title: 'Back',
    undo: false,
    focus: false,
    back: true,
    callback: function () {
      this.image.back();
    },
    refresh: function ($btn) {
      var $current_image = this.image.get();
      if (!$current_image && !this.opts.toolbarInline) {
        $btn.addClass('fr-hidden');
        $btn.next('.fr-separator').addClass('fr-hidden');
      } else {
        $btn.removeClass('fr-hidden');
        $btn.next('.fr-separator').removeClass('fr-hidden');
      }
    }
  });

  $.FE.RegisterCommand('imageDismissError', {
    title: 'OK',
    undo: false,
    callback: function () {
      this.image.hideProgressBar(true);
    }
  })

  // Image styles.
  $.FE.DefineIcon('imageStyle', {
    NAME: 'magic'
  })
  $.FE.RegisterCommand('imageStyle', {
    title: 'Style',
    type: 'dropdown',
    html: function () {
      var c = '<ul class="fr-dropdown-list">';
      var options = this.opts.imageStyles;
      for (var cls in options) {
        if (options.hasOwnProperty(cls)) {
          c += '<li><a class="fr-command" data-cmd="imageStyle" data-param1="' + cls + '">' + this.language.translate(options[cls]) + '</a></li>';
        }
      }
      c += '</ul>';

      return c;
    },
    callback: function (cmd, val) {
      this.image.applyStyle(val);
    },
    refreshOnShow: function ($btn, $dropdown) {
      var $current_image = this.image.get();

      if ($current_image) {
        $dropdown.find('.fr-command').each(function () {
          var cls = $(this).data('param1');
          $(this).toggleClass('fr-active', $current_image.hasClass(cls));
        })
      }
    }
  })

  // Image alt.
  $.FE.DefineIcon('imageAlt', {
    NAME: 'info'
  })
  $.FE.RegisterCommand('imageAlt', {
    undo: false,
    focus: false,
    title: 'Alternate Text',
    callback: function () {
      this.image.showAltPopup();
    }
  });

  $.FE.RegisterCommand('imageSetAlt', {
    undo: true,
    focus: false,
    title: 'Update',
    refreshAfterCallback: false,
    callback: function () {
      this.image.setAlt();
    }
  });

  // Image size.
  $.FE.DefineIcon('imageSize', {
    NAME: 'arrows-alt'
  })
  $.FE.RegisterCommand('imageSize', {
    undo: false,
    focus: false,
    title: 'Change Size',
    callback: function () {
      this.image.showSizePopup();
    }
  });

  $.FE.RegisterCommand('imageSetSize', {
    undo: true,
    focus: false,
    title: 'Update',
    refreshAfterCallback: false,
    callback: function () {
      this.image.setSize();
    }
  });

}));

};


modules['helpcenter/primary/plugins/dropzone'] = function(require, exports, module, modules) {

/*
 *
 * More info at [www.dropzonejs.com](http://www.dropzonejs.com)
 *
 * Copyright (c) 2012, Matias Meno
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */

(function() {
  var Dropzone, Emitter, camelize, contentLoaded, detectVerticalSquash, drawImageIOSFix, noop, without,
    __slice = [].slice,
    __hasProp = {}.hasOwnProperty,
    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };

  noop = function() {};

  Emitter = (function() {
    function Emitter() {}

    Emitter.prototype.addEventListener = Emitter.prototype.on;

    Emitter.prototype.on = function(event, fn) {
      this._callbacks = this._callbacks || {};
      if (!this._callbacks[event]) {
        this._callbacks[event] = [];
      }
      this._callbacks[event].push(fn);
      return this;
    };

    Emitter.prototype.emit = function() {
      var args, callback, callbacks, event, _i, _len;
      event = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
      this._callbacks = this._callbacks || {};
      callbacks = this._callbacks[event];
      if (callbacks) {
        for (_i = 0, _len = callbacks.length; _i < _len; _i++) {
          callback = callbacks[_i];
          callback.apply(this, args);
        }
      }
      return this;
    };

    Emitter.prototype.removeListener = Emitter.prototype.off;

    Emitter.prototype.removeAllListeners = Emitter.prototype.off;

    Emitter.prototype.removeEventListener = Emitter.prototype.off;

    Emitter.prototype.off = function(event, fn) {
      var callback, callbacks, i, _i, _len;
      if (!this._callbacks || arguments.length === 0) {
        this._callbacks = {};
        return this;
      }
      callbacks = this._callbacks[event];
      if (!callbacks) {
        return this;
      }
      if (arguments.length === 1) {
        delete this._callbacks[event];
        return this;
      }
      for (i = _i = 0, _len = callbacks.length; _i < _len; i = ++_i) {
        callback = callbacks[i];
        if (callback === fn) {
          callbacks.splice(i, 1);
          break;
        }
      }
      return this;
    };

    return Emitter;

  })();

  Dropzone = (function(_super) {
    var extend, resolveOption;

    __extends(Dropzone, _super);

    Dropzone.prototype.Emitter = Emitter;


    /*
    This is a list of all available events you can register on a dropzone object.
    
    You can register an event handler like this:
    
        dropzone.on("dragEnter", function() { });
     */

    Dropzone.prototype.events = ["drop", "dragstart", "dragend", "dragenter", "dragover", "dragleave", "addedfile", "addedfiles", "removedfile", "thumbnail", "error", "errormultiple", "processing", "processingmultiple", "uploadprogress", "totaluploadprogress", "sending", "sendingmultiple", "success", "successmultiple", "canceled", "canceledmultiple", "complete", "completemultiple", "reset", "maxfilesexceeded", "maxfilesreached", "queuecomplete"];

    Dropzone.prototype.defaultOptions = {
      url: null,
      method: "post",
      withCredentials: false,
      parallelUploads: 2,
      uploadMultiple: false,
      maxFilesize: 256,
      paramName: "file",
      createImageThumbnails: true,
      maxThumbnailFilesize: 10,
      thumbnailWidth: 120,
      thumbnailHeight: 120,
      filesizeBase: 1000,
      maxFiles: null,
      params: {},
      clickable: true,
      ignoreHiddenFiles: true,
      acceptedFiles: null,
      acceptedMimeTypes: null,
      autoProcessQueue: true,
      autoQueue: true,
      addRemoveLinks: false,
      previewsContainer: null,
      hiddenInputContainer: "body",
      capture: null,
      dictDefaultMessage: "Drop files here to upload",
      dictFallbackMessage: "Your browser does not support drag'n'drop file uploads.",
      dictFallbackText: "Please use the fallback form below to upload your files like in the olden days.",
      dictFileTooBig: "File is too big ({{filesize}}MiB). Max filesize: {{maxFilesize}}MiB.",
      dictInvalidFileType: "You can't upload files of this type.",
      dictResponseError: "Server responded with {{statusCode}} code.",
      dictCancelUpload: "",
      dictCancelUploadConfirmation: "Are you sure you want to cancel this upload?",
      dictRemoveFile: "",
      dictRemoveFileConfirmation: null,
      dictMaxFilesExceeded: "You can not upload any more files.",
      accept: function(file, done) {
        return done();
      },
      init: function() {
        return noop;
      },
      forceFallback: false,
      fallback: function() {
        var child, messageElement, span, _i, _len, _ref;
        this.element.className = "" + this.element.className + " dz-browser-not-supported";
        _ref = this.element.getElementsByTagName("div");
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
          child = _ref[_i];
          if (/(^| )dz-message($| )/.test(child.className)) {
            messageElement = child;
            child.className = "dz-message";
            continue;
          }
        }
        if (!messageElement) {
          messageElement = Dropzone.createElement("<div class=\"dz-message\"><span></span></div>");
          this.element.appendChild(messageElement);
        }
        span = messageElement.getElementsByTagName("span")[0];
        if (span) {
          if (span.textContent != null) {
            span.textContent = this.options.dictFallbackMessage;
          } else if (span.innerText != null) {
            span.innerText = this.options.dictFallbackMessage;
          }
        }
        return this.element.appendChild(this.getFallbackForm());
      },
      resize: function(file) {
        var info, srcRatio, trgRatio;
        info = {
          srcX: 0,
          srcY: 0,
          srcWidth: file.width,
          srcHeight: file.height
        };
        srcRatio = file.width / file.height;
        info.optWidth = this.options.thumbnailWidth;
        info.optHeight = this.options.thumbnailHeight;
        if ((info.optWidth == null) && (info.optHeight == null)) {
          info.optWidth = info.srcWidth;
          info.optHeight = info.srcHeight;
        } else if (info.optWidth == null) {
          info.optWidth = srcRatio * info.optHeight;
        } else if (info.optHeight == null) {
          info.optHeight = (1 / srcRatio) * info.optWidth;
        }
        trgRatio = info.optWidth / info.optHeight;
        if (file.height < info.optHeight || file.width < info.optWidth) {
          info.trgHeight = info.srcHeight;
          info.trgWidth = info.srcWidth;
        } else {
          if (srcRatio > trgRatio) {
            info.srcHeight = file.height;
            info.srcWidth = info.srcHeight * trgRatio;
          } else {
            info.srcWidth = file.width;
            info.srcHeight = info.srcWidth / trgRatio;
          }
        }
        info.srcX = (file.width - info.srcWidth) / 2;
        info.srcY = (file.height - info.srcHeight) / 2;
        return info;
      },

      /*
      Those functions register themselves to the events on init and handle all
      the user interface specific stuff. Overwriting them won't break the upload
      but can break the way it's displayed.
      You can overwrite them if you don't like the default behavior. If you just
      want to add an additional event handler, register it on the dropzone object
      and don't overwrite those options.
       */
      drop: function(e) {
        return this.element.classList.remove("dz-drag-hover");
      },
      dragstart: noop,
      dragend: function(e) {
        return this.element.classList.remove("dz-drag-hover");
      },
      dragenter: function(e) {
        return this.element.classList.add("dz-drag-hover");
      },
      dragover: function(e) {
        return this.element.classList.add("dz-drag-hover");
      },
      dragleave: function(e) {
        return this.element.classList.remove("dz-drag-hover");
      },
      paste: noop,
      reset: function() {
        return this.element.classList.remove("dz-started");
      },
      addedfile: function(file) {
        var node, removeFileEvent, removeLink, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _results;
        if (this.element === this.previewsContainer) {
          this.element.classList.add("dz-started");
        }
        if (this.previewsContainer) {
          file.previewElement = Dropzone.createElement(this.options.previewTemplate.trim());
          file.previewTemplate = file.previewElement;
          this.previewsContainer.appendChild(file.previewElement);
          _ref = file.previewElement.querySelectorAll("[data-dz-name]");
          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
            node = _ref[_i];
            node.textContent = file.name;
          }
          _ref1 = file.previewElement.querySelectorAll("[data-dz-size]");
          for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
            node = _ref1[_j];
            node.innerHTML = this.filesize(file.size);
          }
          if (this.options.addRemoveLinks) {
            file._removeLink = Dropzone.createElement("<a class=\"dz-remove icon icon-alert--close\" href=\"javascript:undefined;\" data-dz-remove>" + this.options.dictRemoveFile + "</a>");
            file.previewElement.appendChild(file._removeLink);
          }
          removeFileEvent = (function(_this) {
            return function(e) {
              e.preventDefault();
              e.stopPropagation();
              if (file.status === Dropzone.UPLOADING) {
                return Dropzone.confirm(_this.options.dictCancelUploadConfirmation, function() {
                  return _this.removeFile(file);
                });
              } else {
                if (_this.options.dictRemoveFileConfirmation) {
                  return Dropzone.confirm(_this.options.dictRemoveFileConfirmation, function() {
                    return _this.removeFile(file);
                  });
                } else {
                  return _this.removeFile(file);
                }
              }
            };
          })(this);
          _ref2 = file.previewElement.querySelectorAll("[data-dz-remove]");
          _results = [];
          for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
            removeLink = _ref2[_k];
            _results.push(removeLink.addEventListener("click", removeFileEvent));
          }
          return _results;
        }
      },
      removedfile: function(file) {
        var _ref;
        if (file.previewElement) {
          if ((_ref = file.previewElement) != null) {
            _ref.parentNode.removeChild(file.previewElement);
          }
        }
        return this._updateMaxFilesReachedClass();
      },
      thumbnail: function(file, dataUrl) {
        var thumbnailElement, _i, _len, _ref;
        if (file.previewElement) {
          file.previewElement.classList.remove("dz-file-preview");
          _ref = file.previewElement.querySelectorAll("[data-dz-thumbnail]");
          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
            thumbnailElement = _ref[_i];
            thumbnailElement.alt = file.name;
            thumbnailElement.src = dataUrl;
          }
          return setTimeout(((function(_this) {
            return function() {
              return file.previewElement.classList.add("dz-image-preview");
            };
          })(this)), 1);
        }
      },
      // @custom: error()
      error: function(file, message) {
          if (message.status === 401) {
              window.location.href = _Payload.url + 'login';
          } else if (message.status !== 400) {
              var node, _i, _len, _ref, _results;
              if (file.previewElement) {
                file.previewElement.classList.add("dz-error");
                if (_.isObject(message)) {
                  message = message.notifications[0].message;
                }
                _ref = file.previewElement.querySelectorAll("[data-dz-errormessage]");
                _results = [];
                for (_i = 0, _len = _ref.length; _i < _len; _i++) {
                  node = _ref[_i];
                  _results.push(node.textContent = message);
                }
                return _results;
              }
          }
          $('#save-config').removeClass('is-disabled');
      },
      errormultiple: noop,
      processing: function(file) {
        if (file.previewElement) {
          file.previewElement.classList.add("dz-processing");
          if (file._removeLink) {
            return file._removeLink.textContent = this.options.dictCancelUpload;
          }
        }
      },
      processingmultiple: noop,
      uploadprogress: function(file, progress, bytesSent) {
        var node, _i, _len, _ref, _results;
        if (file.previewElement) {
          _ref = file.previewElement.querySelectorAll("[data-dz-uploadprogress]");
          _results = [];
          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
            node = _ref[_i];
            if (node.nodeName === 'PROGRESS') {
              _results.push(node.value = progress);
            } else {
              _results.push(node.style.width = "" + progress + "%");
            }
          }
          return _results;
        }
      },
      totaluploadprogress: noop,
      sending: noop,
      sendingmultiple: noop,
      success: function(file) {
        if (file.previewElement) {
          return file.previewElement.classList.add("dz-success");
        }
      },
      successmultiple: noop,
      canceled: function(file) {
        return this.emit("error", file, "Upload canceled.");
      },
      canceledmultiple: noop,
      complete: function(file) {
        if (file._removeLink) {
          file._removeLink.textContent = this.options.dictRemoveFile;
        }
        if (file.previewElement) {
          return file.previewElement.classList.add("dz-complete");
        }
      },
      completemultiple: noop,
      maxfilesexceeded: noop,
      maxfilesreached: noop,
      queuecomplete: noop,
      addedfiles: noop,
      previewTemplate: "<div class=\"dz-preview dz-file-preview\">\n  <div class=\"dz-image\"><img data-dz-thumbnail /></div>\n  <div class=\"dz-details\">\n    <div class=\"dz-size\"><span data-dz-size></span></div>\n    <div class=\"dz-filename\"><span data-dz-name></span></div>\n  </div>\n  <div class=\"dz-progress\"><span class=\"dz-upload\" data-dz-uploadprogress></span></div>\n  <div class=\"dz-error-message\"><span data-dz-errormessage></span></div>\n  <div class=\"dz-success-mark\">\n    <svg width=\"54px\" height=\"54px\" viewBox=\"0 0 54 54\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:sketch=\"http://www.bohemiancoding.com/sketch/ns\">\n      <title>Check</title>\n      <defs></defs>\n      <g id=\"Page-1\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\" sketch:type=\"MSPage\">\n        <path d=\"M23.5,31.8431458 L17.5852419,25.9283877 C16.0248253,24.3679711 13.4910294,24.366835 11.9289322,25.9289322 C10.3700136,27.4878508 10.3665912,30.0234455 11.9283877,31.5852419 L20.4147581,40.0716123 C20.5133999,40.1702541 20.6159315,40.2626649 20.7218615,40.3488435 C22.2835669,41.8725651 24.794234,41.8626202 26.3461564,40.3106978 L43.3106978,23.3461564 C44.8771021,21.7797521 44.8758057,19.2483887 43.3137085,17.6862915 C41.7547899,16.1273729 39.2176035,16.1255422 37.6538436,17.6893022 L23.5,31.8431458 Z M27,53 C41.3594035,53 53,41.3594035 53,27 C53,12.6405965 41.3594035,1 27,1 C12.6405965,1 1,12.6405965 1,27 C1,41.3594035 12.6405965,53 27,53 Z\" id=\"Oval-2\" stroke-opacity=\"0.198794158\" stroke=\"#747474\" fill-opacity=\"0.816519475\" fill=\"#FFFFFF\" sketch:type=\"MSShapeGroup\"></path>\n      </g>\n    </svg>\n  </div>\n  <div class=\"dz-error-mark\">\n    <svg width=\"54px\" height=\"54px\" viewBox=\"0 0 54 54\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:sketch=\"http://www.bohemiancoding.com/sketch/ns\">\n      <title>Error</title>\n      <defs></defs>\n      <g id=\"Page-1\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\" sketch:type=\"MSPage\">\n        <g id=\"Check-+-Oval-2\" sketch:type=\"MSLayerGroup\" stroke=\"#747474\" stroke-opacity=\"0.198794158\" fill=\"#FFFFFF\" fill-opacity=\"0.816519475\">\n          <path d=\"M32.6568542,29 L38.3106978,23.3461564 C39.8771021,21.7797521 39.8758057,19.2483887 38.3137085,17.6862915 C36.7547899,16.1273729 34.2176035,16.1255422 32.6538436,17.6893022 L27,23.3431458 L21.3461564,17.6893022 C19.7823965,16.1255422 17.2452101,16.1273729 15.6862915,17.6862915 C14.1241943,19.2483887 14.1228979,21.7797521 15.6893022,23.3461564 L21.3431458,29 L15.6893022,34.6538436 C14.1228979,36.2202479 14.1241943,38.7516113 15.6862915,40.3137085 C17.2452101,41.8726271 19.7823965,41.8744578 21.3461564,40.3106978 L27,34.6568542 L32.6538436,40.3106978 C34.2176035,41.8744578 36.7547899,41.8726271 38.3137085,40.3137085 C39.8758057,38.7516113 39.8771021,36.2202479 38.3106978,34.6538436 L32.6568542,29 Z M27,53 C41.3594035,53 53,41.3594035 53,27 C53,12.6405965 41.3594035,1 27,1 C12.6405965,1 1,12.6405965 1,27 C1,41.3594035 12.6405965,53 27,53 Z\" id=\"Oval-2\" sketch:type=\"MSShapeGroup\"></path>\n        </g>\n      </g>\n    </svg>\n  </div>\n</div>"
    };

    extend = function() {
      var key, object, objects, target, val, _i, _len;
      target = arguments[0], objects = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
      for (_i = 0, _len = objects.length; _i < _len; _i++) {
        object = objects[_i];
        for (key in object) {
          val = object[key];
          target[key] = val;
        }
      }
      return target;
    };

    function Dropzone(element, options) {
      var elementOptions, fallback, _ref;
      this.element = element;
      this.version = Dropzone.version;
      this.defaultOptions.previewTemplate = this.defaultOptions.previewTemplate.replace(/\n*/g, "");
      this.clickableElements = [];
      this.listeners = [];
      this.files = [];
      if (typeof this.element === "string") {
        this.element = document.querySelector(this.element);
      }
      if (!(this.element && (this.element.nodeType != null))) {
        throw new Error("Invalid dropzone element.");
      }
      if (this.element.dropzone) {
        throw new Error("Dropzone already attached.");
      }
      Dropzone.instances.push(this);
      this.element.dropzone = this;
      elementOptions = (_ref = Dropzone.optionsForElement(this.element)) != null ? _ref : {};
      this.options = extend({}, this.defaultOptions, elementOptions, options != null ? options : {});
      if (this.options.forceFallback || !Dropzone.isBrowserSupported()) {
        return this.options.fallback.call(this);
      }
      if (this.options.url == null) {
        this.options.url = this.element.getAttribute("action");
      }
      if (!this.options.url) {
        throw new Error("No URL provided.");
      }
      if (this.options.acceptedFiles && this.options.acceptedMimeTypes) {
        throw new Error("You can't provide both 'acceptedFiles' and 'acceptedMimeTypes'. 'acceptedMimeTypes' is deprecated.");
      }
      if (this.options.acceptedMimeTypes) {
        this.options.acceptedFiles = this.options.acceptedMimeTypes;
        delete this.options.acceptedMimeTypes;
      }
      this.options.method = this.options.method.toUpperCase();
      if ((fallback = this.getExistingFallback()) && fallback.parentNode) {
        fallback.parentNode.removeChild(fallback);
      }
      if (this.options.previewsContainer !== false) {
        if (this.options.previewsContainer) {
          this.previewsContainer = Dropzone.getElement(this.options.previewsContainer, "previewsContainer");
        } else {
          this.previewsContainer = this.element;
        }
      }
      if (this.options.clickable) {
        if (this.options.clickable === true) {
          this.clickableElements = [this.element];
        } else {
          this.clickableElements = Dropzone.getElements(this.options.clickable, "clickable");
        }
      }
      this.init();
    }

    Dropzone.prototype.getAcceptedFiles = function() {
      var file, _i, _len, _ref, _results;
      _ref = this.files;
      _results = [];
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        file = _ref[_i];
        if (file.accepted) {
          _results.push(file);
        }
      }
      return _results;
    };

    Dropzone.prototype.getRejectedFiles = function() {
      var file, _i, _len, _ref, _results;
      _ref = this.files;
      _results = [];
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        file = _ref[_i];
        if (!file.accepted) {
          _results.push(file);
        }
      }
      return _results;
    };

    Dropzone.prototype.getFilesWithStatus = function(status) {
      var file, _i, _len, _ref, _results;
      _ref = this.files;
      _results = [];
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        file = _ref[_i];
        if (file.status === status) {
          _results.push(file);
        }
      }
      return _results;
    };

    Dropzone.prototype.getQueuedFiles = function() {
      return this.getFilesWithStatus(Dropzone.QUEUED);
    };

    Dropzone.prototype.getUploadingFiles = function() {
      return this.getFilesWithStatus(Dropzone.UPLOADING);
    };

    Dropzone.prototype.getAddedFiles = function() {
      return this.getFilesWithStatus(Dropzone.ADDED);
    };

    Dropzone.prototype.getActiveFiles = function() {
      var file, _i, _len, _ref, _results;
      _ref = this.files;
      _results = [];
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        file = _ref[_i];
        if (file.status === Dropzone.UPLOADING || file.status === Dropzone.QUEUED) {
          _results.push(file);
        }
      }
      return _results;
    };

    Dropzone.prototype.init = function() {
      var eventName, noPropagation, setupHiddenFileInput, _i, _len, _ref, _ref1;
      if (this.element.tagName === "form") {
        this.element.setAttribute("enctype", "multipart/form-data");
      }
      if (this.element.classList.contains("dropzone") && !this.element.querySelector(".dz-message")) {
        this.element.appendChild(Dropzone.createElement("<div class=\"dz-default dz-message\"><span>" + this.options.dictDefaultMessage + "</span></div>"));
      }
      if (this.clickableElements.length) {
        setupHiddenFileInput = (function(_this) {
          return function() {
            if (_this.hiddenFileInput) {
              _this.hiddenFileInput.parentNode.removeChild(_this.hiddenFileInput);
            }
            _this.hiddenFileInput = document.createElement("input");
            _this.hiddenFileInput.setAttribute("type", "file");
            if ((_this.options.maxFiles == null) || _this.options.maxFiles > 1) {
              _this.hiddenFileInput.setAttribute("multiple", "multiple");
            }
            _this.hiddenFileInput.className = "dz-hidden-input";
            if (_this.options.acceptedFiles != null) {
              _this.hiddenFileInput.setAttribute("accept", _this.options.acceptedFiles);
            }
            if (_this.options.capture != null) {
              _this.hiddenFileInput.setAttribute("capture", _this.options.capture);
            }
            _this.hiddenFileInput.style.visibility = "hidden";
            _this.hiddenFileInput.style.position = "absolute";
            _this.hiddenFileInput.style.top = "0";
            _this.hiddenFileInput.style.left = "0";
            _this.hiddenFileInput.style.height = "0";
            _this.hiddenFileInput.style.width = "0";
            document.querySelector(_this.options.hiddenInputContainer).appendChild(_this.hiddenFileInput);
            return _this.hiddenFileInput.addEventListener("change", function() {
              var file, files, _i, _len;
              files = _this.hiddenFileInput.files;
              if (files.length) {
                for (_i = 0, _len = files.length; _i < _len; _i++) {
                  file = files[_i];
                  _this.addFile(file);
                }
              }
              _this.emit("addedfiles", files);
              return setupHiddenFileInput();
            });
          };
        })(this);
        setupHiddenFileInput();
      }
      this.URL = (_ref = window.URL) != null ? _ref : window.webkitURL;
      _ref1 = this.events;
      for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
        eventName = _ref1[_i];
        this.on(eventName, this.options[eventName]);
      }
      this.on("uploadprogress", (function(_this) {
        return function() {
          return _this.updateTotalUploadProgress();
        };
      })(this));
      this.on("removedfile", (function(_this) {
        return function() {
          return _this.updateTotalUploadProgress();
        };
      })(this));
      this.on("canceled", (function(_this) {
        return function(file) {
          return _this.emit("complete", file);
        };
      })(this));
      this.on("complete", (function(_this) {
        return function(file) {
          if (_this.getAddedFiles().length === 0 && _this.getUploadingFiles().length === 0 && _this.getQueuedFiles().length === 0) {
            return setTimeout((function() {
              return _this.emit("queuecomplete");
            }), 0);
          }
        };
      })(this));
      noPropagation = function(e) {
        e.stopPropagation();
        if (e.preventDefault) {
          return e.preventDefault();
        } else {
          return e.returnValue = false;
        }
      };
      this.listeners = [
        {
          element: this.element,
          events: {
            "dragstart": (function(_this) {
              return function(e) {
                return _this.emit("dragstart", e);
              };
            })(this),
            "dragenter": (function(_this) {
              return function(e) {
                noPropagation(e);
                return _this.emit("dragenter", e);
              };
            })(this),
            "dragover": (function(_this) {
              return function(e) {
                var efct;
                try {
                  efct = e.dataTransfer.effectAllowed;
                } catch (_error) {}
                e.dataTransfer.dropEffect = 'move' === efct || 'linkMove' === efct ? 'move' : 'copy';
                noPropagation(e);
                return _this.emit("dragover", e);
              };
            })(this),
            "dragleave": (function(_this) {
              return function(e) {
                return _this.emit("dragleave", e);
              };
            })(this),
            "drop": (function(_this) {
              return function(e) {
                noPropagation(e);
                return _this.drop(e);
              };
            })(this),
            "dragend": (function(_this) {
              return function(e) {
                return _this.emit("dragend", e);
              };
            })(this)
          }
        }
      ];
      this.clickableElements.forEach((function(_this) {
        return function(clickableElement) {
          return _this.listeners.push({
            element: clickableElement,
            events: {
              "click": function(evt) {
                if ((clickableElement !== _this.element) || (evt.target === _this.element || Dropzone.elementInside(evt.target, _this.element.querySelector(".dz-message")))) {
                  _this.hiddenFileInput.click();
                }
                return true;
              }
            }
          });
        };
      })(this));
      this.enable();
      return this.options.init.call(this);
    };

    Dropzone.prototype.destroy = function() {
      var _ref;
      this.disable();
      this.removeAllFiles(true);
      if ((_ref = this.hiddenFileInput) != null ? _ref.parentNode : void 0) {
        this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput);
        this.hiddenFileInput = null;
      }
      delete this.element.dropzone;
      return Dropzone.instances.splice(Dropzone.instances.indexOf(this), 1);
    };

    Dropzone.prototype.updateTotalUploadProgress = function() {
      var activeFiles, file, totalBytes, totalBytesSent, totalUploadProgress, _i, _len, _ref;
      totalBytesSent = 0;
      totalBytes = 0;
      activeFiles = this.getActiveFiles();
      if (activeFiles.length) {
        _ref = this.getActiveFiles();
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
          file = _ref[_i];
          totalBytesSent += file.upload.bytesSent;
          totalBytes += file.upload.total;
        }
        totalUploadProgress = 100 * totalBytesSent / totalBytes;
      } else {
        totalUploadProgress = 100;
      }
      return this.emit("totaluploadprogress", totalUploadProgress, totalBytes, totalBytesSent);
    };

    Dropzone.prototype._getParamName = function(n) {
      if (typeof this.options.paramName === "function") {
        return this.options.paramName(n);
      } else {
        return "" + this.options.paramName + (this.options.uploadMultiple ? "[" + n + "]" : "");
      }
    };

    Dropzone.prototype.getFallbackForm = function() {
      var existingFallback, fields, fieldsString, form;
      if (existingFallback = this.getExistingFallback()) {
        return existingFallback;
      }
      fieldsString = "<div class=\"dz-fallback\">";
      if (this.options.dictFallbackText) {
        fieldsString += "<p>" + this.options.dictFallbackText + "</p>";
      }
      fieldsString += "<input type=\"file\" name=\"" + (this._getParamName(0)) + "\" " + (this.options.uploadMultiple ? 'multiple="multiple"' : void 0) + " /><input type=\"submit\" value=\"Upload!\"></div>";
      fields = Dropzone.createElement(fieldsString);
      if (this.element.tagName !== "FORM") {
        form = Dropzone.createElement("<form action=\"" + this.options.url + "\" enctype=\"multipart/form-data\" method=\"" + this.options.method + "\"></form>");
        form.appendChild(fields);
      } else {
        this.element.setAttribute("enctype", "multipart/form-data");
        this.element.setAttribute("method", this.options.method);
      }
      return form != null ? form : fields;
    };

    Dropzone.prototype.getExistingFallback = function() {
      var fallback, getFallback, tagName, _i, _len, _ref;
      getFallback = function(elements) {
        var el, _i, _len;
        for (_i = 0, _len = elements.length; _i < _len; _i++) {
          el = elements[_i];
          if (/(^| )fallback($| )/.test(el.className)) {
            return el;
          }
        }
      };
      _ref = ["div", "form"];
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        tagName = _ref[_i];
        if (fallback = getFallback(this.element.getElementsByTagName(tagName))) {
          return fallback;
        }
      }
    };

    Dropzone.prototype.setupEventListeners = function() {
      var elementListeners, event, listener, _i, _len, _ref, _results;
      _ref = this.listeners;
      _results = [];
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        elementListeners = _ref[_i];
        _results.push((function() {
          var _ref1, _results1;
          _ref1 = elementListeners.events;
          _results1 = [];
          for (event in _ref1) {
            listener = _ref1[event];
            _results1.push(elementListeners.element.addEventListener(event, listener, false));
          }
          return _results1;
        })());
      }
      return _results;
    };

    Dropzone.prototype.removeEventListeners = function() {
      var elementListeners, event, listener, _i, _len, _ref, _results;
      _ref = this.listeners;
      _results = [];
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        elementListeners = _ref[_i];
        _results.push((function() {
          var _ref1, _results1;
          _ref1 = elementListeners.events;
          _results1 = [];
          for (event in _ref1) {
            listener = _ref1[event];
            _results1.push(elementListeners.element.removeEventListener(event, listener, false));
          }
          return _results1;
        })());
      }
      return _results;
    };

    Dropzone.prototype.disable = function() {
      var file, _i, _len, _ref, _results;
      this.clickableElements.forEach(function(element) {
        return element.classList.remove("dz-clickable");
      });
      this.removeEventListeners();
      _ref = this.files;
      _results = [];
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        file = _ref[_i];
        _results.push(this.cancelUpload(file));
      }
      return _results;
    };

    Dropzone.prototype.enable = function() {
      this.clickableElements.forEach(function(element) {
        return element.classList.add("dz-clickable");
      });
      return this.setupEventListeners();
    };

    Dropzone.prototype.filesize = function(size) {
      var cutoff, i, selectedSize, selectedUnit, unit, units, _i, _len;
      selectedSize = 0;
      selectedUnit = "b";
      if (size > 0) {
        units = ['TB', 'GB', 'MB', 'KB', 'b'];
        for (i = _i = 0, _len = units.length; _i < _len; i = ++_i) {
          unit = units[i];
          cutoff = Math.pow(this.options.filesizeBase, 4 - i) / 10;
          if (size >= cutoff) {
            selectedSize = size / Math.pow(this.options.filesizeBase, 4 - i);
            selectedUnit = unit;
            break;
          }
        }
        selectedSize = Math.round(10 * selectedSize) / 10;
      }
      return "<strong>" + selectedSize + "</strong> " + selectedUnit;
    };

    Dropzone.prototype._updateMaxFilesReachedClass = function() {
      if ((this.options.maxFiles != null) && this.getAcceptedFiles().length >= this.options.maxFiles) {
        if (this.getAcceptedFiles().length === this.options.maxFiles) {
          this.emit('maxfilesreached', this.files);
        }
        return this.element.classList.add("dz-max-files-reached");
      } else {
        return this.element.classList.remove("dz-max-files-reached");
      }
    };

    Dropzone.prototype.drop = function(e) {
      var files, items;
      if (!e.dataTransfer) {
        return;
      }
      this.emit("drop", e);
      files = e.dataTransfer.files;
      this.emit("addedfiles", files);
      if (files.length) {
        items = e.dataTransfer.items;
        if (items && items.length && (items[0].webkitGetAsEntry != null)) {
          this._addFilesFromItems(items);
        } else {
          this.handleFiles(files);
        }
      }
    };

    Dropzone.prototype.paste = function(e) {
      var items, _ref;
      if ((e != null ? (_ref = e.clipboardData) != null ? _ref.items : void 0 : void 0) == null) {
        return;
      }
      this.emit("paste", e);
      items = e.clipboardData.items;
      if (items.length) {
        return this._addFilesFromItems(items);
      }
    };

    Dropzone.prototype.handleFiles = function(files) {
      var file, _i, _len, _results;
      _results = [];
      for (_i = 0, _len = files.length; _i < _len; _i++) {
        file = files[_i];
        _results.push(this.addFile(file));
      }
      return _results;
    };

    Dropzone.prototype._addFilesFromItems = function(items) {
      var entry, item, _i, _len, _results;
      _results = [];
      for (_i = 0, _len = items.length; _i < _len; _i++) {
        item = items[_i];
        if ((item.webkitGetAsEntry != null) && (entry = item.webkitGetAsEntry())) {
          if (entry.isFile) {
            _results.push(this.addFile(item.getAsFile()));
          } else if (entry.isDirectory) {
            _results.push(this._addFilesFromDirectory(entry, entry.name));
          } else {
            _results.push(void 0);
          }
        } else if (item.getAsFile != null) {
          if ((item.kind == null) || item.kind === "file") {
            _results.push(this.addFile(item.getAsFile()));
          } else {
            _results.push(void 0);
          }
        } else {
          _results.push(void 0);
        }
      }
      return _results;
    };

    Dropzone.prototype._addFilesFromDirectory = function(directory, path) {
      var dirReader, entriesReader;
      dirReader = directory.createReader();
      entriesReader = (function(_this) {
        return function(entries) {
          var entry, _i, _len;
          for (_i = 0, _len = entries.length; _i < _len; _i++) {
            entry = entries[_i];
            if (entry.isFile) {
              entry.file(function(file) {
                if (_this.options.ignoreHiddenFiles && file.name.substring(0, 1) === '.') {
                  return;
                }
                file.fullPath = "" + path + "/" + file.name;
                return _this.addFile(file);
              });
            } else if (entry.isDirectory) {
              _this._addFilesFromDirectory(entry, "" + path + "/" + entry.name);
            }
          }
        };
      })(this);
      return dirReader.readEntries(entriesReader, function(error) {
        return typeof console !== "undefined" && console !== null ? typeof console.log === "function" ? console.log(error) : void 0 : void 0;
      });
    };

    Dropzone.prototype.accept = function(file, done) {
      if (file.size > this.options.maxFilesize * 1024 * 1024) {
        return done(this.options.dictFileTooBig.replace("{{filesize}}", Math.round(file.size / 1024 / 10.24) / 100).replace("{{maxFilesize}}", this.options.maxFilesize));
      } else if (!Dropzone.isValidFile(file, this.options.acceptedFiles)) {
        return done(this.options.dictInvalidFileType);
      } else if ((this.options.maxFiles != null) && this.getAcceptedFiles().length >= this.options.maxFiles) {
        done(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}", this.options.maxFiles));
        return this.emit("maxfilesexceeded", file);
      } else {
        return this.options.accept.call(this, file, done);
      }
    };

    Dropzone.prototype.addFile = function(file) {
      file.upload = {
        progress: 0,
        total: file.size,
        bytesSent: 0
      };
      this.files.push(file);
      file.status = Dropzone.ADDED;
      this.emit("addedfile", file);
      this._enqueueThumbnail(file);
      return this.accept(file, (function(_this) {
        return function(error) {
          if (error) {
            file.accepted = false;
            _this._errorProcessing([file], error);
          } else {
            file.accepted = true;
            if (_this.options.autoQueue) {
              _this.enqueueFile(file);
            }
          }
          return _this._updateMaxFilesReachedClass();
        };
      })(this));
    };

    Dropzone.prototype.enqueueFiles = function(files) {
      var file, _i, _len;
      for (_i = 0, _len = files.length; _i < _len; _i++) {
        file = files[_i];
        this.enqueueFile(file);
      }
      return null;
    };

    Dropzone.prototype.enqueueFile = function(file) {
      if (file.status === Dropzone.ADDED && file.accepted === true) {
        file.status = Dropzone.QUEUED;
        if (this.options.autoProcessQueue) {
          return setTimeout(((function(_this) {
            return function() {
              return _this.processQueue();
            };
          })(this)), 0);
        }
      } else {
        throw new Error("This file can't be queued because it has already been processed or was rejected.");
      }
    };

    Dropzone.prototype._thumbnailQueue = [];

    Dropzone.prototype._processingThumbnail = false;

    Dropzone.prototype._enqueueThumbnail = function(file) {
      if (this.options.createImageThumbnails && file.type.match(/image.*/) && file.size <= this.options.maxThumbnailFilesize * 1024 * 1024) {
        this._thumbnailQueue.push(file);
        return setTimeout(((function(_this) {
          return function() {
            return _this._processThumbnailQueue();
          };
        })(this)), 0);
      }
    };

    Dropzone.prototype._processThumbnailQueue = function() {
      if (this._processingThumbnail || this._thumbnailQueue.length === 0) {
        return;
      }
      this._processingThumbnail = true;
      return this.createThumbnail(this._thumbnailQueue.shift(), (function(_this) {
        return function() {
          _this._processingThumbnail = false;
          return _this._processThumbnailQueue();
        };
      })(this));
    };

    Dropzone.prototype.removeFile = function(file) {
      if (file.status === Dropzone.UPLOADING) {
        this.cancelUpload(file);
      }
      this.files = without(this.files, file);
      this.emit("removedfile", file);
      if (this.files.length === 0) {
        return this.emit("reset");
      }
    };

    Dropzone.prototype.removeAllFiles = function(cancelIfNecessary) {
      var file, _i, _len, _ref;
      if (cancelIfNecessary == null) {
        cancelIfNecessary = false;
      }
      _ref = this.files.slice();
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        file = _ref[_i];
        if (file.status !== Dropzone.UPLOADING || cancelIfNecessary) {
          this.removeFile(file);
        }
      }
      return null;
    };

    Dropzone.prototype.createThumbnail = function(file, callback) {
      var fileReader;
      fileReader = new FileReader;
      fileReader.onload = (function(_this) {
        return function() {
          if (file.type === "image/svg+xml") {
            _this.emit("thumbnail", file, fileReader.result);
            if (callback != null) {
              callback();
            }
            return;
          }
          return _this.createThumbnailFromUrl(file, fileReader.result, callback);
        };
      })(this);
      return fileReader.readAsDataURL(file);
    };

    Dropzone.prototype.createThumbnailFromUrl = function(file, imageUrl, callback, crossOrigin) {
      var img;
      img = document.createElement("img");
      if (crossOrigin) {
        img.crossOrigin = crossOrigin;
      }
      img.onload = (function(_this) {
        return function() {
          var canvas, ctx, resizeInfo, thumbnail, _ref, _ref1, _ref2, _ref3;
          file.width = img.width;
          file.height = img.height;
          resizeInfo = _this.options.resize.call(_this, file);
          if (resizeInfo.trgWidth == null) {
            resizeInfo.trgWidth = resizeInfo.optWidth;
          }
          if (resizeInfo.trgHeight == null) {
            resizeInfo.trgHeight = resizeInfo.optHeight;
          }
          canvas = document.createElement("canvas");
          ctx = canvas.getContext("2d");
          canvas.width = resizeInfo.trgWidth;
          canvas.height = resizeInfo.trgHeight;
          drawImageIOSFix(ctx, img, (_ref = resizeInfo.srcX) != null ? _ref : 0, (_ref1 = resizeInfo.srcY) != null ? _ref1 : 0, resizeInfo.srcWidth, resizeInfo.srcHeight, (_ref2 = resizeInfo.trgX) != null ? _ref2 : 0, (_ref3 = resizeInfo.trgY) != null ? _ref3 : 0, resizeInfo.trgWidth, resizeInfo.trgHeight);
          thumbnail = canvas.toDataURL("image/png");
          _this.emit("thumbnail", file, thumbnail);
          if (callback != null) {
            return callback();
          }
        };
      })(this);
      if (callback != null) {
        img.onerror = callback;
      }
      return img.src = imageUrl;
    };

    Dropzone.prototype.processQueue = function() {
      var i, parallelUploads, processingLength, queuedFiles;
      parallelUploads = this.options.parallelUploads;
      processingLength = this.getUploadingFiles().length;
      i = processingLength;
      if (processingLength >= parallelUploads) {
        return;
      }
      queuedFiles = this.getQueuedFiles();
      if (!(queuedFiles.length > 0)) {
        return;
      }
      if (this.options.uploadMultiple) {
        return this.processFiles(queuedFiles.slice(0, parallelUploads - processingLength));
      } else {
        while (i < parallelUploads) {
          if (!queuedFiles.length) {
            return;
          }
          this.processFile(queuedFiles.shift());
          i++;
        }
      }
    };

    Dropzone.prototype.processFile = function(file) {
      return this.processFiles([file]);
    };

    Dropzone.prototype.processFiles = function(files) {
      var file, _i, _len;
      for (_i = 0, _len = files.length; _i < _len; _i++) {
        file = files[_i];
        file.processing = true;
        file.status = Dropzone.UPLOADING;
        this.emit("processing", file);
      }
      if (this.options.uploadMultiple) {
        this.emit("processingmultiple", files);
      }
      return this.uploadFiles(files);
    };

    Dropzone.prototype._getFilesWithXhr = function(xhr) {
      var file, files;
      return files = (function() {
        var _i, _len, _ref, _results;
        _ref = this.files;
        _results = [];
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
          file = _ref[_i];
          if (file.xhr === xhr) {
            _results.push(file);
          }
        }
        return _results;
      }).call(this);
    };

    Dropzone.prototype.cancelUpload = function(file) {
      var groupedFile, groupedFiles, _i, _j, _len, _len1, _ref;
      if (file.status === Dropzone.UPLOADING) {
        groupedFiles = this._getFilesWithXhr(file.xhr);
        for (_i = 0, _len = groupedFiles.length; _i < _len; _i++) {
          groupedFile = groupedFiles[_i];
          groupedFile.status = Dropzone.CANCELED;
        }
        file.xhr.abort();
        for (_j = 0, _len1 = groupedFiles.length; _j < _len1; _j++) {
          groupedFile = groupedFiles[_j];
          this.emit("canceled", groupedFile);
        }
        if (this.options.uploadMultiple) {
          this.emit("canceledmultiple", groupedFiles);
        }
      } else if ((_ref = file.status) === Dropzone.ADDED || _ref === Dropzone.QUEUED) {
        file.status = Dropzone.CANCELED;
        this.emit("canceled", file);
        if (this.options.uploadMultiple) {
          this.emit("canceledmultiple", [file]);
        }
      }
      if (this.options.autoProcessQueue) {
        return this.processQueue();
      }
    };

    resolveOption = function() {
      var args, option;
      option = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
      if (typeof option === 'function') {
        return option.apply(this, args);
      }
      return option;
    };

    Dropzone.prototype.uploadFile = function(file) {
      return this.uploadFiles([file]);
    };

    Dropzone.prototype.uploadFiles = function(files) {
      var file, formData, handleError, headerName, headerValue, headers, i, input, inputName, inputType, key, method, option, progressObj, response, updateProgress, url, value, xhr, _i, _j, _k, _l, _len, _len1, _len2, _len3, _m, _ref, _ref1, _ref2, _ref3, _ref4, _ref5;
      xhr = new XMLHttpRequest();
      for (_i = 0, _len = files.length; _i < _len; _i++) {
        file = files[_i];
        file.xhr = xhr;
      }
      method = resolveOption(this.options.method, files);
      url = resolveOption(this.options.url, files);
      xhr.open(method, url, true);
      xhr.withCredentials = !!this.options.withCredentials;
      response = null;
      handleError = (function(_this) {
        return function() {
          var _j, _len1, _results;
          _results = [];
          for (_j = 0, _len1 = files.length; _j < _len1; _j++) {
            file = files[_j];
            _results.push(_this._errorProcessing(files, response || _this.options.dictResponseError.replace("{{statusCode}}", xhr.status), xhr));
          }
          return _results;
        };
      })(this);
      updateProgress = (function(_this) {
        return function(e) {
          var allFilesFinished, progress, _j, _k, _l, _len1, _len2, _len3, _results;
          if (e != null) {
            progress = 100 * e.loaded / e.total;
            for (_j = 0, _len1 = files.length; _j < _len1; _j++) {
              file = files[_j];
              file.upload = {
                progress: progress,
                total: e.total,
                bytesSent: e.loaded
              };
            }
          } else {
            allFilesFinished = true;
            progress = 100;
            for (_k = 0, _len2 = files.length; _k < _len2; _k++) {
              file = files[_k];
              if (!(file.upload.progress === 100 && file.upload.bytesSent === file.upload.total)) {
                allFilesFinished = false;
              }
              file.upload.progress = progress;
              file.upload.bytesSent = file.upload.total;
            }
            if (allFilesFinished) {
              return;
            }
          }
          _results = [];
          for (_l = 0, _len3 = files.length; _l < _len3; _l++) {
            file = files[_l];
            _results.push(_this.emit("uploadprogress", file, progress, file.upload.bytesSent));
          }
          return _results;
        };
      })(this);
      xhr.onload = (function(_this) {
        return function(e) {
          var _ref;
          if (files[0].status === Dropzone.CANCELED) {
            return;
          }
          if (xhr.readyState !== 4) {
            return;
          }
          response = xhr.responseText;
          if (xhr.getResponseHeader("content-type") && ~xhr.getResponseHeader("content-type").indexOf("application/json")) {
            try {
              response = JSON.parse(response);
            } catch (_error) {
              e = _error;
              response = "Invalid JSON response from server.";
            }
          }
          updateProgress();
          if (!((200 <= (_ref = xhr.status) && _ref < 300))) {
            return handleError();
          } else {
            return _this._finished(files, response, e);
          }
        };
      })(this);
      xhr.onerror = (function(_this) {
        return function() {
          if (files[0].status === Dropzone.CANCELED) {
            return;
          }
          return handleError();
        };
      })(this);
      progressObj = (_ref = xhr.upload) != null ? _ref : xhr;
      progressObj.onprogress = updateProgress;
      headers = {
        "Accept": "application/json",
        "Cache-Control": "no-cache",
        "X-Requested-With": "XMLHttpRequest",
        "X-CSRF-Token": $('#csrftoken').val()
      };
      if (this.options.headers) {
        extend(headers, this.options.headers);
      }
      for (headerName in headers) {
        headerValue = headers[headerName];
        if (headerValue) {
          xhr.setRequestHeader(headerName, headerValue);
        }
      }
      formData = new FormData();
      if (this.options.params) {
        _ref1 = this.options.params;
        for (key in _ref1) {
          value = _ref1[key];
          formData.append(key, value);
        }
      }
      for (_j = 0, _len1 = files.length; _j < _len1; _j++) {
        file = files[_j];
        this.emit("sending", file, xhr, formData);
      }
      if (this.options.uploadMultiple) {
        this.emit("sendingmultiple", files, xhr, formData);
      }
      if (this.element.tagName === "FORM") {
        _ref2 = this.element.querySelectorAll("input, textarea, select, button");
        for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
          input = _ref2[_k];
          inputName = input.getAttribute("name");
          inputType = input.getAttribute("type");
          if (input.tagName === "SELECT" && input.hasAttribute("multiple")) {
            _ref3 = input.options;
            for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) {
              option = _ref3[_l];
              if (option.selected) {
                formData.append(inputName, option.value);
              }
            }
          } else if (!inputType || ((_ref4 = inputType.toLowerCase()) !== "checkbox" && _ref4 !== "radio") || input.checked) {
            formData.append(inputName, input.value);
          }
        }
      }
      for (i = _m = 0, _ref5 = files.length - 1; 0 <= _ref5 ? _m <= _ref5 : _m >= _ref5; i = 0 <= _ref5 ? ++_m : --_m) {
        formData.append(this._getParamName(i), files[i], files[i].name);
      }
      return this.submitRequest(xhr, formData, files);
    };

    Dropzone.prototype.submitRequest = function(xhr, formData, files) {
      return xhr.send(formData);
    };

    Dropzone.prototype._finished = function(files, responseText, e) {
      var file, _i, _len;
      for (_i = 0, _len = files.length; _i < _len; _i++) {
        file = files[_i];
        file.status = Dropzone.SUCCESS;
        this.emit("success", file, responseText, e);
        this.emit("complete", file);
      }
      if (this.options.uploadMultiple) {
        this.emit("successmultiple", files, responseText, e);
        this.emit("completemultiple", files);
      }
      if (this.options.autoProcessQueue) {
        return this.processQueue();
      }
    };

    Dropzone.prototype._errorProcessing = function(files, message, xhr) {
      var file, _i, _len;
      for (_i = 0, _len = files.length; _i < _len; _i++) {
        file = files[_i];
        file.status = Dropzone.ERROR;
        this.emit("error", file, message, xhr);
        this.emit("complete", file);
      }
      if (this.options.uploadMultiple) {
        this.emit("errormultiple", files, message, xhr);
        this.emit("completemultiple", files);
      }
      if (this.options.autoProcessQueue) {
        return this.processQueue();
      }
    };

    return Dropzone;

  })(Emitter);

  Dropzone.version = "4.2.0";

  Dropzone.options = {};

  Dropzone.optionsForElement = function(element) {
    if (element.getAttribute("id")) {
      return Dropzone.options[camelize(element.getAttribute("id"))];
    } else {
      return void 0;
    }
  };

  Dropzone.instances = [];

  Dropzone.forElement = function(element) {
    if (typeof element === "string") {
      element = document.querySelector(element);
    }
    if ((element != null ? element.dropzone : void 0) == null) {
      throw new Error("No Dropzone found for given element. This is probably because you're trying to access it before Dropzone had the time to initialize. Use the `init` option to setup any additional observers on your Dropzone.");
    }
    return element.dropzone;
  };

  Dropzone.autoDiscover = true;

  Dropzone.discover = function() {
    var checkElements, dropzone, dropzones, _i, _len, _results;
    if (document.querySelectorAll) {
      dropzones = document.querySelectorAll(".dropzone");
    } else {
      dropzones = [];
      checkElements = function(elements) {
        var el, _i, _len, _results;
        _results = [];
        for (_i = 0, _len = elements.length; _i < _len; _i++) {
          el = elements[_i];
          if (/(^| )dropzone($| )/.test(el.className)) {
            _results.push(dropzones.push(el));
          } else {
            _results.push(void 0);
          }
        }
        return _results;
      };
      checkElements(document.getElementsByTagName("div"));
      checkElements(document.getElementsByTagName("form"));
    }
    _results = [];
    for (_i = 0, _len = dropzones.length; _i < _len; _i++) {
      dropzone = dropzones[_i];
      if (Dropzone.optionsForElement(dropzone) !== false) {
        _results.push(new Dropzone(dropzone));
      } else {
        _results.push(void 0);
      }
    }
    return _results;
  };

  Dropzone.blacklistedBrowsers = [/opera.*Macintosh.*version\/12/i];

  Dropzone.isBrowserSupported = function() {
    var capableBrowser, regex, _i, _len, _ref;
    capableBrowser = true;
    if (window.File && window.FileReader && window.FileList && window.Blob && window.FormData && document.querySelector) {
      if (!("classList" in document.createElement("a"))) {
        capableBrowser = false;
      } else {
        _ref = Dropzone.blacklistedBrowsers;
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
          regex = _ref[_i];
          if (regex.test(navigator.userAgent)) {
            capableBrowser = false;
            continue;
          }
        }
      }
    } else {
      capableBrowser = false;
    }
    return capableBrowser;
  };

  without = function(list, rejectedItem) {
    var item, _i, _len, _results;
    _results = [];
    for (_i = 0, _len = list.length; _i < _len; _i++) {
      item = list[_i];
      if (item !== rejectedItem) {
        _results.push(item);
      }
    }
    return _results;
  };

  camelize = function(str) {
    return str.replace(/[\-_](\w)/g, function(match) {
      return match.charAt(1).toUpperCase();
    });
  };

  Dropzone.createElement = function(string) {
    var div;
    div = document.createElement("div");
    div.innerHTML = string;
    return div.childNodes[0];
  };

  Dropzone.elementInside = function(element, container) {
    if (element === container) {
      return true;
    }
    while (element = element.parentNode) {
      if (element === container) {
        return true;
      }
    }
    return false;
  };

  Dropzone.getElement = function(el, name) {
    var element;
    if (typeof el === "string") {
      element = document.querySelector(el);
    } else if (el.nodeType != null) {
      element = el;
    }
    if (element == null) {
      throw new Error("Invalid `" + name + "` option provided. Please provide a CSS selector or a plain HTML element.");
    }
    return element;
  };

  Dropzone.getElements = function(els, name) {
    var e, el, elements, _i, _j, _len, _len1, _ref;
    if (els instanceof Array) {
      elements = [];
      try {
        for (_i = 0, _len = els.length; _i < _len; _i++) {
          el = els[_i];
          elements.push(this.getElement(el, name));
        }
      } catch (_error) {
        e = _error;
        elements = null;
      }
    } else if (typeof els === "string") {
      elements = [];
      _ref = document.querySelectorAll(els);
      for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
        el = _ref[_j];
        elements.push(el);
      }
    } else if (els.nodeType != null) {
      elements = [els];
    }
    if (!((elements != null) && elements.length)) {
      throw new Error("Invalid `" + name + "` option provided. Please provide a CSS selector, a plain HTML element or a list of those.");
    }
    return elements;
  };

  Dropzone.confirm = function(question, accepted, rejected) {
    if (window.confirm(question)) {
      return accepted();
    } else if (rejected != null) {
      return rejected();
    }
  };

  Dropzone.isValidFile = function(file, acceptedFiles) {
    var baseMimeType, mimeType, validType, _i, _len;
    if (!acceptedFiles) {
      return true;
    }
    acceptedFiles = acceptedFiles.split(",");
    mimeType = file.type;
    baseMimeType = mimeType.replace(/\/.*$/, "");
    for (_i = 0, _len = acceptedFiles.length; _i < _len; _i++) {
      validType = acceptedFiles[_i];
      validType = validType.trim();
      if (validType.charAt(0) === ".") {
        if (file.name.toLowerCase().indexOf(validType.toLowerCase(), file.name.length - validType.length) !== -1) {
          return true;
        }
      } else if (/\/\*$/.test(validType)) {
        if (baseMimeType === validType.replace(/\/.*$/, "")) {
          return true;
        }
      } else {
        if (mimeType === validType) {
          return true;
        }
      }
    }
    return false;
  };

  if (typeof jQuery !== "undefined" && jQuery !== null) {
    jQuery.fn.dropzone = function(options) {
      return this.each(function() {
        return new Dropzone(this, options);
      });
    };
  }

  if (typeof module !== "undefined" && module !== null) {
    module.exports = Dropzone;
  } else {
    window.Dropzone = Dropzone;
  }

  Dropzone.ADDED = "added";

  Dropzone.QUEUED = "queued";

  Dropzone.ACCEPTED = Dropzone.QUEUED;

  Dropzone.UPLOADING = "uploading";

  Dropzone.PROCESSING = Dropzone.UPLOADING;

  Dropzone.CANCELED = "canceled";

  Dropzone.ERROR = "error";

  Dropzone.SUCCESS = "success";


  /*
  
  Bugfix for iOS 6 and 7
  Source: http://stackoverflow.com/questions/11929099/html5-canvas-drawimage-ratio-bug-ios
  based on the work of https://github.com/stomita/ios-imagefile-megapixel
   */

  detectVerticalSquash = function(img) {
    var alpha, canvas, ctx, data, ey, ih, iw, py, ratio, sy;
    iw = img.naturalWidth;
    ih = img.naturalHeight;
    canvas = document.createElement("canvas");
    canvas.width = 1;
    canvas.height = ih;
    ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0);
    data = ctx.getImageData(0, 0, 1, ih).data;
    sy = 0;
    ey = ih;
    py = ih;
    while (py > sy) {
      alpha = data[(py - 1) * 4 + 3];
      if (alpha === 0) {
        ey = py;
      } else {
        sy = py;
      }
      py = (ey + sy) >> 1;
    }
    ratio = py / ih;
    if (ratio === 0) {
      return 1;
    } else {
      return ratio;
    }
  };

  drawImageIOSFix = function(ctx, img, sx, sy, sw, sh, dx, dy, dw, dh) {
    var vertSquashRatio;
    vertSquashRatio = detectVerticalSquash(img);
    return ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh / vertSquashRatio);
  };


  /*
   * contentloaded.js
   *
   * Author: Diego Perini (diego.perini at gmail.com)
   * Summary: cross-browser wrapper for DOMContentLoaded
   * Updated: 20101020
   * License: MIT
   * Version: 1.2
   *
   * URL:
   * http://javascript.nwbox.com/ContentLoaded/
   * http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE
   */

  contentLoaded = function(win, fn) {
    var add, doc, done, init, poll, pre, rem, root, top;
    done = false;
    top = true;
    doc = win.document;
    root = doc.documentElement;
    add = (doc.addEventListener ? "addEventListener" : "attachEvent");
    rem = (doc.addEventListener ? "removeEventListener" : "detachEvent");
    pre = (doc.addEventListener ? "" : "on");
    init = function(e) {
      if (e.type === "readystatechange" && doc.readyState !== "complete") {
        return;
      }
      (e.type === "load" ? win : doc)[rem](pre + e.type, init, false);
      if (!done && (done = true)) {
        return fn.call(win, e.type || e);
      }
    };
    poll = function() {
      var e;
      try {
        root.doScroll("left");
      } catch (_error) {
        e = _error;
        setTimeout(poll, 50);
        return;
      }
      return init("poll");
    };
    if (doc.readyState !== "complete") {
      if (doc.createEventObject && root.doScroll) {
        try {
          top = !win.frameElement;
        } catch (_error) {}
        if (top) {
          poll();
        }
      }
      doc[add](pre + "DOMContentLoaded", init, false);
      doc[add](pre + "readystatechange", init, false);
      return win[add](pre + "load", init, false);
    }
  };

  Dropzone._autoDiscoverFunction = function() {
    if (Dropzone.autoDiscover) {
      return Dropzone.discover();
    }
  };

  contentLoaded(window, Dropzone._autoDiscoverFunction);

}).call(this);
};


modules['helpcenter/primary/router'] = function(require, exports, module, modules) {
'use strict';

var app = app || {};

// Load views
var VALIDATE      = require('helpcenter/primary/views/validate'),
	RELATEDARTICLES = require('helpcenter/primary/views/relatedarticles'),
	VOTE            = require('helpcenter/primary/views/vote'),
	COMMENTLIKES    = require('helpcenter/primary/views/likes'),
	ADDCOMMENT      = require('helpcenter/primary/views/comment'),
	SIGNIN          = require('helpcenter/primary/views/signin'),
	ACCOUNT         = require('helpcenter/primary/views/account'),
	PROFILE         = require('helpcenter/primary/views/profile'),
	ADDEMAIL        = require('helpcenter/primary/views/addemail'),
	SEARCH          = require('helpcenter/primary/views/search'),
	FILEINPUT       = require('helpcenter/primary/views/fileinput'),
	DRILLDOWN       = require('helpcenter/primary/views/drilldown'),
	TOGGLE          = require('helpcenter/primary/views/toggle'),
	SURVEY          = require('helpcenter/primary/views/survey'),
	RECAPTCHA       = require('helpcenter/primary/views/recaptcha'),
	UNSAVED         = require('helpcenter/primary/views/unsaved'),
	FOCUSFIELD      = require('helpcenter/primary/views/focusfield'),
	PASSWORD        = require('helpcenter/primary/views/password'),
	TIMEZONE        = require('helpcenter/primary/views/autotimezone'),
	SELECTFORM		= require('helpcenter/primary/views/select-form'),

	// Load views for help center management
	ARTICLES        = require('helpcenter/primary/views/manage/article'),
	AUTHOR          = require('helpcenter/primary/views/manage/author'),
	SECTION         = require('helpcenter/primary/views/manage/section'),
	HOME            = require('helpcenter/primary/views/manage/home'),
	CATEGORY        = require('helpcenter/primary/views/manage/category'),
	CUSTOMIZE       = require('helpcenter/primary/views/customize');

app.router = Backbone.Router.extend({
	routes: {
		// Search
		'(*locale)search/:filter(?)(utf8=✓&)(query=:query)': 'search',

		// Content
		'(*locale)category/(*anything)': 'category',
		'(*locale)section/(*anything)': 'section',
		'(*locale)article/(*anything)': 'article',

		// Cases
		'(*locale)case/create(/:id)(/)(*anything)': 'caseCreate',
		'(*locale)case/view/:id(/)(*anything)': 'caseView',
		'(*locale)case/reply/:id(/)(*anything)': 'caseView',
		'(*locale)case/rating/(*anything)': 'survey',

		'(*locale)request/new(/:id)(/)(*anything)': 'caseCreate',
		'(*locale)request/view/:id(/)(*anything)': 'caseView',
		'(*locale)request/reply/:id(/)(*anything)': 'caseView',
		'(*locale)request/rating/(*anything)': 'survey',

		'(*locale)conversation/new(/:id)(/)(*anything)': 'caseCreate',
		'(*locale)conversation/view/:id(/)(*anything)': 'caseView',
		'(*locale)conversation/reply/:id(/)(*anything)': 'caseView',
		'(*locale)conversation/rating/(*anything)': 'survey',

		// User account
		'(*locale)login(/)(/regular)': 'signin',
		'(*locale)login/google(/)': 'signin',
		'(*locale)register(/)(/doregister)': 'register',
		'(*locale)signup(/)(/doregister)': 'register',
		'(*locale)profile(/)(*anything)': 'profile',
		'(*locale)account/forgotpassword(/)': 'forgot',
		'(*locale)account/setpassword/:id(/)': 'account',
		'(*locale)Auth/ResetPassword/:id(/)': 'account'
	},

	initialize: function() {
		// Validate: search field is not empty
		new VALIDATE({ el: $('.js-search-view') });

		// Show email verification popup if required
		new ADDEMAIL();

		// Customization options
		new CUSTOMIZE();

		new HOME();

		// Add CSRF Token to all API requests
		if(!(window.location.pathname).match('/login')) {
			var defaultSync = Backbone.sync;
			Backbone.sync = function(method, model, options) {
				if (method != 'read') {
					options.beforeSend = function(xhr){
					    xhr.setRequestHeader('X-CSRF-Token', $('#csrftoken').val());
					};
				}
			    return defaultSync(method, model, options);
			};
		}
	}
});

// Instantiate the router
var hcRouter = new app.router;

hcRouter.on('route:search', function(locale, filter, query) {
	// Filter search results: All, atricles, cases
	new SEARCH({ filter: filter, query: query });
});

hcRouter.on('route:caseCreate', function() {
	// Validate required fields
	new VALIDATE({ el: $('.js-validate-view') });

	// File upload field
	new FILEINPUT();

	// Suggest similar cases in sidebar
	new RELATEDARTICLES();

	// Render any drilldown menu
	if ($('#new-conversation-view').length) {
		new DRILLDOWN({ el: $('#new-conversation-view'), endpoint: 'conversations' });
	} else {
		new DRILLDOWN({ el: $('#new-request-view'), endpoint: 'conversations' });
	}

	// Yes/No toggle
	new TOGGLE({ el: $('#contact') });

	// Warn user if there are unsaved changes
	new UNSAVED({ el: $('#contact') });

	// Checks if recaptcha is filled
	new RECAPTCHA({ el: $('.js-validate-view') });

	new SELECTFORM();
});

hcRouter.on('route:caseView', function(id) {
	// Validate required fields
	new VALIDATE({ el: $('.js-validate-view') });

	// File upload field
	new FILEINPUT();

	if ($('#view-conversation').length) {
		// Render any drilldown menu
		new DRILLDOWN({ el: $('#view-conversation'), endpoint: 'conversations' });
		// Yes/No toggle
		new TOGGLE({ el: $('#view-conversation') });
	} else {
		// Render any drilldown menu
		new DRILLDOWN({ el: $('#view-request'), endpoint: 'conversations' });
		// Yes/No toggle
		new TOGGLE({ el: $('#view-request') });
	}

	// Warn user if there are unsaved changes
	new UNSAVED({ el: $('.js-validate-view') });
});

hcRouter.on('route:category', function(locale, id) {
	// Category management
	new CATEGORY({ itemId: id });

	// Validate required fields
	new VALIDATE({ el: $('#category') });

	// Warn user if there are unsaved changes
	new UNSAVED({ el: $('#category') });
});

hcRouter.on('route:section', function(locale, id) {
	// Section management
	new SECTION({ itemId: id });

	// Validate required fields
	new VALIDATE({ el: $('#section') });

	// Warn user if there are unsaved changes
	new UNSAVED({ el: $('#section') });
});

hcRouter.on('route:article', function(locale, id) {
	// Vote for an article
	new VOTE({ articleId: id });

	// Like/unlike comments
	new COMMENTLIKES({ articleId: id });

	// Add/edit/delete comments
	new ADDCOMMENT({ articleId: id });

	// Article management
	new ARTICLES({ itemId: id });

	// Select author dropdown
	new AUTHOR();
});

hcRouter.on('route:profile', function() {
	// Validate required fields
	new VALIDATE({ el: $('.js-validate-view') });

	// File upload field
	new FILEINPUT();

	// Manage profile fields
	new PROFILE();

	// Load timezone selector
	new TIMEZONE();

	// Render any drilldown menu
	new DRILLDOWN({ el: $('#profile'), endpoint: 'profile' });

	// Yes/No toggle
	new TOGGLE({ el: $('#profile') });
});

hcRouter.on('route:validate', function() {
	// Validate required fields
	new VALIDATE({ el: $('.js-validate-view') });
});

hcRouter.on('route:signin', function() {
	// Validate required fields
	new VALIDATE({ el: $('#login-container') });

	// Handles all the sign-in process
	new SIGNIN();

	// Validate new password
	new PASSWORD({ el: $('#login-container')})
});

hcRouter.on('route:account', function() {
	// Validates password fields
	new VALIDATE({ el: $('#password-container') });

	// Manages user passwords
	new ACCOUNT();

	// Validate new password
	new PASSWORD({ el: $('#password-container')});

});

hcRouter.on('route:forgot', function() {
	// Validates password fields
	new VALIDATE({ el: $('.js-validate-view') });

	// Focus first field
	new FOCUSFIELD({ el: $('.js-validate-view') });
});

hcRouter.on('route:survey', function() {
	// Shows good/bad message
	new SURVEY();
});

hcRouter.on('route:register', function() {
	// Validate required fields
	new VALIDATE({ el: $('.js-validate-view') });

	// Checks if recaptcha is filled
	new RECAPTCHA({ el: $('.js-validate-view') });

	// Focus first field
	new FOCUSFIELD({ el: $('.js-validate-view') });
});

Backbone.history.start({ pushState: true });

};


modules['jquery'] = function(require, exports, module, modules) {
/*!
 * jQuery JavaScript Library v1.11.1
 * http://jquery.com/
 *
 * Includes Sizzle.js
 * http://sizzlejs.com/
 *
 * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors
 * Released under the MIT license
 * http://jquery.org/license
 *
 * Date: 2014-05-01T17:42Z
 */

(function( global, factory ) {

	if ( typeof module === "object" && typeof module.exports === "object" ) {
		// For CommonJS and CommonJS-like environments where a proper window is present,
		// execute the factory and get jQuery
		// For environments that do not inherently posses a window with a document
		// (such as Node.js), expose a jQuery-making factory as module.exports
		// This accentuates the need for the creation of a real window
		// e.g. var jQuery = require("jquery")(window);
		// See ticket #14549 for more info
		module.exports = global.document ?
			factory( global, true ) :
			function( w ) {
				if ( !w.document ) {
					throw new Error( "jQuery requires a window with a document" );
				}
				return factory( w );
			};
	} else {
		factory( global );
	}

// Pass this if window is not defined yet
}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {

// Can't do this because several apps including ASP.NET trace
// the stack via arguments.caller.callee and Firefox dies if
// you try to trace through "use strict" call chains. (#13335)
// Support: Firefox 18+
//

var deletedIds = [];

var slice = deletedIds.slice;

var concat = deletedIds.concat;

var push = deletedIds.push;

var indexOf = deletedIds.indexOf;

var class2type = {};

var toString = class2type.toString;

var hasOwn = class2type.hasOwnProperty;

var support = {};



var
	version = "1.11.1",

	// Define a local copy of jQuery
	jQuery = function( selector, context ) {
		// The jQuery object is actually just the init constructor 'enhanced'
		// Need init if jQuery is called (just allow error to be thrown if not included)
		return new jQuery.fn.init( selector, context );
	},

	// Support: Android<4.1, IE<9
	// Make sure we trim BOM and NBSP
	rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,

	// Matches dashed string for camelizing
	rmsPrefix = /^-ms-/,
	rdashAlpha = /-([\da-z])/gi,

	// Used by jQuery.camelCase as callback to replace()
	fcamelCase = function( all, letter ) {
		return letter.toUpperCase();
	};

jQuery.fn = jQuery.prototype = {
	// The current version of jQuery being used
	jquery: version,

	constructor: jQuery,

	// Start with an empty selector
	selector: "",

	// The default length of a jQuery object is 0
	length: 0,

	toArray: function() {
		return slice.call( this );
	},

	// Get the Nth element in the matched element set OR
	// Get the whole matched element set as a clean array
	get: function( num ) {
		return num != null ?

			// Return just the one element from the set
			( num < 0 ? this[ num + this.length ] : this[ num ] ) :

			// Return all the elements in a clean array
			slice.call( this );
	},

	// Take an array of elements and push it onto the stack
	// (returning the new matched element set)
	pushStack: function( elems ) {

		// Build a new jQuery matched element set
		var ret = jQuery.merge( this.constructor(), elems );

		// Add the old object onto the stack (as a reference)
		ret.prevObject = this;
		ret.context = this.context;

		// Return the newly-formed element set
		return ret;
	},

	// Execute a callback for every element in the matched set.
	// (You can seed the arguments with an array of args, but this is
	// only used internally.)
	each: function( callback, args ) {
		return jQuery.each( this, callback, args );
	},

	map: function( callback ) {
		return this.pushStack( jQuery.map(this, function( elem, i ) {
			return callback.call( elem, i, elem );
		}));
	},

	slice: function() {
		return this.pushStack( slice.apply( this, arguments ) );
	},

	first: function() {
		return this.eq( 0 );
	},

	last: function() {
		return this.eq( -1 );
	},

	eq: function( i ) {
		var len = this.length,
			j = +i + ( i < 0 ? len : 0 );
		return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
	},

	end: function() {
		return this.prevObject || this.constructor(null);
	},

	// For internal use only.
	// Behaves like an Array's method, not like a jQuery method.
	push: push,
	sort: deletedIds.sort,
	splice: deletedIds.splice
};

jQuery.extend = jQuery.fn.extend = function() {
	var src, copyIsArray, copy, name, options, clone,
		target = arguments[0] || {},
		i = 1,
		length = arguments.length,
		deep = false;

	// Handle a deep copy situation
	if ( typeof target === "boolean" ) {
		deep = target;

		// skip the boolean and the target
		target = arguments[ i ] || {};
		i++;
	}

	// Handle case when target is a string or something (possible in deep copy)
	if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
		target = {};
	}

	// extend jQuery itself if only one argument is passed
	if ( i === length ) {
		target = this;
		i--;
	}

	for ( ; i < length; i++ ) {
		// Only deal with non-null/undefined values
		if ( (options = arguments[ i ]) != null ) {
			// Extend the base object
			for ( name in options ) {
				src = target[ name ];
				copy = options[ name ];

				// Prevent never-ending loop
				if ( target === copy ) {
					continue;
				}

				// Recurse if we're merging plain objects or arrays
				if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
					if ( copyIsArray ) {
						copyIsArray = false;
						clone = src && jQuery.isArray(src) ? src : [];

					} else {
						clone = src && jQuery.isPlainObject(src) ? src : {};
					}

					// Never move original objects, clone them
					target[ name ] = jQuery.extend( deep, clone, copy );

				// Don't bring in undefined values
				} else if ( copy !== undefined ) {
					target[ name ] = copy;
				}
			}
		}
	}

	// Return the modified object
	return target;
};

jQuery.extend({
	// Unique for each copy of jQuery on the page
	expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),

	// Assume jQuery is ready without the ready module
	isReady: true,

	error: function( msg ) {
		throw new Error( msg );
	},

	noop: function() {},

	// See test/unit/core.js for details concerning isFunction.
	// Since version 1.3, DOM methods and functions like alert
	// aren't supported. They return false on IE (#2968).
	isFunction: function( obj ) {
		return jQuery.type(obj) === "function";
	},

	isArray: Array.isArray || function( obj ) {
		return jQuery.type(obj) === "array";
	},

	isWindow: function( obj ) {
		/* jshint eqeqeq: false */
		return obj != null && obj == obj.window;
	},

	isNumeric: function( obj ) {
		// parseFloat NaNs numeric-cast false positives (null|true|false|"")
		// ...but misinterprets leading-number strings, particularly hex literals ("0x...")
		// subtraction forces infinities to NaN
		return !jQuery.isArray( obj ) && obj - parseFloat( obj ) >= 0;
	},

	isEmptyObject: function( obj ) {
		var name;
		for ( name in obj ) {
			return false;
		}
		return true;
	},

	isPlainObject: function( obj ) {
		var key;

		// Must be an Object.
		// Because of IE, we also have to check the presence of the constructor property.
		// Make sure that DOM nodes and window objects don't pass through, as well
		if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
			return false;
		}

		try {
			// Not own constructor property must be Object
			if ( obj.constructor &&
				!hasOwn.call(obj, "constructor") &&
				!hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
				return false;
			}
		} catch ( e ) {
			// IE8,9 Will throw exceptions on certain host objects #9897
			return false;
		}

		// Support: IE<9
		// Handle iteration over inherited properties before own properties.
		if ( support.ownLast ) {
			for ( key in obj ) {
				return hasOwn.call( obj, key );
			}
		}

		// Own properties are enumerated firstly, so to speed up,
		// if last one is own, then all properties are own.
		for ( key in obj ) {}

		return key === undefined || hasOwn.call( obj, key );
	},

	type: function( obj ) {
		if ( obj == null ) {
			return obj + "";
		}
		return typeof obj === "object" || typeof obj === "function" ?
			class2type[ toString.call(obj) ] || "object" :
			typeof obj;
	},

	// Evaluates a script in a global context
	// Workarounds based on findings by Jim Driscoll
	// http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
	globalEval: function( data ) {
		if ( data && jQuery.trim( data ) ) {
			// We use execScript on Internet Explorer
			// We use an anonymous function so that context is window
			// rather than jQuery in Firefox
			( window.execScript || function( data ) {
				window[ "eval" ].call( window, data );
			} )( data );
		}
	},

	// Convert dashed to camelCase; used by the css and data modules
	// Microsoft forgot to hump their vendor prefix (#9572)
	camelCase: function( string ) {
		return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
	},

	nodeName: function( elem, name ) {
		return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
	},

	// args is for internal usage only
	each: function( obj, callback, args ) {
		var value,
			i = 0,
			length = obj.length,
			isArray = isArraylike( obj );

		if ( args ) {
			if ( isArray ) {
				for ( ; i < length; i++ ) {
					value = callback.apply( obj[ i ], args );

					if ( value === false ) {
						break;
					}
				}
			} else {
				for ( i in obj ) {
					value = callback.apply( obj[ i ], args );

					if ( value === false ) {
						break;
					}
				}
			}

		// A special, fast, case for the most common use of each
		} else {
			if ( isArray ) {
				for ( ; i < length; i++ ) {
					value = callback.call( obj[ i ], i, obj[ i ] );

					if ( value === false ) {
						break;
					}
				}
			} else {
				for ( i in obj ) {
					value = callback.call( obj[ i ], i, obj[ i ] );

					if ( value === false ) {
						break;
					}
				}
			}
		}

		return obj;
	},

	// Support: Android<4.1, IE<9
	trim: function( text ) {
		return text == null ?
			"" :
			( text + "" ).replace( rtrim, "" );
	},

	// results is for internal usage only
	makeArray: function( arr, results ) {
		var ret = results || [];

		if ( arr != null ) {
			if ( isArraylike( Object(arr) ) ) {
				jQuery.merge( ret,
					typeof arr === "string" ?
					[ arr ] : arr
				);
			} else {
				push.call( ret, arr );
			}
		}

		return ret;
	},

	inArray: function( elem, arr, i ) {
		var len;

		if ( arr ) {
			if ( indexOf ) {
				return indexOf.call( arr, elem, i );
			}

			len = arr.length;
			i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;

			for ( ; i < len; i++ ) {
				// Skip accessing in sparse arrays
				if ( i in arr && arr[ i ] === elem ) {
					return i;
				}
			}
		}

		return -1;
	},

	merge: function( first, second ) {
		var len = +second.length,
			j = 0,
			i = first.length;

		while ( j < len ) {
			first[ i++ ] = second[ j++ ];
		}

		// Support: IE<9
		// Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists)
		if ( len !== len ) {
			while ( second[j] !== undefined ) {
				first[ i++ ] = second[ j++ ];
			}
		}

		first.length = i;

		return first;
	},

	grep: function( elems, callback, invert ) {
		var callbackInverse,
			matches = [],
			i = 0,
			length = elems.length,
			callbackExpect = !invert;

		// Go through the array, only saving the items
		// that pass the validator function
		for ( ; i < length; i++ ) {
			callbackInverse = !callback( elems[ i ], i );
			if ( callbackInverse !== callbackExpect ) {
				matches.push( elems[ i ] );
			}
		}

		return matches;
	},

	// arg is for internal usage only
	map: function( elems, callback, arg ) {
		var value,
			i = 0,
			length = elems.length,
			isArray = isArraylike( elems ),
			ret = [];

		// Go through the array, translating each of the items to their new values
		if ( isArray ) {
			for ( ; i < length; i++ ) {
				value = callback( elems[ i ], i, arg );

				if ( value != null ) {
					ret.push( value );
				}
			}

		// Go through every key on the object,
		} else {
			for ( i in elems ) {
				value = callback( elems[ i ], i, arg );

				if ( value != null ) {
					ret.push( value );
				}
			}
		}

		// Flatten any nested arrays
		return concat.apply( [], ret );
	},

	// A global GUID counter for objects
	guid: 1,

	// Bind a function to a context, optionally partially applying any
	// arguments.
	proxy: function( fn, context ) {
		var args, proxy, tmp;

		if ( typeof context === "string" ) {
			tmp = fn[ context ];
			context = fn;
			fn = tmp;
		}

		// Quick check to determine if target is callable, in the spec
		// this throws a TypeError, but we will just return undefined.
		if ( !jQuery.isFunction( fn ) ) {
			return undefined;
		}

		// Simulated bind
		args = slice.call( arguments, 2 );
		proxy = function() {
			return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
		};

		// Set the guid of unique handler to the same of original handler, so it can be removed
		proxy.guid = fn.guid = fn.guid || jQuery.guid++;

		return proxy;
	},

	now: function() {
		return +( new Date() );
	},

	// jQuery.support is not used in Core but other projects attach their
	// properties to it so it needs to exist.
	support: support
});

// Populate the class2type map
jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
	class2type[ "[object " + name + "]" ] = name.toLowerCase();
});

function isArraylike( obj ) {
	var length = obj.length,
		type = jQuery.type( obj );

	if ( type === "function" || jQuery.isWindow( obj ) ) {
		return false;
	}

	if ( obj.nodeType === 1 && length ) {
		return true;
	}

	return type === "array" || length === 0 ||
		typeof length === "number" && length > 0 && ( length - 1 ) in obj;
}
var Sizzle =
/*!
 * Sizzle CSS Selector Engine v1.10.19
 * http://sizzlejs.com/
 *
 * Copyright 2013 jQuery Foundation, Inc. and other contributors
 * Released under the MIT license
 * http://jquery.org/license
 *
 * Date: 2014-04-18
 */
(function( window ) {

var i,
	support,
	Expr,
	getText,
	isXML,
	tokenize,
	compile,
	select,
	outermostContext,
	sortInput,
	hasDuplicate,

	// Local document vars
	setDocument,
	document,
	docElem,
	documentIsHTML,
	rbuggyQSA,
	rbuggyMatches,
	matches,
	contains,

	// Instance-specific data
	expando = "sizzle" + -(new Date()),
	preferredDoc = window.document,
	dirruns = 0,
	done = 0,
	classCache = createCache(),
	tokenCache = createCache(),
	compilerCache = createCache(),
	sortOrder = function( a, b ) {
		if ( a === b ) {
			hasDuplicate = true;
		}
		return 0;
	},

	// General-purpose constants
	strundefined = typeof undefined,
	MAX_NEGATIVE = 1 << 31,

	// Instance methods
	hasOwn = ({}).hasOwnProperty,
	arr = [],
	pop = arr.pop,
	push_native = arr.push,
	push = arr.push,
	slice = arr.slice,
	// Use a stripped-down indexOf if we can't use a native one
	indexOf = arr.indexOf || function( elem ) {
		var i = 0,
			len = this.length;
		for ( ; i < len; i++ ) {
			if ( this[i] === elem ) {
				return i;
			}
		}
		return -1;
	},

	booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",

	// Regular expressions

	// Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
	whitespace = "[\\x20\\t\\r\\n\\f]",
	// http://www.w3.org/TR/css3-syntax/#characters
	characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",

	// Loosely modeled on CSS identifier characters
	// An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
	// Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
	identifier = characterEncoding.replace( "w", "w#" ),

	// Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
	attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace +
		// Operator (capture 2)
		"*([*^$|!~]?=)" + whitespace +
		// "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
		"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
		"*\\]",

	pseudos = ":(" + characterEncoding + ")(?:\\((" +
		// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
		// 1. quoted (capture 3; capture 4 or capture 5)
		"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
		// 2. simple (capture 6)
		"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
		// 3. anything else (capture 2)
		".*" +
		")\\)|)",

	// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
	rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),

	rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
	rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),

	rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),

	rpseudo = new RegExp( pseudos ),
	ridentifier = new RegExp( "^" + identifier + "$" ),

	matchExpr = {
		"ID": new RegExp( "^#(" + characterEncoding + ")" ),
		"CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
		"TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
		"ATTR": new RegExp( "^" + attributes ),
		"PSEUDO": new RegExp( "^" + pseudos ),
		"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
			"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
			"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
		"bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
		// For use in libraries implementing .is()
		// We use this for POS matching in `select`
		"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
			whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
	},

	rinputs = /^(?:input|select|textarea|button)$/i,
	rheader = /^h\d$/i,

	rnative = /^[^{]+\{\s*\[native \w/,

	// Easily-parseable/retrievable ID or TAG or CLASS selectors
	rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,

	rsibling = /[+~]/,
	rescape = /'|\\/g,

	// CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
	runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
	funescape = function( _, escaped, escapedWhitespace ) {
		var high = "0x" + escaped - 0x10000;
		// NaN means non-codepoint
		// Support: Firefox<24
		// Workaround erroneous numeric interpretation of +"0x"
		return high !== high || escapedWhitespace ?
			escaped :
			high < 0 ?
				// BMP codepoint
				String.fromCharCode( high + 0x10000 ) :
				// Supplemental Plane codepoint (surrogate pair)
				String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
	};

// Optimize for push.apply( _, NodeList )
try {
	push.apply(
		(arr = slice.call( preferredDoc.childNodes )),
		preferredDoc.childNodes
	);
	// Support: Android<4.0
	// Detect silently failing push.apply
	arr[ preferredDoc.childNodes.length ].nodeType;
} catch ( e ) {
	push = { apply: arr.length ?

		// Leverage slice if possible
		function( target, els ) {
			push_native.apply( target, slice.call(els) );
		} :

		// Support: IE<9
		// Otherwise append directly
		function( target, els ) {
			var j = target.length,
				i = 0;
			// Can't trust NodeList.length
			while ( (target[j++] = els[i++]) ) {}
			target.length = j - 1;
		}
	};
}

function Sizzle( selector, context, results, seed ) {
	var match, elem, m, nodeType,
		// QSA vars
		i, groups, old, nid, newContext, newSelector;

	if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
		setDocument( context );
	}

	context = context || document;
	results = results || [];

	if ( !selector || typeof selector !== "string" ) {
		return results;
	}

	if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
		return [];
	}

	if ( documentIsHTML && !seed ) {

		// Shortcuts
		if ( (match = rquickExpr.exec( selector )) ) {
			// Speed-up: Sizzle("#ID")
			if ( (m = match[1]) ) {
				if ( nodeType === 9 ) {
					elem = context.getElementById( m );
					// Check parentNode to catch when Blackberry 4.6 returns
					// nodes that are no longer in the document (jQuery #6963)
					if ( elem && elem.parentNode ) {
						// Handle the case where IE, Opera, and Webkit return items
						// by name instead of ID
						if ( elem.id === m ) {
							results.push( elem );
							return results;
						}
					} else {
						return results;
					}
				} else {
					// Context is not a document
					if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
						contains( context, elem ) && elem.id === m ) {
						results.push( elem );
						return results;
					}
				}

			// Speed-up: Sizzle("TAG")
			} else if ( match[2] ) {
				push.apply( results, context.getElementsByTagName( selector ) );
				return results;

			// Speed-up: Sizzle(".CLASS")
			} else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
				push.apply( results, context.getElementsByClassName( m ) );
				return results;
			}
		}

		// QSA path
		if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
			nid = old = expando;
			newContext = context;
			newSelector = nodeType === 9 && selector;

			// qSA works strangely on Element-rooted queries
			// We can work around this by specifying an extra ID on the root
			// and working up from there (Thanks to Andrew Dupont for the technique)
			// IE 8 doesn't work on object elements
			if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
				groups = tokenize( selector );

				if ( (old = context.getAttribute("id")) ) {
					nid = old.replace( rescape, "\\$&" );
				} else {
					context.setAttribute( "id", nid );
				}
				nid = "[id='" + nid + "'] ";

				i = groups.length;
				while ( i-- ) {
					groups[i] = nid + toSelector( groups[i] );
				}
				newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context;
				newSelector = groups.join(",");
			}

			if ( newSelector ) {
				try {
					push.apply( results,
						newContext.querySelectorAll( newSelector )
					);
					return results;
				} catch(qsaError) {
				} finally {
					if ( !old ) {
						context.removeAttribute("id");
					}
				}
			}
		}
	}

	// All others
	return select( selector.replace( rtrim, "$1" ), context, results, seed );
}

/**
 * Create key-value caches of limited size
 * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
 *	property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
 *	deleting the oldest entry
 */
function createCache() {
	var keys = [];

	function cache( key, value ) {
		// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
		if ( keys.push( key + " " ) > Expr.cacheLength ) {
			// Only keep the most recent entries
			delete cache[ keys.shift() ];
		}
		return (cache[ key + " " ] = value);
	}
	return cache;
}

/**
 * Mark a function for special use by Sizzle
 * @param {Function} fn The function to mark
 */
function markFunction( fn ) {
	fn[ expando ] = true;
	return fn;
}

/**
 * Support testing using an element
 * @param {Function} fn Passed the created div and expects a boolean result
 */
function assert( fn ) {
	var div = document.createElement("div");

	try {
		return !!fn( div );
	} catch (e) {
		return false;
	} finally {
		// Remove from its parent by default
		if ( div.parentNode ) {
			div.parentNode.removeChild( div );
		}
		// release memory in IE
		div = null;
	}
}

/**
 * Adds the same handler for all of the specified attrs
 * @param {String} attrs Pipe-separated list of attributes
 * @param {Function} handler The method that will be applied
 */
function addHandle( attrs, handler ) {
	var arr = attrs.split("|"),
		i = attrs.length;

	while ( i-- ) {
		Expr.attrHandle[ arr[i] ] = handler;
	}
}

/**
 * Checks document order of two siblings
 * @param {Element} a
 * @param {Element} b
 * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
 */
function siblingCheck( a, b ) {
	var cur = b && a,
		diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
			( ~b.sourceIndex || MAX_NEGATIVE ) -
			( ~a.sourceIndex || MAX_NEGATIVE );

	// Use IE sourceIndex if available on both nodes
	if ( diff ) {
		return diff;
	}

	// Check if b follows a
	if ( cur ) {
		while ( (cur = cur.nextSibling) ) {
			if ( cur === b ) {
				return -1;
			}
		}
	}

	return a ? 1 : -1;
}

/**
 * Returns a function to use in pseudos for input types
 * @param {String} type
 */
function createInputPseudo( type ) {
	return function( elem ) {
		var name = elem.nodeName.toLowerCase();
		return name === "input" && elem.type === type;
	};
}

/**
 * Returns a function to use in pseudos for buttons
 * @param {String} type
 */
function createButtonPseudo( type ) {
	return function( elem ) {
		var name = elem.nodeName.toLowerCase();
		return (name === "input" || name === "button") && elem.type === type;
	};
}

/**
 * Returns a function to use in pseudos for positionals
 * @param {Function} fn
 */
function createPositionalPseudo( fn ) {
	return markFunction(function( argument ) {
		argument = +argument;
		return markFunction(function( seed, matches ) {
			var j,
				matchIndexes = fn( [], seed.length, argument ),
				i = matchIndexes.length;

			// Match elements found at the specified indexes
			while ( i-- ) {
				if ( seed[ (j = matchIndexes[i]) ] ) {
					seed[j] = !(matches[j] = seed[j]);
				}
			}
		});
	});
}

/**
 * Checks a node for validity as a Sizzle context
 * @param {Element|Object=} context
 * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
 */
function testContext( context ) {
	return context && typeof context.getElementsByTagName !== strundefined && context;
}

// Expose support vars for convenience
support = Sizzle.support = {};

/**
 * Detects XML nodes
 * @param {Element|Object} elem An element or a document
 * @returns {Boolean} True iff elem is a non-HTML XML node
 */
isXML = Sizzle.isXML = function( elem ) {
	// documentElement is verified for cases where it doesn't yet exist
	// (such as loading iframes in IE - #4833)
	var documentElement = elem && (elem.ownerDocument || elem).documentElement;
	return documentElement ? documentElement.nodeName !== "HTML" : false;
};

/**
 * Sets document-related variables once based on the current document
 * @param {Element|Object} [doc] An element or document object to use to set the document
 * @returns {Object} Returns the current document
 */
setDocument = Sizzle.setDocument = function( node ) {
	var hasCompare,
		doc = node ? node.ownerDocument || node : preferredDoc,
		parent = doc.defaultView;

	// If no document and documentElement is available, return
	if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
		return document;
	}

	// Set our document
	document = doc;
	docElem = doc.documentElement;

	// Support tests
	documentIsHTML = !isXML( doc );

	// Support: IE>8
	// If iframe document is assigned to "document" variable and if iframe has been reloaded,
	// IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
	// IE6-8 do not support the defaultView property so parent will be undefined
	if ( parent && parent !== parent.top ) {
		// IE11 does not have attachEvent, so all must suffer
		if ( parent.addEventListener ) {
			parent.addEventListener( "unload", function() {
				setDocument();
			}, false );
		} else if ( parent.attachEvent ) {
			parent.attachEvent( "onunload", function() {
				setDocument();
			});
		}
	}

	/* Attributes
	---------------------------------------------------------------------- */

	// Support: IE<8
	// Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans)
	support.attributes = assert(function( div ) {
		div.className = "i";
		return !div.getAttribute("className");
	});

	/* getElement(s)By*
	---------------------------------------------------------------------- */

	// Check if getElementsByTagName("*") returns only elements
	support.getElementsByTagName = assert(function( div ) {
		div.appendChild( doc.createComment("") );
		return !div.getElementsByTagName("*").length;
	});

	// Check if getElementsByClassName can be trusted
	support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) {
		div.innerHTML = "<div class='a'></div><div class='a i'></div>";

		// Support: Safari<4
		// Catch class over-caching
		div.firstChild.className = "i";
		// Support: Opera<10
		// Catch gEBCN failure to find non-leading classes
		return div.getElementsByClassName("i").length === 2;
	});

	// Support: IE<10
	// Check if getElementById returns elements by name
	// The broken getElementById methods don't pick up programatically-set names,
	// so use a roundabout getElementsByName test
	support.getById = assert(function( div ) {
		docElem.appendChild( div ).id = expando;
		return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
	});

	// ID find and filter
	if ( support.getById ) {
		Expr.find["ID"] = function( id, context ) {
			if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
				var m = context.getElementById( id );
				// Check parentNode to catch when Blackberry 4.6 returns
				// nodes that are no longer in the document #6963
				return m && m.parentNode ? [ m ] : [];
			}
		};
		Expr.filter["ID"] = function( id ) {
			var attrId = id.replace( runescape, funescape );
			return function( elem ) {
				return elem.getAttribute("id") === attrId;
			};
		};
	} else {
		// Support: IE6/7
		// getElementById is not reliable as a find shortcut
		delete Expr.find["ID"];

		Expr.filter["ID"] =  function( id ) {
			var attrId = id.replace( runescape, funescape );
			return function( elem ) {
				var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
				return node && node.value === attrId;
			};
		};
	}

	// Tag
	Expr.find["TAG"] = support.getElementsByTagName ?
		function( tag, context ) {
			if ( typeof context.getElementsByTagName !== strundefined ) {
				return context.getElementsByTagName( tag );
			}
		} :
		function( tag, context ) {
			var elem,
				tmp = [],
				i = 0,
				results = context.getElementsByTagName( tag );

			// Filter out possible comments
			if ( tag === "*" ) {
				while ( (elem = results[i++]) ) {
					if ( elem.nodeType === 1 ) {
						tmp.push( elem );
					}
				}

				return tmp;
			}
			return results;
		};

	// Class
	Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
		if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
			return context.getElementsByClassName( className );
		}
	};

	/* QSA/matchesSelector
	---------------------------------------------------------------------- */

	// QSA and matchesSelector support

	// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
	rbuggyMatches = [];

	// qSa(:focus) reports false when true (Chrome 21)
	// We allow this because of a bug in IE8/9 that throws an error
	// whenever `document.activeElement` is accessed on an iframe
	// So, we allow :focus to pass through QSA all the time to avoid the IE error
	// See http://bugs.jquery.com/ticket/13378
	rbuggyQSA = [];

	if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
		// Build QSA regex
		// Regex strategy adopted from Diego Perini
		assert(function( div ) {
			// Select is set to empty string on purpose
			// This is to test IE's treatment of not explicitly
			// setting a boolean content attribute,
			// since its presence should be enough
			// http://bugs.jquery.com/ticket/12359
			div.innerHTML = "<select msallowclip=''><option selected=''></option></select>";

			// Support: IE8, Opera 11-12.16
			// Nothing should be selected when empty strings follow ^= or $= or *=
			// The test attribute must be unknown in Opera but "safe" for WinRT
			// http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
			if ( div.querySelectorAll("[msallowclip^='']").length ) {
				rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
			}

			// Support: IE8
			// Boolean attributes and "value" are not treated correctly
			if ( !div.querySelectorAll("[selected]").length ) {
				rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
			}

			// Webkit/Opera - :checked should return selected option elements
			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
			// IE8 throws error here and will not see later tests
			if ( !div.querySelectorAll(":checked").length ) {
				rbuggyQSA.push(":checked");
			}
		});

		assert(function( div ) {
			// Support: Windows 8 Native Apps
			// The type and name attributes are restricted during .innerHTML assignment
			var input = doc.createElement("input");
			input.setAttribute( "type", "hidden" );
			div.appendChild( input ).setAttribute( "name", "D" );

			// Support: IE8
			// Enforce case-sensitivity of name attribute
			if ( div.querySelectorAll("[name=d]").length ) {
				rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
			}

			// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
			// IE8 throws error here and will not see later tests
			if ( !div.querySelectorAll(":enabled").length ) {
				rbuggyQSA.push( ":enabled", ":disabled" );
			}

			// Opera 10-11 does not throw on post-comma invalid pseudos
			div.querySelectorAll("*,:x");
			rbuggyQSA.push(",.*:");
		});
	}

	if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
		docElem.webkitMatchesSelector ||
		docElem.mozMatchesSelector ||
		docElem.oMatchesSelector ||
		docElem.msMatchesSelector) )) ) {

		assert(function( div ) {
			// Check to see if it's possible to do matchesSelector
			// on a disconnected node (IE 9)
			support.disconnectedMatch = matches.call( div, "div" );

			// This should fail with an exception
			// Gecko does not error, returns false instead
			matches.call( div, "[s!='']:x" );
			rbuggyMatches.push( "!=", pseudos );
		});
	}

	rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
	rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );

	/* Contains
	---------------------------------------------------------------------- */
	hasCompare = rnative.test( docElem.compareDocumentPosition );

	// Element contains another
	// Purposefully does not implement inclusive descendent
	// As in, an element does not contain itself
	contains = hasCompare || rnative.test( docElem.contains ) ?
		function( a, b ) {
			var adown = a.nodeType === 9 ? a.documentElement : a,
				bup = b && b.parentNode;
			return a === bup || !!( bup && bup.nodeType === 1 && (
				adown.contains ?
					adown.contains( bup ) :
					a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
			));
		} :
		function( a, b ) {
			if ( b ) {
				while ( (b = b.parentNode) ) {
					if ( b === a ) {
						return true;
					}
				}
			}
			return false;
		};

	/* Sorting
	---------------------------------------------------------------------- */

	// Document order sorting
	sortOrder = hasCompare ?
	function( a, b ) {

		// Flag for duplicate removal
		if ( a === b ) {
			hasDuplicate = true;
			return 0;
		}

		// Sort on method existence if only one input has compareDocumentPosition
		var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
		if ( compare ) {
			return compare;
		}

		// Calculate position if both inputs belong to the same document
		compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
			a.compareDocumentPosition( b ) :

			// Otherwise we know they are disconnected
			1;

		// Disconnected nodes
		if ( compare & 1 ||
			(!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {

			// Choose the first element that is related to our preferred document
			if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
				return -1;
			}
			if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
				return 1;
			}

			// Maintain original order
			return sortInput ?
				( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
				0;
		}

		return compare & 4 ? -1 : 1;
	} :
	function( a, b ) {
		// Exit early if the nodes are identical
		if ( a === b ) {
			hasDuplicate = true;
			return 0;
		}

		var cur,
			i = 0,
			aup = a.parentNode,
			bup = b.parentNode,
			ap = [ a ],
			bp = [ b ];

		// Parentless nodes are either documents or disconnected
		if ( !aup || !bup ) {
			return a === doc ? -1 :
				b === doc ? 1 :
				aup ? -1 :
				bup ? 1 :
				sortInput ?
				( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
				0;

		// If the nodes are siblings, we can do a quick check
		} else if ( aup === bup ) {
			return siblingCheck( a, b );
		}

		// Otherwise we need full lists of their ancestors for comparison
		cur = a;
		while ( (cur = cur.parentNode) ) {
			ap.unshift( cur );
		}
		cur = b;
		while ( (cur = cur.parentNode) ) {
			bp.unshift( cur );
		}

		// Walk down the tree looking for a discrepancy
		while ( ap[i] === bp[i] ) {
			i++;
		}

		return i ?
			// Do a sibling check if the nodes have a common ancestor
			siblingCheck( ap[i], bp[i] ) :

			// Otherwise nodes in our document sort first
			ap[i] === preferredDoc ? -1 :
			bp[i] === preferredDoc ? 1 :
			0;
	};

	return doc;
};

Sizzle.matches = function( expr, elements ) {
	return Sizzle( expr, null, null, elements );
};

Sizzle.matchesSelector = function( elem, expr ) {
	// Set document vars if needed
	if ( ( elem.ownerDocument || elem ) !== document ) {
		setDocument( elem );
	}

	// Make sure that attribute selectors are quoted
	expr = expr.replace( rattributeQuotes, "='$1']" );

	if ( support.matchesSelector && documentIsHTML &&
		( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
		( !rbuggyQSA     || !rbuggyQSA.test( expr ) ) ) {

		try {
			var ret = matches.call( elem, expr );

			// IE 9's matchesSelector returns false on disconnected nodes
			if ( ret || support.disconnectedMatch ||
					// As well, disconnected nodes are said to be in a document
					// fragment in IE 9
					elem.document && elem.document.nodeType !== 11 ) {
				return ret;
			}
		} catch(e) {}
	}

	return Sizzle( expr, document, null, [ elem ] ).length > 0;
};

Sizzle.contains = function( context, elem ) {
	// Set document vars if needed
	if ( ( context.ownerDocument || context ) !== document ) {
		setDocument( context );
	}
	return contains( context, elem );
};

Sizzle.attr = function( elem, name ) {
	// Set document vars if needed
	if ( ( elem.ownerDocument || elem ) !== document ) {
		setDocument( elem );
	}

	var fn = Expr.attrHandle[ name.toLowerCase() ],
		// Don't get fooled by Object.prototype properties (jQuery #13807)
		val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
			fn( elem, name, !documentIsHTML ) :
			undefined;

	return val !== undefined ?
		val :
		support.attributes || !documentIsHTML ?
			elem.getAttribute( name ) :
			(val = elem.getAttributeNode(name)) && val.specified ?
				val.value :
				null;
};

Sizzle.error = function( msg ) {
	throw new Error( "Syntax error, unrecognized expression: " + msg );
};

/**
 * Document sorting and removing duplicates
 * @param {ArrayLike} results
 */
Sizzle.uniqueSort = function( results ) {
	var elem,
		duplicates = [],
		j = 0,
		i = 0;

	// Unless we *know* we can detect duplicates, assume their presence
	hasDuplicate = !support.detectDuplicates;
	sortInput = !support.sortStable && results.slice( 0 );
	results.sort( sortOrder );

	if ( hasDuplicate ) {
		while ( (elem = results[i++]) ) {
			if ( elem === results[ i ] ) {
				j = duplicates.push( i );
			}
		}
		while ( j-- ) {
			results.splice( duplicates[ j ], 1 );
		}
	}

	// Clear input after sorting to release objects
	// See https://github.com/jquery/sizzle/pull/225
	sortInput = null;

	return results;
};

/**
 * Utility function for retrieving the text value of an array of DOM nodes
 * @param {Array|Element} elem
 */
getText = Sizzle.getText = function( elem ) {
	var node,
		ret = "",
		i = 0,
		nodeType = elem.nodeType;

	if ( !nodeType ) {
		// If no nodeType, this is expected to be an array
		while ( (node = elem[i++]) ) {
			// Do not traverse comment nodes
			ret += getText( node );
		}
	} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
		// Use textContent for elements
		// innerText usage removed for consistency of new lines (jQuery #11153)
		if ( typeof elem.textContent === "string" ) {
			return elem.textContent;
		} else {
			// Traverse its children
			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
				ret += getText( elem );
			}
		}
	} else if ( nodeType === 3 || nodeType === 4 ) {
		return elem.nodeValue;
	}
	// Do not include comment or processing instruction nodes

	return ret;
};

Expr = Sizzle.selectors = {

	// Can be adjusted by the user
	cacheLength: 50,

	createPseudo: markFunction,

	match: matchExpr,

	attrHandle: {},

	find: {},

	relative: {
		">": { dir: "parentNode", first: true },
		" ": { dir: "parentNode" },
		"+": { dir: "previousSibling", first: true },
		"~": { dir: "previousSibling" }
	},

	preFilter: {
		"ATTR": function( match ) {
			match[1] = match[1].replace( runescape, funescape );

			// Move the given value to match[3] whether quoted or unquoted
			match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );

			if ( match[2] === "~=" ) {
				match[3] = " " + match[3] + " ";
			}

			return match.slice( 0, 4 );
		},

		"CHILD": function( match ) {
			/* matches from matchExpr["CHILD"]
				1 type (only|nth|...)
				2 what (child|of-type)
				3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
				4 xn-component of xn+y argument ([+-]?\d*n|)
				5 sign of xn-component
				6 x of xn-component
				7 sign of y-component
				8 y of y-component
			*/
			match[1] = match[1].toLowerCase();

			if ( match[1].slice( 0, 3 ) === "nth" ) {
				// nth-* requires argument
				if ( !match[3] ) {
					Sizzle.error( match[0] );
				}

				// numeric x and y parameters for Expr.filter.CHILD
				// remember that false/true cast respectively to 0/1
				match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
				match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );

			// other types prohibit arguments
			} else if ( match[3] ) {
				Sizzle.error( match[0] );
			}

			return match;
		},

		"PSEUDO": function( match ) {
			var excess,
				unquoted = !match[6] && match[2];

			if ( matchExpr["CHILD"].test( match[0] ) ) {
				return null;
			}

			// Accept quoted arguments as-is
			if ( match[3] ) {
				match[2] = match[4] || match[5] || "";

			// Strip excess characters from unquoted arguments
			} else if ( unquoted && rpseudo.test( unquoted ) &&
				// Get excess from tokenize (recursively)
				(excess = tokenize( unquoted, true )) &&
				// advance to the next closing parenthesis
				(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {

				// excess is a negative index
				match[0] = match[0].slice( 0, excess );
				match[2] = unquoted.slice( 0, excess );
			}

			// Return only captures needed by the pseudo filter method (type and argument)
			return match.slice( 0, 3 );
		}
	},

	filter: {

		"TAG": function( nodeNameSelector ) {
			var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
			return nodeNameSelector === "*" ?
				function() { return true; } :
				function( elem ) {
					return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
				};
		},

		"CLASS": function( className ) {
			var pattern = classCache[ className + " " ];

			return pattern ||
				(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
				classCache( className, function( elem ) {
					return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" );
				});
		},

		"ATTR": function( name, operator, check ) {
			return function( elem ) {
				var result = Sizzle.attr( elem, name );

				if ( result == null ) {
					return operator === "!=";
				}
				if ( !operator ) {
					return true;
				}

				result += "";

				return operator === "=" ? result === check :
					operator === "!=" ? result !== check :
					operator === "^=" ? check && result.indexOf( check ) === 0 :
					operator === "*=" ? check && result.indexOf( check ) > -1 :
					operator === "$=" ? check && result.slice( -check.length ) === check :
					operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
					operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
					false;
			};
		},

		"CHILD": function( type, what, argument, first, last ) {
			var simple = type.slice( 0, 3 ) !== "nth",
				forward = type.slice( -4 ) !== "last",
				ofType = what === "of-type";

			return first === 1 && last === 0 ?

				// Shortcut for :nth-*(n)
				function( elem ) {
					return !!elem.parentNode;
				} :

				function( elem, context, xml ) {
					var cache, outerCache, node, diff, nodeIndex, start,
						dir = simple !== forward ? "nextSibling" : "previousSibling",
						parent = elem.parentNode,
						name = ofType && elem.nodeName.toLowerCase(),
						useCache = !xml && !ofType;

					if ( parent ) {

						// :(first|last|only)-(child|of-type)
						if ( simple ) {
							while ( dir ) {
								node = elem;
								while ( (node = node[ dir ]) ) {
									if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
										return false;
									}
								}
								// Reverse direction for :only-* (if we haven't yet done so)
								start = dir = type === "only" && !start && "nextSibling";
							}
							return true;
						}

						start = [ forward ? parent.firstChild : parent.lastChild ];

						// non-xml :nth-child(...) stores cache data on `parent`
						if ( forward && useCache ) {
							// Seek `elem` from a previously-cached index
							outerCache = parent[ expando ] || (parent[ expando ] = {});
							cache = outerCache[ type ] || [];
							nodeIndex = cache[0] === dirruns && cache[1];
							diff = cache[0] === dirruns && cache[2];
							node = nodeIndex && parent.childNodes[ nodeIndex ];

							while ( (node = ++nodeIndex && node && node[ dir ] ||

								// Fallback to seeking `elem` from the start
								(diff = nodeIndex = 0) || start.pop()) ) {

								// When found, cache indexes on `parent` and break
								if ( node.nodeType === 1 && ++diff && node === elem ) {
									outerCache[ type ] = [ dirruns, nodeIndex, diff ];
									break;
								}
							}

						// Use previously-cached element index if available
						} else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
							diff = cache[1];

						// xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
						} else {
							// Use the same loop as above to seek `elem` from the start
							while ( (node = ++nodeIndex && node && node[ dir ] ||
								(diff = nodeIndex = 0) || start.pop()) ) {

								if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
									// Cache the index of each encountered element
									if ( useCache ) {
										(node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
									}

									if ( node === elem ) {
										break;
									}
								}
							}
						}

						// Incorporate the offset, then check against cycle size
						diff -= last;
						return diff === first || ( diff % first === 0 && diff / first >= 0 );
					}
				};
		},

		"PSEUDO": function( pseudo, argument ) {
			// pseudo-class names are case-insensitive
			// http://www.w3.org/TR/selectors/#pseudo-classes
			// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
			// Remember that setFilters inherits from pseudos
			var args,
				fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
					Sizzle.error( "unsupported pseudo: " + pseudo );

			// The user may use createPseudo to indicate that
			// arguments are needed to create the filter function
			// just as Sizzle does
			if ( fn[ expando ] ) {
				return fn( argument );
			}

			// But maintain support for old signatures
			if ( fn.length > 1 ) {
				args = [ pseudo, pseudo, "", argument ];
				return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
					markFunction(function( seed, matches ) {
						var idx,
							matched = fn( seed, argument ),
							i = matched.length;
						while ( i-- ) {
							idx = indexOf.call( seed, matched[i] );
							seed[ idx ] = !( matches[ idx ] = matched[i] );
						}
					}) :
					function( elem ) {
						return fn( elem, 0, args );
					};
			}

			return fn;
		}
	},

	pseudos: {
		// Potentially complex pseudos
		"not": markFunction(function( selector ) {
			// Trim the selector passed to compile
			// to avoid treating leading and trailing
			// spaces as combinators
			var input = [],
				results = [],
				matcher = compile( selector.replace( rtrim, "$1" ) );

			return matcher[ expando ] ?
				markFunction(function( seed, matches, context, xml ) {
					var elem,
						unmatched = matcher( seed, null, xml, [] ),
						i = seed.length;

					// Match elements unmatched by `matcher`
					while ( i-- ) {
						if ( (elem = unmatched[i]) ) {
							seed[i] = !(matches[i] = elem);
						}
					}
				}) :
				function( elem, context, xml ) {
					input[0] = elem;
					matcher( input, null, xml, results );
					return !results.pop();
				};
		}),

		"has": markFunction(function( selector ) {
			return function( elem ) {
				return Sizzle( selector, elem ).length > 0;
			};
		}),

		"contains": markFunction(function( text ) {
			return function( elem ) {
				return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
			};
		}),

		// "Whether an element is represented by a :lang() selector
		// is based solely on the element's language value
		// being equal to the identifier C,
		// or beginning with the identifier C immediately followed by "-".
		// The matching of C against the element's language value is performed case-insensitively.
		// The identifier C does not have to be a valid language name."
		// http://www.w3.org/TR/selectors/#lang-pseudo
		"lang": markFunction( function( lang ) {
			// lang value must be a valid identifier
			if ( !ridentifier.test(lang || "") ) {
				Sizzle.error( "unsupported lang: " + lang );
			}
			lang = lang.replace( runescape, funescape ).toLowerCase();
			return function( elem ) {
				var elemLang;
				do {
					if ( (elemLang = documentIsHTML ?
						elem.lang :
						elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {

						elemLang = elemLang.toLowerCase();
						return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
					}
				} while ( (elem = elem.parentNode) && elem.nodeType === 1 );
				return false;
			};
		}),

		// Miscellaneous
		"target": function( elem ) {
			var hash = window.location && window.location.hash;
			return hash && hash.slice( 1 ) === elem.id;
		},

		"root": function( elem ) {
			return elem === docElem;
		},

		"focus": function( elem ) {
			return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
		},

		// Boolean properties
		"enabled": function( elem ) {
			return elem.disabled === false;
		},

		"disabled": function( elem ) {
			return elem.disabled === true;
		},

		"checked": function( elem ) {
			// In CSS3, :checked should return both checked and selected elements
			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
			var nodeName = elem.nodeName.toLowerCase();
			return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
		},

		"selected": function( elem ) {
			// Accessing this property makes selected-by-default
			// options in Safari work properly
			if ( elem.parentNode ) {
				elem.parentNode.selectedIndex;
			}

			return elem.selected === true;
		},

		// Contents
		"empty": function( elem ) {
			// http://www.w3.org/TR/selectors/#empty-pseudo
			// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
			//   but not by others (comment: 8; processing instruction: 7; etc.)
			// nodeType < 6 works because attributes (2) do not appear as children
			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
				if ( elem.nodeType < 6 ) {
					return false;
				}
			}
			return true;
		},

		"parent": function( elem ) {
			return !Expr.pseudos["empty"]( elem );
		},

		// Element/input types
		"header": function( elem ) {
			return rheader.test( elem.nodeName );
		},

		"input": function( elem ) {
			return rinputs.test( elem.nodeName );
		},

		"button": function( elem ) {
			var name = elem.nodeName.toLowerCase();
			return name === "input" && elem.type === "button" || name === "button";
		},

		"text": function( elem ) {
			var attr;
			return elem.nodeName.toLowerCase() === "input" &&
				elem.type === "text" &&

				// Support: IE<8
				// New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
				( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
		},

		// Position-in-collection
		"first": createPositionalPseudo(function() {
			return [ 0 ];
		}),

		"last": createPositionalPseudo(function( matchIndexes, length ) {
			return [ length - 1 ];
		}),

		"eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
			return [ argument < 0 ? argument + length : argument ];
		}),

		"even": createPositionalPseudo(function( matchIndexes, length ) {
			var i = 0;
			for ( ; i < length; i += 2 ) {
				matchIndexes.push( i );
			}
			return matchIndexes;
		}),

		"odd": createPositionalPseudo(function( matchIndexes, length ) {
			var i = 1;
			for ( ; i < length; i += 2 ) {
				matchIndexes.push( i );
			}
			return matchIndexes;
		}),

		"lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
			var i = argument < 0 ? argument + length : argument;
			for ( ; --i >= 0; ) {
				matchIndexes.push( i );
			}
			return matchIndexes;
		}),

		"gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
			var i = argument < 0 ? argument + length : argument;
			for ( ; ++i < length; ) {
				matchIndexes.push( i );
			}
			return matchIndexes;
		})
	}
};

Expr.pseudos["nth"] = Expr.pseudos["eq"];

// Add button/input type pseudos
for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
	Expr.pseudos[ i ] = createInputPseudo( i );
}
for ( i in { submit: true, reset: true } ) {
	Expr.pseudos[ i ] = createButtonPseudo( i );
}

// Easy API for creating new setFilters
function setFilters() {}
setFilters.prototype = Expr.filters = Expr.pseudos;
Expr.setFilters = new setFilters();

tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
	var matched, match, tokens, type,
		soFar, groups, preFilters,
		cached = tokenCache[ selector + " " ];

	if ( cached ) {
		return parseOnly ? 0 : cached.slice( 0 );
	}

	soFar = selector;
	groups = [];
	preFilters = Expr.preFilter;

	while ( soFar ) {

		// Comma and first run
		if ( !matched || (match = rcomma.exec( soFar )) ) {
			if ( match ) {
				// Don't consume trailing commas as valid
				soFar = soFar.slice( match[0].length ) || soFar;
			}
			groups.push( (tokens = []) );
		}

		matched = false;

		// Combinators
		if ( (match = rcombinators.exec( soFar )) ) {
			matched = match.shift();
			tokens.push({
				value: matched,
				// Cast descendant combinators to space
				type: match[0].replace( rtrim, " " )
			});
			soFar = soFar.slice( matched.length );
		}

		// Filters
		for ( type in Expr.filter ) {
			if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
				(match = preFilters[ type ]( match ))) ) {
				matched = match.shift();
				tokens.push({
					value: matched,
					type: type,
					matches: match
				});
				soFar = soFar.slice( matched.length );
			}
		}

		if ( !matched ) {
			break;
		}
	}

	// Return the length of the invalid excess
	// if we're just parsing
	// Otherwise, throw an error or return tokens
	return parseOnly ?
		soFar.length :
		soFar ?
			Sizzle.error( selector ) :
			// Cache the tokens
			tokenCache( selector, groups ).slice( 0 );
};

function toSelector( tokens ) {
	var i = 0,
		len = tokens.length,
		selector = "";
	for ( ; i < len; i++ ) {
		selector += tokens[i].value;
	}
	return selector;
}

function addCombinator( matcher, combinator, base ) {
	var dir = combinator.dir,
		checkNonElements = base && dir === "parentNode",
		doneName = done++;

	return combinator.first ?
		// Check against closest ancestor/preceding element
		function( elem, context, xml ) {
			while ( (elem = elem[ dir ]) ) {
				if ( elem.nodeType === 1 || checkNonElements ) {
					return matcher( elem, context, xml );
				}
			}
		} :

		// Check against all ancestor/preceding elements
		function( elem, context, xml ) {
			var oldCache, outerCache,
				newCache = [ dirruns, doneName ];

			// We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
			if ( xml ) {
				while ( (elem = elem[ dir ]) ) {
					if ( elem.nodeType === 1 || checkNonElements ) {
						if ( matcher( elem, context, xml ) ) {
							return true;
						}
					}
				}
			} else {
				while ( (elem = elem[ dir ]) ) {
					if ( elem.nodeType === 1 || checkNonElements ) {
						outerCache = elem[ expando ] || (elem[ expando ] = {});
						if ( (oldCache = outerCache[ dir ]) &&
							oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {

							// Assign to newCache so results back-propagate to previous elements
							return (newCache[ 2 ] = oldCache[ 2 ]);
						} else {
							// Reuse newcache so results back-propagate to previous elements
							outerCache[ dir ] = newCache;

							// A match means we're done; a fail means we have to keep checking
							if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
								return true;
							}
						}
					}
				}
			}
		};
}

function elementMatcher( matchers ) {
	return matchers.length > 1 ?
		function( elem, context, xml ) {
			var i = matchers.length;
			while ( i-- ) {
				if ( !matchers[i]( elem, context, xml ) ) {
					return false;
				}
			}
			return true;
		} :
		matchers[0];
}

function multipleContexts( selector, contexts, results ) {
	var i = 0,
		len = contexts.length;
	for ( ; i < len; i++ ) {
		Sizzle( selector, contexts[i], results );
	}
	return results;
}

function condense( unmatched, map, filter, context, xml ) {
	var elem,
		newUnmatched = [],
		i = 0,
		len = unmatched.length,
		mapped = map != null;

	for ( ; i < len; i++ ) {
		if ( (elem = unmatched[i]) ) {
			if ( !filter || filter( elem, context, xml ) ) {
				newUnmatched.push( elem );
				if ( mapped ) {
					map.push( i );
				}
			}
		}
	}

	return newUnmatched;
}

function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
	if ( postFilter && !postFilter[ expando ] ) {
		postFilter = setMatcher( postFilter );
	}
	if ( postFinder && !postFinder[ expando ] ) {
		postFinder = setMatcher( postFinder, postSelector );
	}
	return markFunction(function( seed, results, context, xml ) {
		var temp, i, elem,
			preMap = [],
			postMap = [],
			preexisting = results.length,

			// Get initial elements from seed or context
			elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),

			// Prefilter to get matcher input, preserving a map for seed-results synchronization
			matcherIn = preFilter && ( seed || !selector ) ?
				condense( elems, preMap, preFilter, context, xml ) :
				elems,

			matcherOut = matcher ?
				// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
				postFinder || ( seed ? preFilter : preexisting || postFilter ) ?

					// ...intermediate processing is necessary
					[] :

					// ...otherwise use results directly
					results :
				matcherIn;

		// Find primary matches
		if ( matcher ) {
			matcher( matcherIn, matcherOut, context, xml );
		}

		// Apply postFilter
		if ( postFilter ) {
			temp = condense( matcherOut, postMap );
			postFilter( temp, [], context, xml );

			// Un-match failing elements by moving them back to matcherIn
			i = temp.length;
			while ( i-- ) {
				if ( (elem = temp[i]) ) {
					matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
				}
			}
		}

		if ( seed ) {
			if ( postFinder || preFilter ) {
				if ( postFinder ) {
					// Get the final matcherOut by condensing this intermediate into postFinder contexts
					temp = [];
					i = matcherOut.length;
					while ( i-- ) {
						if ( (elem = matcherOut[i]) ) {
							// Restore matcherIn since elem is not yet a final match
							temp.push( (matcherIn[i] = elem) );
						}
					}
					postFinder( null, (matcherOut = []), temp, xml );
				}

				// Move matched elements from seed to results to keep them synchronized
				i = matcherOut.length;
				while ( i-- ) {
					if ( (elem = matcherOut[i]) &&
						(temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {

						seed[temp] = !(results[temp] = elem);
					}
				}
			}

		// Add elements to results, through postFinder if defined
		} else {
			matcherOut = condense(
				matcherOut === results ?
					matcherOut.splice( preexisting, matcherOut.length ) :
					matcherOut
			);
			if ( postFinder ) {
				postFinder( null, results, matcherOut, xml );
			} else {
				push.apply( results, matcherOut );
			}
		}
	});
}

function matcherFromTokens( tokens ) {
	var checkContext, matcher, j,
		len = tokens.length,
		leadingRelative = Expr.relative[ tokens[0].type ],
		implicitRelative = leadingRelative || Expr.relative[" "],
		i = leadingRelative ? 1 : 0,

		// The foundational matcher ensures that elements are reachable from top-level context(s)
		matchContext = addCombinator( function( elem ) {
			return elem === checkContext;
		}, implicitRelative, true ),
		matchAnyContext = addCombinator( function( elem ) {
			return indexOf.call( checkContext, elem ) > -1;
		}, implicitRelative, true ),
		matchers = [ function( elem, context, xml ) {
			return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
				(checkContext = context).nodeType ?
					matchContext( elem, context, xml ) :
					matchAnyContext( elem, context, xml ) );
		} ];

	for ( ; i < len; i++ ) {
		if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
			matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
		} else {
			matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );

			// Return special upon seeing a positional matcher
			if ( matcher[ expando ] ) {
				// Find the next relative operator (if any) for proper handling
				j = ++i;
				for ( ; j < len; j++ ) {
					if ( Expr.relative[ tokens[j].type ] ) {
						break;
					}
				}
				return setMatcher(
					i > 1 && elementMatcher( matchers ),
					i > 1 && toSelector(
						// If the preceding token was a descendant combinator, insert an implicit any-element `*`
						tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
					).replace( rtrim, "$1" ),
					matcher,
					i < j && matcherFromTokens( tokens.slice( i, j ) ),
					j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
					j < len && toSelector( tokens )
				);
			}
			matchers.push( matcher );
		}
	}

	return elementMatcher( matchers );
}

function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
	var bySet = setMatchers.length > 0,
		byElement = elementMatchers.length > 0,
		superMatcher = function( seed, context, xml, results, outermost ) {
			var elem, j, matcher,
				matchedCount = 0,
				i = "0",
				unmatched = seed && [],
				setMatched = [],
				contextBackup = outermostContext,
				// We must always have either seed elements or outermost context
				elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
				// Use integer dirruns iff this is the outermost matcher
				dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
				len = elems.length;

			if ( outermost ) {
				outermostContext = context !== document && context;
			}

			// Add elements passing elementMatchers directly to results
			// Keep `i` a string if there are no elements so `matchedCount` will be "00" below
			// Support: IE<9, Safari
			// Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
			for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
				if ( byElement && elem ) {
					j = 0;
					while ( (matcher = elementMatchers[j++]) ) {
						if ( matcher( elem, context, xml ) ) {
							results.push( elem );
							break;
						}
					}
					if ( outermost ) {
						dirruns = dirrunsUnique;
					}
				}

				// Track unmatched elements for set filters
				if ( bySet ) {
					// They will have gone through all possible matchers
					if ( (elem = !matcher && elem) ) {
						matchedCount--;
					}

					// Lengthen the array for every element, matched or not
					if ( seed ) {
						unmatched.push( elem );
					}
				}
			}

			// Apply set filters to unmatched elements
			matchedCount += i;
			if ( bySet && i !== matchedCount ) {
				j = 0;
				while ( (matcher = setMatchers[j++]) ) {
					matcher( unmatched, setMatched, context, xml );
				}

				if ( seed ) {
					// Reintegrate element matches to eliminate the need for sorting
					if ( matchedCount > 0 ) {
						while ( i-- ) {
							if ( !(unmatched[i] || setMatched[i]) ) {
								setMatched[i] = pop.call( results );
							}
						}
					}

					// Discard index placeholder values to get only actual matches
					setMatched = condense( setMatched );
				}

				// Add matches to results
				push.apply( results, setMatched );

				// Seedless set matches succeeding multiple successful matchers stipulate sorting
				if ( outermost && !seed && setMatched.length > 0 &&
					( matchedCount + setMatchers.length ) > 1 ) {

					Sizzle.uniqueSort( results );
				}
			}

			// Override manipulation of globals by nested matchers
			if ( outermost ) {
				dirruns = dirrunsUnique;
				outermostContext = contextBackup;
			}

			return unmatched;
		};

	return bySet ?
		markFunction( superMatcher ) :
		superMatcher;
}

compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
	var i,
		setMatchers = [],
		elementMatchers = [],
		cached = compilerCache[ selector + " " ];

	if ( !cached ) {
		// Generate a function of recursive functions that can be used to check each element
		if ( !match ) {
			match = tokenize( selector );
		}
		i = match.length;
		while ( i-- ) {
			cached = matcherFromTokens( match[i] );
			if ( cached[ expando ] ) {
				setMatchers.push( cached );
			} else {
				elementMatchers.push( cached );
			}
		}

		// Cache the compiled function
		cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );

		// Save selector and tokenization
		cached.selector = selector;
	}
	return cached;
};

/**
 * A low-level selection function that works with Sizzle's compiled
 *  selector functions
 * @param {String|Function} selector A selector or a pre-compiled
 *  selector function built with Sizzle.compile
 * @param {Element} context
 * @param {Array} [results]
 * @param {Array} [seed] A set of elements to match against
 */
select = Sizzle.select = function( selector, context, results, seed ) {
	var i, tokens, token, type, find,
		compiled = typeof selector === "function" && selector,
		match = !seed && tokenize( (selector = compiled.selector || selector) );

	results = results || [];

	// Try to minimize operations if there is no seed and only one group
	if ( match.length === 1 ) {

		// Take a shortcut and set the context if the root selector is an ID
		tokens = match[0] = match[0].slice( 0 );
		if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
				support.getById && context.nodeType === 9 && documentIsHTML &&
				Expr.relative[ tokens[1].type ] ) {

			context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
			if ( !context ) {
				return results;

			// Precompiled matchers will still verify ancestry, so step up a level
			} else if ( compiled ) {
				context = context.parentNode;
			}

			selector = selector.slice( tokens.shift().value.length );
		}

		// Fetch a seed set for right-to-left matching
		i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
		while ( i-- ) {
			token = tokens[i];

			// Abort if we hit a combinator
			if ( Expr.relative[ (type = token.type) ] ) {
				break;
			}
			if ( (find = Expr.find[ type ]) ) {
				// Search, expanding context for leading sibling combinators
				if ( (seed = find(
					token.matches[0].replace( runescape, funescape ),
					rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
				)) ) {

					// If seed is empty or no tokens remain, we can return early
					tokens.splice( i, 1 );
					selector = seed.length && toSelector( tokens );
					if ( !selector ) {
						push.apply( results, seed );
						return results;
					}

					break;
				}
			}
		}
	}

	// Compile and execute a filtering function if one is not provided
	// Provide `match` to avoid retokenization if we modified the selector above
	( compiled || compile( selector, match ) )(
		seed,
		context,
		!documentIsHTML,
		results,
		rsibling.test( selector ) && testContext( context.parentNode ) || context
	);
	return results;
};

// One-time assignments

// Sort stability
support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;

// Support: Chrome<14
// Always assume duplicates if they aren't passed to the comparison function
support.detectDuplicates = !!hasDuplicate;

// Initialize against the default document
setDocument();

// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
// Detached nodes confoundingly follow *each other*
support.sortDetached = assert(function( div1 ) {
	// Should return 1, but returns 4 (following)
	return div1.compareDocumentPosition( document.createElement("div") ) & 1;
});

// Support: IE<8
// Prevent attribute/property "interpolation"
// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
if ( !assert(function( div ) {
	div.innerHTML = "<a href='#'></a>";
	return div.firstChild.getAttribute("href") === "#" ;
}) ) {
	addHandle( "type|href|height|width", function( elem, name, isXML ) {
		if ( !isXML ) {
			return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
		}
	});
}

// Support: IE<9
// Use defaultValue in place of getAttribute("value")
if ( !support.attributes || !assert(function( div ) {
	div.innerHTML = "<input/>";
	div.firstChild.setAttribute( "value", "" );
	return div.firstChild.getAttribute( "value" ) === "";
}) ) {
	addHandle( "value", function( elem, name, isXML ) {
		if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
			return elem.defaultValue;
		}
	});
}

// Support: IE<9
// Use getAttributeNode to fetch booleans when getAttribute lies
if ( !assert(function( div ) {
	return div.getAttribute("disabled") == null;
}) ) {
	addHandle( booleans, function( elem, name, isXML ) {
		var val;
		if ( !isXML ) {
			return elem[ name ] === true ? name.toLowerCase() :
					(val = elem.getAttributeNode( name )) && val.specified ?
					val.value :
				null;
		}
	});
}

return Sizzle;

})( window );



jQuery.find = Sizzle;
jQuery.expr = Sizzle.selectors;
jQuery.expr[":"] = jQuery.expr.pseudos;
jQuery.unique = Sizzle.uniqueSort;
jQuery.text = Sizzle.getText;
jQuery.isXMLDoc = Sizzle.isXML;
jQuery.contains = Sizzle.contains;



var rneedsContext = jQuery.expr.match.needsContext;

var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/);



var risSimple = /^.[^:#\[\.,]*$/;

// Implement the identical functionality for filter and not
function winnow( elements, qualifier, not ) {
	if ( jQuery.isFunction( qualifier ) ) {
		return jQuery.grep( elements, function( elem, i ) {
			/* jshint -W018 */
			return !!qualifier.call( elem, i, elem ) !== not;
		});

	}

	if ( qualifier.nodeType ) {
		return jQuery.grep( elements, function( elem ) {
			return ( elem === qualifier ) !== not;
		});

	}

	if ( typeof qualifier === "string" ) {
		if ( risSimple.test( qualifier ) ) {
			return jQuery.filter( qualifier, elements, not );
		}

		qualifier = jQuery.filter( qualifier, elements );
	}

	return jQuery.grep( elements, function( elem ) {
		return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not;
	});
}

jQuery.filter = function( expr, elems, not ) {
	var elem = elems[ 0 ];

	if ( not ) {
		expr = ":not(" + expr + ")";
	}

	return elems.length === 1 && elem.nodeType === 1 ?
		jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
		jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
			return elem.nodeType === 1;
		}));
};

jQuery.fn.extend({
	find: function( selector ) {
		var i,
			ret = [],
			self = this,
			len = self.length;

		if ( typeof selector !== "string" ) {
			return this.pushStack( jQuery( selector ).filter(function() {
				for ( i = 0; i < len; i++ ) {
					if ( jQuery.contains( self[ i ], this ) ) {
						return true;
					}
				}
			}) );
		}

		for ( i = 0; i < len; i++ ) {
			jQuery.find( selector, self[ i ], ret );
		}

		// Needed because $( selector, context ) becomes $( context ).find( selector )
		ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
		ret.selector = this.selector ? this.selector + " " + selector : selector;
		return ret;
	},
	filter: function( selector ) {
		return this.pushStack( winnow(this, selector || [], false) );
	},
	not: function( selector ) {
		return this.pushStack( winnow(this, selector || [], true) );
	},
	is: function( selector ) {
		return !!winnow(
			this,

			// If this is a positional/relative selector, check membership in the returned set
			// so $("p:first").is("p:last") won't return true for a doc with two "p".
			typeof selector === "string" && rneedsContext.test( selector ) ?
				jQuery( selector ) :
				selector || [],
			false
		).length;
	}
});


// Initialize a jQuery object


// A central reference to the root jQuery(document)
var rootjQuery,

	// Use the correct document accordingly with window argument (sandbox)
	document = window.document,

	// A simple way to check for HTML strings
	// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
	// Strict HTML recognition (#11290: must start with <)
	rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,

	init = jQuery.fn.init = function( selector, context ) {
		var match, elem;

		// HANDLE: $(""), $(null), $(undefined), $(false)
		if ( !selector ) {
			return this;
		}

		// Handle HTML strings
		if ( typeof selector === "string" ) {
			if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
				// Assume that strings that start and end with <> are HTML and skip the regex check
				match = [ null, selector, null ];

			} else {
				match = rquickExpr.exec( selector );
			}

			// Match html or make sure no context is specified for #id
			if ( match && (match[1] || !context) ) {

				// HANDLE: $(html) -> $(array)
				if ( match[1] ) {
					context = context instanceof jQuery ? context[0] : context;

					// scripts is true for back-compat
					// Intentionally let the error be thrown if parseHTML is not present
					jQuery.merge( this, jQuery.parseHTML(
						match[1],
						context && context.nodeType ? context.ownerDocument || context : document,
						true
					) );

					// HANDLE: $(html, props)
					if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
						for ( match in context ) {
							// Properties of context are called as methods if possible
							if ( jQuery.isFunction( this[ match ] ) ) {
								this[ match ]( context[ match ] );

							// ...and otherwise set as attributes
							} else {
								this.attr( match, context[ match ] );
							}
						}
					}

					return this;

				// HANDLE: $(#id)
				} else {
					elem = document.getElementById( match[2] );

					// Check parentNode to catch when Blackberry 4.6 returns
					// nodes that are no longer in the document #6963
					if ( elem && elem.parentNode ) {
						// Handle the case where IE and Opera return items
						// by name instead of ID
						if ( elem.id !== match[2] ) {
							return rootjQuery.find( selector );
						}

						// Otherwise, we inject the element directly into the jQuery object
						this.length = 1;
						this[0] = elem;
					}

					this.context = document;
					this.selector = selector;
					return this;
				}

			// HANDLE: $(expr, $(...))
			} else if ( !context || context.jquery ) {
				return ( context || rootjQuery ).find( selector );

			// HANDLE: $(expr, context)
			// (which is just equivalent to: $(context).find(expr)
			} else {
				return this.constructor( context ).find( selector );
			}

		// HANDLE: $(DOMElement)
		} else if ( selector.nodeType ) {
			this.context = this[0] = selector;
			this.length = 1;
			return this;

		// HANDLE: $(function)
		// Shortcut for document ready
		} else if ( jQuery.isFunction( selector ) ) {
			return typeof rootjQuery.ready !== "undefined" ?
				rootjQuery.ready( selector ) :
				// Execute immediately if ready is not present
				selector( jQuery );
		}

		if ( selector.selector !== undefined ) {
			this.selector = selector.selector;
			this.context = selector.context;
		}

		return jQuery.makeArray( selector, this );
	};

// Give the init function the jQuery prototype for later instantiation
init.prototype = jQuery.fn;

// Initialize central reference
rootjQuery = jQuery( document );


var rparentsprev = /^(?:parents|prev(?:Until|All))/,
	// methods guaranteed to produce a unique set when starting from a unique set
	guaranteedUnique = {
		children: true,
		contents: true,
		next: true,
		prev: true
	};

jQuery.extend({
	dir: function( elem, dir, until ) {
		var matched = [],
			cur = elem[ dir ];

		while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
			if ( cur.nodeType === 1 ) {
				matched.push( cur );
			}
			cur = cur[dir];
		}
		return matched;
	},

	sibling: function( n, elem ) {
		var r = [];

		for ( ; n; n = n.nextSibling ) {
			if ( n.nodeType === 1 && n !== elem ) {
				r.push( n );
			}
		}

		return r;
	}
});

jQuery.fn.extend({
	has: function( target ) {
		var i,
			targets = jQuery( target, this ),
			len = targets.length;

		return this.filter(function() {
			for ( i = 0; i < len; i++ ) {
				if ( jQuery.contains( this, targets[i] ) ) {
					return true;
				}
			}
		});
	},

	closest: function( selectors, context ) {
		var cur,
			i = 0,
			l = this.length,
			matched = [],
			pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
				jQuery( selectors, context || this.context ) :
				0;

		for ( ; i < l; i++ ) {
			for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) {
				// Always skip document fragments
				if ( cur.nodeType < 11 && (pos ?
					pos.index(cur) > -1 :

					// Don't pass non-elements to Sizzle
					cur.nodeType === 1 &&
						jQuery.find.matchesSelector(cur, selectors)) ) {

					matched.push( cur );
					break;
				}
			}
		}

		return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched );
	},

	// Determine the position of an element within
	// the matched set of elements
	index: function( elem ) {

		// No argument, return index in parent
		if ( !elem ) {
			return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1;
		}

		// index in selector
		if ( typeof elem === "string" ) {
			return jQuery.inArray( this[0], jQuery( elem ) );
		}

		// Locate the position of the desired element
		return jQuery.inArray(
			// If it receives a jQuery object, the first element is used
			elem.jquery ? elem[0] : elem, this );
	},

	add: function( selector, context ) {
		return this.pushStack(
			jQuery.unique(
				jQuery.merge( this.get(), jQuery( selector, context ) )
			)
		);
	},

	addBack: function( selector ) {
		return this.add( selector == null ?
			this.prevObject : this.prevObject.filter(selector)
		);
	}
});

function sibling( cur, dir ) {
	do {
		cur = cur[ dir ];
	} while ( cur && cur.nodeType !== 1 );

	return cur;
}

jQuery.each({
	parent: function( elem ) {
		var parent = elem.parentNode;
		return parent && parent.nodeType !== 11 ? parent : null;
	},
	parents: function( elem ) {
		return jQuery.dir( elem, "parentNode" );
	},
	parentsUntil: function( elem, i, until ) {
		return jQuery.dir( elem, "parentNode", until );
	},
	next: function( elem ) {
		return sibling( elem, "nextSibling" );
	},
	prev: function( elem ) {
		return sibling( elem, "previousSibling" );
	},
	nextAll: function( elem ) {
		return jQuery.dir( elem, "nextSibling" );
	},
	prevAll: function( elem ) {
		return jQuery.dir( elem, "previousSibling" );
	},
	nextUntil: function( elem, i, until ) {
		return jQuery.dir( elem, "nextSibling", until );
	},
	prevUntil: function( elem, i, until ) {
		return jQuery.dir( elem, "previousSibling", until );
	},
	siblings: function( elem ) {
		return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
	},
	children: function( elem ) {
		return jQuery.sibling( elem.firstChild );
	},
	contents: function( elem ) {
		return jQuery.nodeName( elem, "iframe" ) ?
			elem.contentDocument || elem.contentWindow.document :
			jQuery.merge( [], elem.childNodes );
	}
}, function( name, fn ) {
	jQuery.fn[ name ] = function( until, selector ) {
		var ret = jQuery.map( this, fn, until );

		if ( name.slice( -5 ) !== "Until" ) {
			selector = until;
		}

		if ( selector && typeof selector === "string" ) {
			ret = jQuery.filter( selector, ret );
		}

		if ( this.length > 1 ) {
			// Remove duplicates
			if ( !guaranteedUnique[ name ] ) {
				ret = jQuery.unique( ret );
			}

			// Reverse order for parents* and prev-derivatives
			if ( rparentsprev.test( name ) ) {
				ret = ret.reverse();
			}
		}

		return this.pushStack( ret );
	};
});
var rnotwhite = (/\S+/g);



// String to Object options format cache
var optionsCache = {};

// Convert String-formatted options into Object-formatted ones and store in cache
function createOptions( options ) {
	var object = optionsCache[ options ] = {};
	jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
		object[ flag ] = true;
	});
	return object;
}

/*
 * Create a callback list using the following parameters:
 *
 *	options: an optional list of space-separated options that will change how
 *			the callback list behaves or a more traditional option object
 *
 * By default a callback list will act like an event callback list and can be
 * "fired" multiple times.
 *
 * Possible options:
 *
 *	once:			will ensure the callback list can only be fired once (like a Deferred)
 *
 *	memory:			will keep track of previous values and will call any callback added
 *					after the list has been fired right away with the latest "memorized"
 *					values (like a Deferred)
 *
 *	unique:			will ensure a callback can only be added once (no duplicate in the list)
 *
 *	stopOnFalse:	interrupt callings when a callback returns false
 *
 */
jQuery.Callbacks = function( options ) {

	// Convert options from String-formatted to Object-formatted if needed
	// (we check in cache first)
	options = typeof options === "string" ?
		( optionsCache[ options ] || createOptions( options ) ) :
		jQuery.extend( {}, options );

	var // Flag to know if list is currently firing
		firing,
		// Last fire value (for non-forgettable lists)
		memory,
		// Flag to know if list was already fired
		fired,
		// End of the loop when firing
		firingLength,
		// Index of currently firing callback (modified by remove if needed)
		firingIndex,
		// First callback to fire (used internally by add and fireWith)
		firingStart,
		// Actual callback list
		list = [],
		// Stack of fire calls for repeatable lists
		stack = !options.once && [],
		// Fire callbacks
		fire = function( data ) {
			memory = options.memory && data;
			fired = true;
			firingIndex = firingStart || 0;
			firingStart = 0;
			firingLength = list.length;
			firing = true;
			for ( ; list && firingIndex < firingLength; firingIndex++ ) {
				if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
					memory = false; // To prevent further calls using add
					break;
				}
			}
			firing = false;
			if ( list ) {
				if ( stack ) {
					if ( stack.length ) {
						fire( stack.shift() );
					}
				} else if ( memory ) {
					list = [];
				} else {
					self.disable();
				}
			}
		},
		// Actual Callbacks object
		self = {
			// Add a callback or a collection of callbacks to the list
			add: function() {
				if ( list ) {
					// First, we save the current length
					var start = list.length;
					(function add( args ) {
						jQuery.each( args, function( _, arg ) {
							var type = jQuery.type( arg );
							if ( type === "function" ) {
								if ( !options.unique || !self.has( arg ) ) {
									list.push( arg );
								}
							} else if ( arg && arg.length && type !== "string" ) {
								// Inspect recursively
								add( arg );
							}
						});
					})( arguments );
					// Do we need to add the callbacks to the
					// current firing batch?
					if ( firing ) {
						firingLength = list.length;
					// With memory, if we're not firing then
					// we should call right away
					} else if ( memory ) {
						firingStart = start;
						fire( memory );
					}
				}
				return this;
			},
			// Remove a callback from the list
			remove: function() {
				if ( list ) {
					jQuery.each( arguments, function( _, arg ) {
						var index;
						while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
							list.splice( index, 1 );
							// Handle firing indexes
							if ( firing ) {
								if ( index <= firingLength ) {
									firingLength--;
								}
								if ( index <= firingIndex ) {
									firingIndex--;
								}
							}
						}
					});
				}
				return this;
			},
			// Check if a given callback is in the list.
			// If no argument is given, return whether or not list has callbacks attached.
			has: function( fn ) {
				return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
			},
			// Remove all callbacks from the list
			empty: function() {
				list = [];
				firingLength = 0;
				return this;
			},
			// Have the list do nothing anymore
			disable: function() {
				list = stack = memory = undefined;
				return this;
			},
			// Is it disabled?
			disabled: function() {
				return !list;
			},
			// Lock the list in its current state
			lock: function() {
				stack = undefined;
				if ( !memory ) {
					self.disable();
				}
				return this;
			},
			// Is it locked?
			locked: function() {
				return !stack;
			},
			// Call all callbacks with the given context and arguments
			fireWith: function( context, args ) {
				if ( list && ( !fired || stack ) ) {
					args = args || [];
					args = [ context, args.slice ? args.slice() : args ];
					if ( firing ) {
						stack.push( args );
					} else {
						fire( args );
					}
				}
				return this;
			},
			// Call all the callbacks with the given arguments
			fire: function() {
				self.fireWith( this, arguments );
				return this;
			},
			// To know if the callbacks have already been called at least once
			fired: function() {
				return !!fired;
			}
		};

	return self;
};


jQuery.extend({

	Deferred: function( func ) {
		var tuples = [
				// action, add listener, listener list, final state
				[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
				[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
				[ "notify", "progress", jQuery.Callbacks("memory") ]
			],
			state = "pending",
			promise = {
				state: function() {
					return state;
				},
				always: function() {
					deferred.done( arguments ).fail( arguments );
					return this;
				},
				then: function( /* fnDone, fnFail, fnProgress */ ) {
					var fns = arguments;
					return jQuery.Deferred(function( newDefer ) {
						jQuery.each( tuples, function( i, tuple ) {
							var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
							// deferred[ done | fail | progress ] for forwarding actions to newDefer
							deferred[ tuple[1] ](function() {
								var returned = fn && fn.apply( this, arguments );
								if ( returned && jQuery.isFunction( returned.promise ) ) {
									returned.promise()
										.done( newDefer.resolve )
										.fail( newDefer.reject )
										.progress( newDefer.notify );
								} else {
									newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
								}
							});
						});
						fns = null;
					}).promise();
				},
				// Get a promise for this deferred
				// If obj is provided, the promise aspect is added to the object
				promise: function( obj ) {
					return obj != null ? jQuery.extend( obj, promise ) : promise;
				}
			},
			deferred = {};

		// Keep pipe for back-compat
		promise.pipe = promise.then;

		// Add list-specific methods
		jQuery.each( tuples, function( i, tuple ) {
			var list = tuple[ 2 ],
				stateString = tuple[ 3 ];

			// promise[ done | fail | progress ] = list.add
			promise[ tuple[1] ] = list.add;

			// Handle state
			if ( stateString ) {
				list.add(function() {
					// state = [ resolved | rejected ]
					state = stateString;

				// [ reject_list | resolve_list ].disable; progress_list.lock
				}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
			}

			// deferred[ resolve | reject | notify ]
			deferred[ tuple[0] ] = function() {
				deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
				return this;
			};
			deferred[ tuple[0] + "With" ] = list.fireWith;
		});

		// Make the deferred a promise
		promise.promise( deferred );

		// Call given func if any
		if ( func ) {
			func.call( deferred, deferred );
		}

		// All done!
		return deferred;
	},

	// Deferred helper
	when: function( subordinate /* , ..., subordinateN */ ) {
		var i = 0,
			resolveValues = slice.call( arguments ),
			length = resolveValues.length,

			// the count of uncompleted subordinates
			remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,

			// the master Deferred. If resolveValues consist of only a single Deferred, just use that.
			deferred = remaining === 1 ? subordinate : jQuery.Deferred(),

			// Update function for both resolve and progress values
			updateFunc = function( i, contexts, values ) {
				return function( value ) {
					contexts[ i ] = this;
					values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
					if ( values === progressValues ) {
						deferred.notifyWith( contexts, values );

					} else if ( !(--remaining) ) {
						deferred.resolveWith( contexts, values );
					}
				};
			},

			progressValues, progressContexts, resolveContexts;

		// add listeners to Deferred subordinates; treat others as resolved
		if ( length > 1 ) {
			progressValues = new Array( length );
			progressContexts = new Array( length );
			resolveContexts = new Array( length );
			for ( ; i < length; i++ ) {
				if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
					resolveValues[ i ].promise()
						.done( updateFunc( i, resolveContexts, resolveValues ) )
						.fail( deferred.reject )
						.progress( updateFunc( i, progressContexts, progressValues ) );
				} else {
					--remaining;
				}
			}
		}

		// if we're not waiting on anything, resolve the master
		if ( !remaining ) {
			deferred.resolveWith( resolveContexts, resolveValues );
		}

		return deferred.promise();
	}
});


// The deferred used on DOM ready
var readyList;

jQuery.fn.ready = function( fn ) {
	// Add the callback
	jQuery.ready.promise().done( fn );

	return this;
};

jQuery.extend({
	// Is the DOM ready to be used? Set to true once it occurs.
	isReady: false,

	// A counter to track how many items to wait for before
	// the ready event fires. See #6781
	readyWait: 1,

	// Hold (or release) the ready event
	holdReady: function( hold ) {
		if ( hold ) {
			jQuery.readyWait++;
		} else {
			jQuery.ready( true );
		}
	},

	// Handle when the DOM is ready
	ready: function( wait ) {

		// Abort if there are pending holds or we're already ready
		if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
			return;
		}

		// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
		if ( !document.body ) {
			return setTimeout( jQuery.ready );
		}

		// Remember that the DOM is ready
		jQuery.isReady = true;

		// If a normal DOM Ready event fired, decrement, and wait if need be
		if ( wait !== true && --jQuery.readyWait > 0 ) {
			return;
		}

		// If there are functions bound, to execute
		readyList.resolveWith( document, [ jQuery ] );

		// Trigger any bound ready events
		if ( jQuery.fn.triggerHandler ) {
			jQuery( document ).triggerHandler( "ready" );
			jQuery( document ).off( "ready" );
		}
	}
});

/**
 * Clean-up method for dom ready events
 */
function detach() {
	if ( document.addEventListener ) {
		document.removeEventListener( "DOMContentLoaded", completed, false );
		window.removeEventListener( "load", completed, false );

	} else {
		document.detachEvent( "onreadystatechange", completed );
		window.detachEvent( "onload", completed );
	}
}

/**
 * The ready event handler and self cleanup method
 */
function completed() {
	// readyState === "complete" is good enough for us to call the dom ready in oldIE
	if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) {
		detach();
		jQuery.ready();
	}
}

jQuery.ready.promise = function( obj ) {
	if ( !readyList ) {

		readyList = jQuery.Deferred();

		// Catch cases where $(document).ready() is called after the browser event has already occurred.
		// we once tried to use readyState "interactive" here, but it caused issues like the one
		// discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
		if ( document.readyState === "complete" ) {
			// Handle it asynchronously to allow scripts the opportunity to delay ready
			setTimeout( jQuery.ready );

		// Standards-based browsers support DOMContentLoaded
		} else if ( document.addEventListener ) {
			// Use the handy event callback
			document.addEventListener( "DOMContentLoaded", completed, false );

			// A fallback to window.onload, that will always work
			window.addEventListener( "load", completed, false );

		// If IE event model is used
		} else {
			// Ensure firing before onload, maybe late but safe also for iframes
			document.attachEvent( "onreadystatechange", completed );

			// A fallback to window.onload, that will always work
			window.attachEvent( "onload", completed );

			// If IE and not a frame
			// continually check to see if the document is ready
			var top = false;

			try {
				top = window.frameElement == null && document.documentElement;
			} catch(e) {}

			if ( top && top.doScroll ) {
				(function doScrollCheck() {
					if ( !jQuery.isReady ) {

						try {
							// Use the trick by Diego Perini
							// http://javascript.nwbox.com/IEContentLoaded/
							top.doScroll("left");
						} catch(e) {
							return setTimeout( doScrollCheck, 50 );
						}

						// detach all dom ready events
						detach();

						// and execute any waiting functions
						jQuery.ready();
					}
				})();
			}
		}
	}
	return readyList.promise( obj );
};


var strundefined = typeof undefined;



// Support: IE<9
// Iteration over object's inherited properties before its own
var i;
for ( i in jQuery( support ) ) {
	break;
}
support.ownLast = i !== "0";

// Note: most support tests are defined in their respective modules.
// false until the test is run
support.inlineBlockNeedsLayout = false;

// Execute ASAP in case we need to set body.style.zoom
jQuery(function() {
	// Minified: var a,b,c,d
	var val, div, body, container;

	body = document.getElementsByTagName( "body" )[ 0 ];
	if ( !body || !body.style ) {
		// Return for frameset docs that don't have a body
		return;
	}

	// Setup
	div = document.createElement( "div" );
	container = document.createElement( "div" );
	container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px";
	body.appendChild( container ).appendChild( div );

	if ( typeof div.style.zoom !== strundefined ) {
		// Support: IE<8
		// Check if natively block-level elements act like inline-block
		// elements when setting their display to 'inline' and giving
		// them layout
		div.style.cssText = "display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1";

		support.inlineBlockNeedsLayout = val = div.offsetWidth === 3;
		if ( val ) {
			// Prevent IE 6 from affecting layout for positioned elements #11048
			// Prevent IE from shrinking the body in IE 7 mode #12869
			// Support: IE<8
			body.style.zoom = 1;
		}
	}

	body.removeChild( container );
});




(function() {
	var div = document.createElement( "div" );

	// Execute the test only if not already executed in another module.
	if (support.deleteExpando == null) {
		// Support: IE<9
		support.deleteExpando = true;
		try {
			delete div.test;
		} catch( e ) {
			support.deleteExpando = false;
		}
	}

	// Null elements to avoid leaks in IE.
	div = null;
})();


/**
 * Determines whether an object can have data
 */
jQuery.acceptData = function( elem ) {
	var noData = jQuery.noData[ (elem.nodeName + " ").toLowerCase() ],
		nodeType = +elem.nodeType || 1;

	// Do not set data on non-element DOM nodes because it will not be cleared (#8335).
	return nodeType !== 1 && nodeType !== 9 ?
		false :

		// Nodes accept data unless otherwise specified; rejection can be conditional
		!noData || noData !== true && elem.getAttribute("classid") === noData;
};


var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
	rmultiDash = /([A-Z])/g;

function dataAttr( elem, key, data ) {
	// If nothing was found internally, try to fetch any
	// data from the HTML5 data-* attribute
	if ( data === undefined && elem.nodeType === 1 ) {

		var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();

		data = elem.getAttribute( name );

		if ( typeof data === "string" ) {
			try {
				data = data === "true" ? true :
					data === "false" ? false :
					data === "null" ? null :
					// Only convert to a number if it doesn't change the string
					+data + "" === data ? +data :
					rbrace.test( data ) ? jQuery.parseJSON( data ) :
					data;
			} catch( e ) {}

			// Make sure we set the data so it isn't changed later
			jQuery.data( elem, key, data );

		} else {
			data = undefined;
		}
	}

	return data;
}

// checks a cache object for emptiness
function isEmptyDataObject( obj ) {
	var name;
	for ( name in obj ) {

		// if the public data object is empty, the private is still empty
		if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
			continue;
		}
		if ( name !== "toJSON" ) {
			return false;
		}
	}

	return true;
}

function internalData( elem, name, data, pvt /* Internal Use Only */ ) {
	if ( !jQuery.acceptData( elem ) ) {
		return;
	}

	var ret, thisCache,
		internalKey = jQuery.expando,

		// We have to handle DOM nodes and JS objects differently because IE6-7
		// can't GC object references properly across the DOM-JS boundary
		isNode = elem.nodeType,

		// Only DOM nodes need the global jQuery cache; JS object data is
		// attached directly to the object so GC can occur automatically
		cache = isNode ? jQuery.cache : elem,

		// Only defining an ID for JS objects if its cache already exists allows
		// the code to shortcut on the same path as a DOM node with no cache
		id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;

	// Avoid doing any more work than we need to when trying to get data on an
	// object that has no data at all
	if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) {
		return;
	}

	if ( !id ) {
		// Only DOM nodes need a new unique ID for each element since their data
		// ends up in the global cache
		if ( isNode ) {
			id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++;
		} else {
			id = internalKey;
		}
	}

	if ( !cache[ id ] ) {
		// Avoid exposing jQuery metadata on plain JS objects when the object
		// is serialized using JSON.stringify
		cache[ id ] = isNode ? {} : { toJSON: jQuery.noop };
	}

	// An object can be passed to jQuery.data instead of a key/value pair; this gets
	// shallow copied over onto the existing cache
	if ( typeof name === "object" || typeof name === "function" ) {
		if ( pvt ) {
			cache[ id ] = jQuery.extend( cache[ id ], name );
		} else {
			cache[ id ].data = jQuery.extend( cache[ id ].data, name );
		}
	}

	thisCache = cache[ id ];

	// jQuery data() is stored in a separate object inside the object's internal data
	// cache in order to avoid key collisions between internal data and user-defined
	// data.
	if ( !pvt ) {
		if ( !thisCache.data ) {
			thisCache.data = {};
		}

		thisCache = thisCache.data;
	}

	if ( data !== undefined ) {
		thisCache[ jQuery.camelCase( name ) ] = data;
	}

	// Check for both converted-to-camel and non-converted data property names
	// If a data property was specified
	if ( typeof name === "string" ) {

		// First Try to find as-is property data
		ret = thisCache[ name ];

		// Test for null|undefined property data
		if ( ret == null ) {

			// Try to find the camelCased property
			ret = thisCache[ jQuery.camelCase( name ) ];
		}
	} else {
		ret = thisCache;
	}

	return ret;
}

function internalRemoveData( elem, name, pvt ) {
	if ( !jQuery.acceptData( elem ) ) {
		return;
	}

	var thisCache, i,
		isNode = elem.nodeType,

		// See jQuery.data for more information
		cache = isNode ? jQuery.cache : elem,
		id = isNode ? elem[ jQuery.expando ] : jQuery.expando;

	// If there is already no cache entry for this object, there is no
	// purpose in continuing
	if ( !cache[ id ] ) {
		return;
	}

	if ( name ) {

		thisCache = pvt ? cache[ id ] : cache[ id ].data;

		if ( thisCache ) {

			// Support array or space separated string names for data keys
			if ( !jQuery.isArray( name ) ) {

				// try the string as a key before any manipulation
				if ( name in thisCache ) {
					name = [ name ];
				} else {

					// split the camel cased version by spaces unless a key with the spaces exists
					name = jQuery.camelCase( name );
					if ( name in thisCache ) {
						name = [ name ];
					} else {
						name = name.split(" ");
					}
				}
			} else {
				// If "name" is an array of keys...
				// When data is initially created, via ("key", "val") signature,
				// keys will be converted to camelCase.
				// Since there is no way to tell _how_ a key was added, remove
				// both plain key and camelCase key. #12786
				// This will only penalize the array argument path.
				name = name.concat( jQuery.map( name, jQuery.camelCase ) );
			}

			i = name.length;
			while ( i-- ) {
				delete thisCache[ name[i] ];
			}

			// If there is no data left in the cache, we want to continue
			// and let the cache object itself get destroyed
			if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) {
				return;
			}
		}
	}

	// See jQuery.data for more information
	if ( !pvt ) {
		delete cache[ id ].data;

		// Don't destroy the parent cache unless the internal data object
		// had been the only thing left in it
		if ( !isEmptyDataObject( cache[ id ] ) ) {
			return;
		}
	}

	// Destroy the cache
	if ( isNode ) {
		jQuery.cleanData( [ elem ], true );

	// Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
	/* jshint eqeqeq: false */
	} else if ( support.deleteExpando || cache != cache.window ) {
		/* jshint eqeqeq: true */
		delete cache[ id ];

	// When all else fails, null
	} else {
		cache[ id ] = null;
	}
}

jQuery.extend({
	cache: {},

	// The following elements (space-suffixed to avoid Object.prototype collisions)
	// throw uncatchable exceptions if you attempt to set expando properties
	noData: {
		"applet ": true,
		"embed ": true,
		// ...but Flash objects (which have this classid) *can* handle expandos
		"object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
	},

	hasData: function( elem ) {
		elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
		return !!elem && !isEmptyDataObject( elem );
	},

	data: function( elem, name, data ) {
		return internalData( elem, name, data );
	},

	removeData: function( elem, name ) {
		return internalRemoveData( elem, name );
	},

	// For internal use only.
	_data: function( elem, name, data ) {
		return internalData( elem, name, data, true );
	},

	_removeData: function( elem, name ) {
		return internalRemoveData( elem, name, true );
	}
});

jQuery.fn.extend({
	data: function( key, value ) {
		var i, name, data,
			elem = this[0],
			attrs = elem && elem.attributes;

		// Special expections of .data basically thwart jQuery.access,
		// so implement the relevant behavior ourselves

		// Gets all values
		if ( key === undefined ) {
			if ( this.length ) {
				data = jQuery.data( elem );

				if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
					i = attrs.length;
					while ( i-- ) {

						// Support: IE11+
						// The attrs elements can be null (#14894)
						if ( attrs[ i ] ) {
							name = attrs[ i ].name;
							if ( name.indexOf( "data-" ) === 0 ) {
								name = jQuery.camelCase( name.slice(5) );
								dataAttr( elem, name, data[ name ] );
							}
						}
					}
					jQuery._data( elem, "parsedAttrs", true );
				}
			}

			return data;
		}

		// Sets multiple values
		if ( typeof key === "object" ) {
			return this.each(function() {
				jQuery.data( this, key );
			});
		}

		return arguments.length > 1 ?

			// Sets one value
			this.each(function() {
				jQuery.data( this, key, value );
			}) :

			// Gets one value
			// Try to fetch any internally stored data first
			elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined;
	},

	removeData: function( key ) {
		return this.each(function() {
			jQuery.removeData( this, key );
		});
	}
});


jQuery.extend({
	queue: function( elem, type, data ) {
		var queue;

		if ( elem ) {
			type = ( type || "fx" ) + "queue";
			queue = jQuery._data( elem, type );

			// Speed up dequeue by getting out quickly if this is just a lookup
			if ( data ) {
				if ( !queue || jQuery.isArray(data) ) {
					queue = jQuery._data( elem, type, jQuery.makeArray(data) );
				} else {
					queue.push( data );
				}
			}
			return queue || [];
		}
	},

	dequeue: function( elem, type ) {
		type = type || "fx";

		var queue = jQuery.queue( elem, type ),
			startLength = queue.length,
			fn = queue.shift(),
			hooks = jQuery._queueHooks( elem, type ),
			next = function() {
				jQuery.dequeue( elem, type );
			};

		// If the fx queue is dequeued, always remove the progress sentinel
		if ( fn === "inprogress" ) {
			fn = queue.shift();
			startLength--;
		}

		if ( fn ) {

			// Add a progress sentinel to prevent the fx queue from being
			// automatically dequeued
			if ( type === "fx" ) {
				queue.unshift( "inprogress" );
			}

			// clear up the last queue stop function
			delete hooks.stop;
			fn.call( elem, next, hooks );
		}

		if ( !startLength && hooks ) {
			hooks.empty.fire();
		}
	},

	// not intended for public consumption - generates a queueHooks object, or returns the current one
	_queueHooks: function( elem, type ) {
		var key = type + "queueHooks";
		return jQuery._data( elem, key ) || jQuery._data( elem, key, {
			empty: jQuery.Callbacks("once memory").add(function() {
				jQuery._removeData( elem, type + "queue" );
				jQuery._removeData( elem, key );
			})
		});
	}
});

jQuery.fn.extend({
	queue: function( type, data ) {
		var setter = 2;

		if ( typeof type !== "string" ) {
			data = type;
			type = "fx";
			setter--;
		}

		if ( arguments.length < setter ) {
			return jQuery.queue( this[0], type );
		}

		return data === undefined ?
			this :
			this.each(function() {
				var queue = jQuery.queue( this, type, data );

				// ensure a hooks for this queue
				jQuery._queueHooks( this, type );

				if ( type === "fx" && queue[0] !== "inprogress" ) {
					jQuery.dequeue( this, type );
				}
			});
	},
	dequeue: function( type ) {
		return this.each(function() {
			jQuery.dequeue( this, type );
		});
	},
	clearQueue: function( type ) {
		return this.queue( type || "fx", [] );
	},
	// Get a promise resolved when queues of a certain type
	// are emptied (fx is the type by default)
	promise: function( type, obj ) {
		var tmp,
			count = 1,
			defer = jQuery.Deferred(),
			elements = this,
			i = this.length,
			resolve = function() {
				if ( !( --count ) ) {
					defer.resolveWith( elements, [ elements ] );
				}
			};

		if ( typeof type !== "string" ) {
			obj = type;
			type = undefined;
		}
		type = type || "fx";

		while ( i-- ) {
			tmp = jQuery._data( elements[ i ], type + "queueHooks" );
			if ( tmp && tmp.empty ) {
				count++;
				tmp.empty.add( resolve );
			}
		}
		resolve();
		return defer.promise( obj );
	}
});
var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source;

var cssExpand = [ "Top", "Right", "Bottom", "Left" ];

var isHidden = function( elem, el ) {
		// isHidden might be called from jQuery#filter function;
		// in that case, element will be second argument
		elem = el || elem;
		return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
	};



// Multifunctional method to get and set values of a collection
// The value/s can optionally be executed if it's a function
var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
	var i = 0,
		length = elems.length,
		bulk = key == null;

	// Sets many values
	if ( jQuery.type( key ) === "object" ) {
		chainable = true;
		for ( i in key ) {
			jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
		}

	// Sets one value
	} else if ( value !== undefined ) {
		chainable = true;

		if ( !jQuery.isFunction( value ) ) {
			raw = true;
		}

		if ( bulk ) {
			// Bulk operations run against the entire set
			if ( raw ) {
				fn.call( elems, value );
				fn = null;

			// ...except when executing function values
			} else {
				bulk = fn;
				fn = function( elem, key, value ) {
					return bulk.call( jQuery( elem ), value );
				};
			}
		}

		if ( fn ) {
			for ( ; i < length; i++ ) {
				fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
			}
		}
	}

	return chainable ?
		elems :

		// Gets
		bulk ?
			fn.call( elems ) :
			length ? fn( elems[0], key ) : emptyGet;
};
var rcheckableType = (/^(?:checkbox|radio)$/i);



(function() {
	// Minified: var a,b,c
	var input = document.createElement( "input" ),
		div = document.createElement( "div" ),
		fragment = document.createDocumentFragment();

	// Setup
	div.innerHTML = "  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";

	// IE strips leading whitespace when .innerHTML is used
	support.leadingWhitespace = div.firstChild.nodeType === 3;

	// Make sure that tbody elements aren't automatically inserted
	// IE will insert them into empty tables
	support.tbody = !div.getElementsByTagName( "tbody" ).length;

	// Make sure that link elements get serialized correctly by innerHTML
	// This requires a wrapper element in IE
	support.htmlSerialize = !!div.getElementsByTagName( "link" ).length;

	// Makes sure cloning an html5 element does not cause problems
	// Where outerHTML is undefined, this still works
	support.html5Clone =
		document.createElement( "nav" ).cloneNode( true ).outerHTML !== "<:nav></:nav>";

	// Check if a disconnected checkbox will retain its checked
	// value of true after appended to the DOM (IE6/7)
	input.type = "checkbox";
	input.checked = true;
	fragment.appendChild( input );
	support.appendChecked = input.checked;

	// Make sure textarea (and checkbox) defaultValue is properly cloned
	// Support: IE6-IE11+
	div.innerHTML = "<textarea>x</textarea>";
	support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;

	// #11217 - WebKit loses check when the name is after the checked attribute
	fragment.appendChild( div );
	div.innerHTML = "<input type='radio' checked='checked' name='t'/>";

	// Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3
	// old WebKit doesn't clone checked state correctly in fragments
	support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;

	// Support: IE<9
	// Opera does not clone events (and typeof div.attachEvent === undefined).
	// IE9-10 clones events bound via attachEvent, but they don't trigger with .click()
	support.noCloneEvent = true;
	if ( div.attachEvent ) {
		div.attachEvent( "onclick", function() {
			support.noCloneEvent = false;
		});

		div.cloneNode( true ).click();
	}

	// Execute the test only if not already executed in another module.
	if (support.deleteExpando == null) {
		// Support: IE<9
		support.deleteExpando = true;
		try {
			delete div.test;
		} catch( e ) {
			support.deleteExpando = false;
		}
	}
})();


(function() {
	var i, eventName,
		div = document.createElement( "div" );

	// Support: IE<9 (lack submit/change bubble), Firefox 23+ (lack focusin event)
	for ( i in { submit: true, change: true, focusin: true }) {
		eventName = "on" + i;

		if ( !(support[ i + "Bubbles" ] = eventName in window) ) {
			// Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP)
			div.setAttribute( eventName, "t" );
			support[ i + "Bubbles" ] = div.attributes[ eventName ].expando === false;
		}
	}

	// Null elements to avoid leaks in IE.
	div = null;
})();


var rformElems = /^(?:input|select|textarea)$/i,
	rkeyEvent = /^key/,
	rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/,
	rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
	rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;

function returnTrue() {
	return true;
}

function returnFalse() {
	return false;
}

function safeActiveElement() {
	try {
		return document.activeElement;
	} catch ( err ) { }
}

/*
 * Helper functions for managing events -- not part of the public interface.
 * Props to Dean Edwards' addEvent library for many of the ideas.
 */
jQuery.event = {

	global: {},

	add: function( elem, types, handler, data, selector ) {
		var tmp, events, t, handleObjIn,
			special, eventHandle, handleObj,
			handlers, type, namespaces, origType,
			elemData = jQuery._data( elem );

		// Don't attach events to noData or text/comment nodes (but allow plain objects)
		if ( !elemData ) {
			return;
		}

		// Caller can pass in an object of custom data in lieu of the handler
		if ( handler.handler ) {
			handleObjIn = handler;
			handler = handleObjIn.handler;
			selector = handleObjIn.selector;
		}

		// Make sure that the handler has a unique ID, used to find/remove it later
		if ( !handler.guid ) {
			handler.guid = jQuery.guid++;
		}

		// Init the element's event structure and main handler, if this is the first
		if ( !(events = elemData.events) ) {
			events = elemData.events = {};
		}
		if ( !(eventHandle = elemData.handle) ) {
			eventHandle = elemData.handle = function( e ) {
				// Discard the second event of a jQuery.event.trigger() and
				// when an event is called after a page has unloaded
				return typeof jQuery !== strundefined && (!e || jQuery.event.triggered !== e.type) ?
					jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
					undefined;
			};
			// Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
			eventHandle.elem = elem;
		}

		// Handle multiple events separated by a space
		types = ( types || "" ).match( rnotwhite ) || [ "" ];
		t = types.length;
		while ( t-- ) {
			tmp = rtypenamespace.exec( types[t] ) || [];
			type = origType = tmp[1];
			namespaces = ( tmp[2] || "" ).split( "." ).sort();

			// There *must* be a type, no attaching namespace-only handlers
			if ( !type ) {
				continue;
			}

			// If event changes its type, use the special event handlers for the changed type
			special = jQuery.event.special[ type ] || {};

			// If selector defined, determine special event api type, otherwise given type
			type = ( selector ? special.delegateType : special.bindType ) || type;

			// Update special based on newly reset type
			special = jQuery.event.special[ type ] || {};

			// handleObj is passed to all event handlers
			handleObj = jQuery.extend({
				type: type,
				origType: origType,
				data: data,
				handler: handler,
				guid: handler.guid,
				selector: selector,
				needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
				namespace: namespaces.join(".")
			}, handleObjIn );

			// Init the event handler queue if we're the first
			if ( !(handlers = events[ type ]) ) {
				handlers = events[ type ] = [];
				handlers.delegateCount = 0;

				// Only use addEventListener/attachEvent if the special events handler returns false
				if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
					// Bind the global event handler to the element
					if ( elem.addEventListener ) {
						elem.addEventListener( type, eventHandle, false );

					} else if ( elem.attachEvent ) {
						elem.attachEvent( "on" + type, eventHandle );
					}
				}
			}

			if ( special.add ) {
				special.add.call( elem, handleObj );

				if ( !handleObj.handler.guid ) {
					handleObj.handler.guid = handler.guid;
				}
			}

			// Add to the element's handler list, delegates in front
			if ( selector ) {
				handlers.splice( handlers.delegateCount++, 0, handleObj );
			} else {
				handlers.push( handleObj );
			}

			// Keep track of which events have ever been used, for event optimization
			jQuery.event.global[ type ] = true;
		}

		// Nullify elem to prevent memory leaks in IE
		elem = null;
	},

	// Detach an event or set of events from an element
	remove: function( elem, types, handler, selector, mappedTypes ) {
		var j, handleObj, tmp,
			origCount, t, events,
			special, handlers, type,
			namespaces, origType,
			elemData = jQuery.hasData( elem ) && jQuery._data( elem );

		if ( !elemData || !(events = elemData.events) ) {
			return;
		}

		// Once for each type.namespace in types; type may be omitted
		types = ( types || "" ).match( rnotwhite ) || [ "" ];
		t = types.length;
		while ( t-- ) {
			tmp = rtypenamespace.exec( types[t] ) || [];
			type = origType = tmp[1];
			namespaces = ( tmp[2] || "" ).split( "." ).sort();

			// Unbind all events (on this namespace, if provided) for the element
			if ( !type ) {
				for ( type in events ) {
					jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
				}
				continue;
			}

			special = jQuery.event.special[ type ] || {};
			type = ( selector ? special.delegateType : special.bindType ) || type;
			handlers = events[ type ] || [];
			tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );

			// Remove matching events
			origCount = j = handlers.length;
			while ( j-- ) {
				handleObj = handlers[ j ];

				if ( ( mappedTypes || origType === handleObj.origType ) &&
					( !handler || handler.guid === handleObj.guid ) &&
					( !tmp || tmp.test( handleObj.namespace ) ) &&
					( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
					handlers.splice( j, 1 );

					if ( handleObj.selector ) {
						handlers.delegateCount--;
					}
					if ( special.remove ) {
						special.remove.call( elem, handleObj );
					}
				}
			}

			// Remove generic event handler if we removed something and no more handlers exist
			// (avoids potential for endless recursion during removal of special event handlers)
			if ( origCount && !handlers.length ) {
				if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
					jQuery.removeEvent( elem, type, elemData.handle );
				}

				delete events[ type ];
			}
		}

		// Remove the expando if it's no longer used
		if ( jQuery.isEmptyObject( events ) ) {
			delete elemData.handle;

			// removeData also checks for emptiness and clears the expando if empty
			// so use it instead of delete
			jQuery._removeData( elem, "events" );
		}
	},

	trigger: function( event, data, elem, onlyHandlers ) {
		var handle, ontype, cur,
			bubbleType, special, tmp, i,
			eventPath = [ elem || document ],
			type = hasOwn.call( event, "type" ) ? event.type : event,
			namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];

		cur = tmp = elem = elem || document;

		// Don't do events on text and comment nodes
		if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
			return;
		}

		// focus/blur morphs to focusin/out; ensure we're not firing them right now
		if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
			return;
		}

		if ( type.indexOf(".") >= 0 ) {
			// Namespaced trigger; create a regexp to match event type in handle()
			namespaces = type.split(".");
			type = namespaces.shift();
			namespaces.sort();
		}
		ontype = type.indexOf(":") < 0 && "on" + type;

		// Caller can pass in a jQuery.Event object, Object, or just an event type string
		event = event[ jQuery.expando ] ?
			event :
			new jQuery.Event( type, typeof event === "object" && event );

		// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
		event.isTrigger = onlyHandlers ? 2 : 3;
		event.namespace = namespaces.join(".");
		event.namespace_re = event.namespace ?
			new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
			null;

		// Clean up the event in case it is being reused
		event.result = undefined;
		if ( !event.target ) {
			event.target = elem;
		}

		// Clone any incoming data and prepend the event, creating the handler arg list
		data = data == null ?
			[ event ] :
			jQuery.makeArray( data, [ event ] );

		// Allow special events to draw outside the lines
		special = jQuery.event.special[ type ] || {};
		if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
			return;
		}

		// Determine event propagation path in advance, per W3C events spec (#9951)
		// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
		if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {

			bubbleType = special.delegateType || type;
			if ( !rfocusMorph.test( bubbleType + type ) ) {
				cur = cur.parentNode;
			}
			for ( ; cur; cur = cur.parentNode ) {
				eventPath.push( cur );
				tmp = cur;
			}

			// Only add window if we got to document (e.g., not plain obj or detached DOM)
			if ( tmp === (elem.ownerDocument || document) ) {
				eventPath.push( tmp.defaultView || tmp.parentWindow || window );
			}
		}

		// Fire handlers on the event path
		i = 0;
		while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {

			event.type = i > 1 ?
				bubbleType :
				special.bindType || type;

			// jQuery handler
			handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
			if ( handle ) {
				handle.apply( cur, data );
			}

			// Native handler
			handle = ontype && cur[ ontype ];
			if ( handle && handle.apply && jQuery.acceptData( cur ) ) {
				event.result = handle.apply( cur, data );
				if ( event.result === false ) {
					event.preventDefault();
				}
			}
		}
		event.type = type;

		// If nobody prevented the default action, do it now
		if ( !onlyHandlers && !event.isDefaultPrevented() ) {

			if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&
				jQuery.acceptData( elem ) ) {

				// Call a native DOM method on the target with the same name name as the event.
				// Can't use an .isFunction() check here because IE6/7 fails that test.
				// Don't do default actions on window, that's where global variables be (#6170)
				if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) {

					// Don't re-trigger an onFOO event when we call its FOO() method
					tmp = elem[ ontype ];

					if ( tmp ) {
						elem[ ontype ] = null;
					}

					// Prevent re-triggering of the same event, since we already bubbled it above
					jQuery.event.triggered = type;
					try {
						elem[ type ]();
					} catch ( e ) {
						// IE<9 dies on focus/blur to hidden element (#1486,#12518)
						// only reproducible on winXP IE8 native, not IE9 in IE8 mode
					}
					jQuery.event.triggered = undefined;

					if ( tmp ) {
						elem[ ontype ] = tmp;
					}
				}
			}
		}

		return event.result;
	},

	dispatch: function( event ) {

		// Make a writable jQuery.Event from the native event object
		event = jQuery.event.fix( event );

		var i, ret, handleObj, matched, j,
			handlerQueue = [],
			args = slice.call( arguments ),
			handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [],
			special = jQuery.event.special[ event.type ] || {};

		// Use the fix-ed jQuery.Event rather than the (read-only) native event
		args[0] = event;
		event.delegateTarget = this;

		// Call the preDispatch hook for the mapped type, and let it bail if desired
		if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
			return;
		}

		// Determine handlers
		handlerQueue = jQuery.event.handlers.call( this, event, handlers );

		// Run delegates first; they may want to stop propagation beneath us
		i = 0;
		while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
			event.currentTarget = matched.elem;

			j = 0;
			while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {

				// Triggered event must either 1) have no namespace, or
				// 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
				if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {

					event.handleObj = handleObj;
					event.data = handleObj.data;

					ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
							.apply( matched.elem, args );

					if ( ret !== undefined ) {
						if ( (event.result = ret) === false ) {
							event.preventDefault();
							event.stopPropagation();
						}
					}
				}
			}
		}

		// Call the postDispatch hook for the mapped type
		if ( special.postDispatch ) {
			special.postDispatch.call( this, event );
		}

		return event.result;
	},

	handlers: function( event, handlers ) {
		var sel, handleObj, matches, i,
			handlerQueue = [],
			delegateCount = handlers.delegateCount,
			cur = event.target;

		// Find delegate handlers
		// Black-hole SVG <use> instance trees (#13180)
		// Avoid non-left-click bubbling in Firefox (#3861)
		if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {

			/* jshint eqeqeq: false */
			for ( ; cur != this; cur = cur.parentNode || this ) {
				/* jshint eqeqeq: true */

				// Don't check non-elements (#13208)
				// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
				if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) {
					matches = [];
					for ( i = 0; i < delegateCount; i++ ) {
						handleObj = handlers[ i ];

						// Don't conflict with Object.prototype properties (#13203)
						sel = handleObj.selector + " ";

						if ( matches[ sel ] === undefined ) {
							matches[ sel ] = handleObj.needsContext ?
								jQuery( sel, this ).index( cur ) >= 0 :
								jQuery.find( sel, this, null, [ cur ] ).length;
						}
						if ( matches[ sel ] ) {
							matches.push( handleObj );
						}
					}
					if ( matches.length ) {
						handlerQueue.push({ elem: cur, handlers: matches });
					}
				}
			}
		}

		// Add the remaining (directly-bound) handlers
		if ( delegateCount < handlers.length ) {
			handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
		}

		return handlerQueue;
	},

	fix: function( event ) {
		if ( event[ jQuery.expando ] ) {
			return event;
		}

		// Create a writable copy of the event object and normalize some properties
		var i, prop, copy,
			type = event.type,
			originalEvent = event,
			fixHook = this.fixHooks[ type ];

		if ( !fixHook ) {
			this.fixHooks[ type ] = fixHook =
				rmouseEvent.test( type ) ? this.mouseHooks :
				rkeyEvent.test( type ) ? this.keyHooks :
				{};
		}
		copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;

		event = new jQuery.Event( originalEvent );

		i = copy.length;
		while ( i-- ) {
			prop = copy[ i ];
			event[ prop ] = originalEvent[ prop ];
		}

		// Support: IE<9
		// Fix target property (#1925)
		if ( !event.target ) {
			event.target = originalEvent.srcElement || document;
		}

		// Support: Chrome 23+, Safari?
		// Target should not be a text node (#504, #13143)
		if ( event.target.nodeType === 3 ) {
			event.target = event.target.parentNode;
		}

		// Support: IE<9
		// For mouse/key events, metaKey==false if it's undefined (#3368, #11328)
		event.metaKey = !!event.metaKey;

		return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
	},

	// Includes some event props shared by KeyEvent and MouseEvent
	props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),

	fixHooks: {},

	keyHooks: {
		props: "char charCode key keyCode".split(" "),
		filter: function( event, original ) {

			// Add which for key events
			if ( event.which == null ) {
				event.which = original.charCode != null ? original.charCode : original.keyCode;
			}

			return event;
		}
	},

	mouseHooks: {
		props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
		filter: function( event, original ) {
			var body, eventDoc, doc,
				button = original.button,
				fromElement = original.fromElement;

			// Calculate pageX/Y if missing and clientX/Y available
			if ( event.pageX == null && original.clientX != null ) {
				eventDoc = event.target.ownerDocument || document;
				doc = eventDoc.documentElement;
				body = eventDoc.body;

				event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
				event.pageY = original.clientY + ( doc && doc.scrollTop  || body && body.scrollTop  || 0 ) - ( doc && doc.clientTop  || body && body.clientTop  || 0 );
			}

			// Add relatedTarget, if necessary
			if ( !event.relatedTarget && fromElement ) {
				event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
			}

			// Add which for click: 1 === left; 2 === middle; 3 === right
			// Note: button is not normalized, so don't use it
			if ( !event.which && button !== undefined ) {
				event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
			}

			return event;
		}
	},

	special: {
		load: {
			// Prevent triggered image.load events from bubbling to window.load
			noBubble: true
		},
		focus: {
			// Fire native event if possible so blur/focus sequence is correct
			trigger: function() {
				if ( this !== safeActiveElement() && this.focus ) {
					try {
						this.focus();
						return false;
					} catch ( e ) {
						// Support: IE<9
						// If we error on focus to hidden element (#1486, #12518),
						// let .trigger() run the handlers
					}
				}
			},
			delegateType: "focusin"
		},
		blur: {
			trigger: function() {
				if ( this === safeActiveElement() && this.blur ) {
					this.blur();
					return false;
				}
			},
			delegateType: "focusout"
		},
		click: {
			// For checkbox, fire native event so checked state will be right
			trigger: function() {
				if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) {
					this.click();
					return false;
				}
			},

			// For cross-browser consistency, don't fire native .click() on links
			_default: function( event ) {
				return jQuery.nodeName( event.target, "a" );
			}
		},

		beforeunload: {
			postDispatch: function( event ) {

				// Support: Firefox 20+
				// Firefox doesn't alert if the returnValue field is not set.
				if ( event.result !== undefined && event.originalEvent ) {
					event.originalEvent.returnValue = event.result;
				}
			}
		}
	},

	simulate: function( type, elem, event, bubble ) {
		// Piggyback on a donor event to simulate a different one.
		// Fake originalEvent to avoid donor's stopPropagation, but if the
		// simulated event prevents default then we do the same on the donor.
		var e = jQuery.extend(
			new jQuery.Event(),
			event,
			{
				type: type,
				isSimulated: true,
				originalEvent: {}
			}
		);
		if ( bubble ) {
			jQuery.event.trigger( e, null, elem );
		} else {
			jQuery.event.dispatch.call( elem, e );
		}
		if ( e.isDefaultPrevented() ) {
			event.preventDefault();
		}
	}
};

jQuery.removeEvent = document.removeEventListener ?
	function( elem, type, handle ) {
		if ( elem.removeEventListener ) {
			elem.removeEventListener( type, handle, false );
		}
	} :
	function( elem, type, handle ) {
		var name = "on" + type;

		if ( elem.detachEvent ) {

			// #8545, #7054, preventing memory leaks for custom events in IE6-8
			// detachEvent needed property on element, by name of that event, to properly expose it to GC
			if ( typeof elem[ name ] === strundefined ) {
				elem[ name ] = null;
			}

			elem.detachEvent( name, handle );
		}
	};

jQuery.Event = function( src, props ) {
	// Allow instantiation without the 'new' keyword
	if ( !(this instanceof jQuery.Event) ) {
		return new jQuery.Event( src, props );
	}

	// Event object
	if ( src && src.type ) {
		this.originalEvent = src;
		this.type = src.type;

		// Events bubbling up the document may have been marked as prevented
		// by a handler lower down the tree; reflect the correct value.
		this.isDefaultPrevented = src.defaultPrevented ||
				src.defaultPrevented === undefined &&
				// Support: IE < 9, Android < 4.0
				src.returnValue === false ?
			returnTrue :
			returnFalse;

	// Event type
	} else {
		this.type = src;
	}

	// Put explicitly provided properties onto the event object
	if ( props ) {
		jQuery.extend( this, props );
	}

	// Create a timestamp if incoming event doesn't have one
	this.timeStamp = src && src.timeStamp || jQuery.now();

	// Mark it as fixed
	this[ jQuery.expando ] = true;
};

// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
jQuery.Event.prototype = {
	isDefaultPrevented: returnFalse,
	isPropagationStopped: returnFalse,
	isImmediatePropagationStopped: returnFalse,

	preventDefault: function() {
		var e = this.originalEvent;

		this.isDefaultPrevented = returnTrue;
		if ( !e ) {
			return;
		}

		// If preventDefault exists, run it on the original event
		if ( e.preventDefault ) {
			e.preventDefault();

		// Support: IE
		// Otherwise set the returnValue property of the original event to false
		} else {
			e.returnValue = false;
		}
	},
	stopPropagation: function() {
		var e = this.originalEvent;

		this.isPropagationStopped = returnTrue;
		if ( !e ) {
			return;
		}
		// If stopPropagation exists, run it on the original event
		if ( e.stopPropagation ) {
			e.stopPropagation();
		}

		// Support: IE
		// Set the cancelBubble property of the original event to true
		e.cancelBubble = true;
	},
	stopImmediatePropagation: function() {
		var e = this.originalEvent;

		this.isImmediatePropagationStopped = returnTrue;

		if ( e && e.stopImmediatePropagation ) {
			e.stopImmediatePropagation();
		}

		this.stopPropagation();
	}
};

// Create mouseenter/leave events using mouseover/out and event-time checks
jQuery.each({
	mouseenter: "mouseover",
	mouseleave: "mouseout",
	pointerenter: "pointerover",
	pointerleave: "pointerout"
}, function( orig, fix ) {
	jQuery.event.special[ orig ] = {
		delegateType: fix,
		bindType: fix,

		handle: function( event ) {
			var ret,
				target = this,
				related = event.relatedTarget,
				handleObj = event.handleObj;

			// For mousenter/leave call the handler if related is outside the target.
			// NB: No relatedTarget if the mouse left/entered the browser window
			if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
				event.type = handleObj.origType;
				ret = handleObj.handler.apply( this, arguments );
				event.type = fix;
			}
			return ret;
		}
	};
});

// IE submit delegation
if ( !support.submitBubbles ) {

	jQuery.event.special.submit = {
		setup: function() {
			// Only need this for delegated form submit events
			if ( jQuery.nodeName( this, "form" ) ) {
				return false;
			}

			// Lazy-add a submit handler when a descendant form may potentially be submitted
			jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
				// Node name check avoids a VML-related crash in IE (#9807)
				var elem = e.target,
					form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
				if ( form && !jQuery._data( form, "submitBubbles" ) ) {
					jQuery.event.add( form, "submit._submit", function( event ) {
						event._submit_bubble = true;
					});
					jQuery._data( form, "submitBubbles", true );
				}
			});
			// return undefined since we don't need an event listener
		},

		postDispatch: function( event ) {
			// If form was submitted by the user, bubble the event up the tree
			if ( event._submit_bubble ) {
				delete event._submit_bubble;
				if ( this.parentNode && !event.isTrigger ) {
					jQuery.event.simulate( "submit", this.parentNode, event, true );
				}
			}
		},

		teardown: function() {
			// Only need this for delegated form submit events
			if ( jQuery.nodeName( this, "form" ) ) {
				return false;
			}

			// Remove delegated handlers; cleanData eventually reaps submit handlers attached above
			jQuery.event.remove( this, "._submit" );
		}
	};
}

// IE change delegation and checkbox/radio fix
if ( !support.changeBubbles ) {

	jQuery.event.special.change = {

		setup: function() {

			if ( rformElems.test( this.nodeName ) ) {
				// IE doesn't fire change on a check/radio until blur; trigger it on click
				// after a propertychange. Eat the blur-change in special.change.handle.
				// This still fires onchange a second time for check/radio after blur.
				if ( this.type === "checkbox" || this.type === "radio" ) {
					jQuery.event.add( this, "propertychange._change", function( event ) {
						if ( event.originalEvent.propertyName === "checked" ) {
							this._just_changed = true;
						}
					});
					jQuery.event.add( this, "click._change", function( event ) {
						if ( this._just_changed && !event.isTrigger ) {
							this._just_changed = false;
						}
						// Allow triggered, simulated change events (#11500)
						jQuery.event.simulate( "change", this, event, true );
					});
				}
				return false;
			}
			// Delegated event; lazy-add a change handler on descendant inputs
			jQuery.event.add( this, "beforeactivate._change", function( e ) {
				var elem = e.target;

				if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) {
					jQuery.event.add( elem, "change._change", function( event ) {
						if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
							jQuery.event.simulate( "change", this.parentNode, event, true );
						}
					});
					jQuery._data( elem, "changeBubbles", true );
				}
			});
		},

		handle: function( event ) {
			var elem = event.target;

			// Swallow native change events from checkbox/radio, we already triggered them above
			if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
				return event.handleObj.handler.apply( this, arguments );
			}
		},

		teardown: function() {
			jQuery.event.remove( this, "._change" );

			return !rformElems.test( this.nodeName );
		}
	};
}

// Create "bubbling" focus and blur events
if ( !support.focusinBubbles ) {
	jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {

		// Attach a single capturing handler on the document while someone wants focusin/focusout
		var handler = function( event ) {
				jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
			};

		jQuery.event.special[ fix ] = {
			setup: function() {
				var doc = this.ownerDocument || this,
					attaches = jQuery._data( doc, fix );

				if ( !attaches ) {
					doc.addEventListener( orig, handler, true );
				}
				jQuery._data( doc, fix, ( attaches || 0 ) + 1 );
			},
			teardown: function() {
				var doc = this.ownerDocument || this,
					attaches = jQuery._data( doc, fix ) - 1;

				if ( !attaches ) {
					doc.removeEventListener( orig, handler, true );
					jQuery._removeData( doc, fix );
				} else {
					jQuery._data( doc, fix, attaches );
				}
			}
		};
	});
}

jQuery.fn.extend({

	on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
		var type, origFn;

		// Types can be a map of types/handlers
		if ( typeof types === "object" ) {
			// ( types-Object, selector, data )
			if ( typeof selector !== "string" ) {
				// ( types-Object, data )
				data = data || selector;
				selector = undefined;
			}
			for ( type in types ) {
				this.on( type, selector, data, types[ type ], one );
			}
			return this;
		}

		if ( data == null && fn == null ) {
			// ( types, fn )
			fn = selector;
			data = selector = undefined;
		} else if ( fn == null ) {
			if ( typeof selector === "string" ) {
				// ( types, selector, fn )
				fn = data;
				data = undefined;
			} else {
				// ( types, data, fn )
				fn = data;
				data = selector;
				selector = undefined;
			}
		}
		if ( fn === false ) {
			fn = returnFalse;
		} else if ( !fn ) {
			return this;
		}

		if ( one === 1 ) {
			origFn = fn;
			fn = function( event ) {
				// Can use an empty set, since event contains the info
				jQuery().off( event );
				return origFn.apply( this, arguments );
			};
			// Use same guid so caller can remove using origFn
			fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
		}
		return this.each( function() {
			jQuery.event.add( this, types, fn, data, selector );
		});
	},
	one: function( types, selector, data, fn ) {
		return this.on( types, selector, data, fn, 1 );
	},
	off: function( types, selector, fn ) {
		var handleObj, type;
		if ( types && types.preventDefault && types.handleObj ) {
			// ( event )  dispatched jQuery.Event
			handleObj = types.handleObj;
			jQuery( types.delegateTarget ).off(
				handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
				handleObj.selector,
				handleObj.handler
			);
			return this;
		}
		if ( typeof types === "object" ) {
			// ( types-object [, selector] )
			for ( type in types ) {
				this.off( type, selector, types[ type ] );
			}
			return this;
		}
		if ( selector === false || typeof selector === "function" ) {
			// ( types [, fn] )
			fn = selector;
			selector = undefined;
		}
		if ( fn === false ) {
			fn = returnFalse;
		}
		return this.each(function() {
			jQuery.event.remove( this, types, fn, selector );
		});
	},

	trigger: function( type, data ) {
		return this.each(function() {
			jQuery.event.trigger( type, data, this );
		});
	},
	triggerHandler: function( type, data ) {
		var elem = this[0];
		if ( elem ) {
			return jQuery.event.trigger( type, data, elem, true );
		}
	}
});


function createSafeFragment( document ) {
	var list = nodeNames.split( "|" ),
		safeFrag = document.createDocumentFragment();

	if ( safeFrag.createElement ) {
		while ( list.length ) {
			safeFrag.createElement(
				list.pop()
			);
		}
	}
	return safeFrag;
}

var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
		"header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
	rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
	rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"),
	rleadingWhitespace = /^\s+/,
	rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
	rtagName = /<([\w:]+)/,
	rtbody = /<tbody/i,
	rhtml = /<|&#?\w+;/,
	rnoInnerhtml = /<(?:script|style|link)/i,
	// checked="checked" or checked
	rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
	rscriptType = /^$|\/(?:java|ecma)script/i,
	rscriptTypeMasked = /^true\/(.*)/,
	rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,

	// We have to close these tags to support XHTML (#13200)
	wrapMap = {
		option: [ 1, "<select multiple='multiple'>", "</select>" ],
		legend: [ 1, "<fieldset>", "</fieldset>" ],
		area: [ 1, "<map>", "</map>" ],
		param: [ 1, "<object>", "</object>" ],
		thead: [ 1, "<table>", "</table>" ],
		tr: [ 2, "<table><tbody>", "</tbody></table>" ],
		col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
		td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],

		// IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,
		// unless wrapped in a div with non-breaking characters in front of it.
		_default: support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X<div>", "</div>"  ]
	},
	safeFragment = createSafeFragment( document ),
	fragmentDiv = safeFragment.appendChild( document.createElement("div") );

wrapMap.optgroup = wrapMap.option;
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;

function getAll( context, tag ) {
	var elems, elem,
		i = 0,
		found = typeof context.getElementsByTagName !== strundefined ? context.getElementsByTagName( tag || "*" ) :
			typeof context.querySelectorAll !== strundefined ? context.querySelectorAll( tag || "*" ) :
			undefined;

	if ( !found ) {
		for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) {
			if ( !tag || jQuery.nodeName( elem, tag ) ) {
				found.push( elem );
			} else {
				jQuery.merge( found, getAll( elem, tag ) );
			}
		}
	}

	return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
		jQuery.merge( [ context ], found ) :
		found;
}

// Used in buildFragment, fixes the defaultChecked property
function fixDefaultChecked( elem ) {
	if ( rcheckableType.test( elem.type ) ) {
		elem.defaultChecked = elem.checked;
	}
}

// Support: IE<8
// Manipulating tables requires a tbody
function manipulationTarget( elem, content ) {
	return jQuery.nodeName( elem, "table" ) &&
		jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ?

		elem.getElementsByTagName("tbody")[0] ||
			elem.appendChild( elem.ownerDocument.createElement("tbody") ) :
		elem;
}

// Replace/restore the type attribute of script elements for safe DOM manipulation
function disableScript( elem ) {
	elem.type = (jQuery.find.attr( elem, "type" ) !== null) + "/" + elem.type;
	return elem;
}
function restoreScript( elem ) {
	var match = rscriptTypeMasked.exec( elem.type );
	if ( match ) {
		elem.type = match[1];
	} else {
		elem.removeAttribute("type");
	}
	return elem;
}

// Mark scripts as having already been evaluated
function setGlobalEval( elems, refElements ) {
	var elem,
		i = 0;
	for ( ; (elem = elems[i]) != null; i++ ) {
		jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) );
	}
}

function cloneCopyEvent( src, dest ) {

	if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
		return;
	}

	var type, i, l,
		oldData = jQuery._data( src ),
		curData = jQuery._data( dest, oldData ),
		events = oldData.events;

	if ( events ) {
		delete curData.handle;
		curData.events = {};

		for ( type in events ) {
			for ( i = 0, l = events[ type ].length; i < l; i++ ) {
				jQuery.event.add( dest, type, events[ type ][ i ] );
			}
		}
	}

	// make the cloned public data object a copy from the original
	if ( curData.data ) {
		curData.data = jQuery.extend( {}, curData.data );
	}
}

function fixCloneNodeIssues( src, dest ) {
	var nodeName, e, data;

	// We do not need to do anything for non-Elements
	if ( dest.nodeType !== 1 ) {
		return;
	}

	nodeName = dest.nodeName.toLowerCase();

	// IE6-8 copies events bound via attachEvent when using cloneNode.
	if ( !support.noCloneEvent && dest[ jQuery.expando ] ) {
		data = jQuery._data( dest );

		for ( e in data.events ) {
			jQuery.removeEvent( dest, e, data.handle );
		}

		// Event data gets referenced instead of copied if the expando gets copied too
		dest.removeAttribute( jQuery.expando );
	}

	// IE blanks contents when cloning scripts, and tries to evaluate newly-set text
	if ( nodeName === "script" && dest.text !== src.text ) {
		disableScript( dest ).text = src.text;
		restoreScript( dest );

	// IE6-10 improperly clones children of object elements using classid.
	// IE10 throws NoModificationAllowedError if parent is null, #12132.
	} else if ( nodeName === "object" ) {
		if ( dest.parentNode ) {
			dest.outerHTML = src.outerHTML;
		}

		// This path appears unavoidable for IE9. When cloning an object
		// element in IE9, the outerHTML strategy above is not sufficient.
		// If the src has innerHTML and the destination does not,
		// copy the src.innerHTML into the dest.innerHTML. #10324
		if ( support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) {
			dest.innerHTML = src.innerHTML;
		}

	} else if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
		// IE6-8 fails to persist the checked state of a cloned checkbox
		// or radio button. Worse, IE6-7 fail to give the cloned element
		// a checked appearance if the defaultChecked value isn't also set

		dest.defaultChecked = dest.checked = src.checked;

		// IE6-7 get confused and end up setting the value of a cloned
		// checkbox/radio button to an empty string instead of "on"
		if ( dest.value !== src.value ) {
			dest.value = src.value;
		}

	// IE6-8 fails to return the selected option to the default selected
	// state when cloning options
	} else if ( nodeName === "option" ) {
		dest.defaultSelected = dest.selected = src.defaultSelected;

	// IE6-8 fails to set the defaultValue to the correct value when
	// cloning other types of input fields
	} else if ( nodeName === "input" || nodeName === "textarea" ) {
		dest.defaultValue = src.defaultValue;
	}
}

jQuery.extend({
	clone: function( elem, dataAndEvents, deepDataAndEvents ) {
		var destElements, node, clone, i, srcElements,
			inPage = jQuery.contains( elem.ownerDocument, elem );

		if ( support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) {
			clone = elem.cloneNode( true );

		// IE<=8 does not properly clone detached, unknown element nodes
		} else {
			fragmentDiv.innerHTML = elem.outerHTML;
			fragmentDiv.removeChild( clone = fragmentDiv.firstChild );
		}

		if ( (!support.noCloneEvent || !support.noCloneChecked) &&
				(elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {

			// We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2
			destElements = getAll( clone );
			srcElements = getAll( elem );

			// Fix all IE cloning issues
			for ( i = 0; (node = srcElements[i]) != null; ++i ) {
				// Ensure that the destination node is not null; Fixes #9587
				if ( destElements[i] ) {
					fixCloneNodeIssues( node, destElements[i] );
				}
			}
		}

		// Copy the events from the original to the clone
		if ( dataAndEvents ) {
			if ( deepDataAndEvents ) {
				srcElements = srcElements || getAll( elem );
				destElements = destElements || getAll( clone );

				for ( i = 0; (node = srcElements[i]) != null; i++ ) {
					cloneCopyEvent( node, destElements[i] );
				}
			} else {
				cloneCopyEvent( elem, clone );
			}
		}

		// Preserve script evaluation history
		destElements = getAll( clone, "script" );
		if ( destElements.length > 0 ) {
			setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
		}

		destElements = srcElements = node = null;

		// Return the cloned set
		return clone;
	},

	buildFragment: function( elems, context, scripts, selection ) {
		var j, elem, contains,
			tmp, tag, tbody, wrap,
			l = elems.length,

			// Ensure a safe fragment
			safe = createSafeFragment( context ),

			nodes = [],
			i = 0;

		for ( ; i < l; i++ ) {
			elem = elems[ i ];

			if ( elem || elem === 0 ) {

				// Add nodes directly
				if ( jQuery.type( elem ) === "object" ) {
					jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );

				// Convert non-html into a text node
				} else if ( !rhtml.test( elem ) ) {
					nodes.push( context.createTextNode( elem ) );

				// Convert html into DOM nodes
				} else {
					tmp = tmp || safe.appendChild( context.createElement("div") );

					// Deserialize a standard representation
					tag = (rtagName.exec( elem ) || [ "", "" ])[ 1 ].toLowerCase();
					wrap = wrapMap[ tag ] || wrapMap._default;

					tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[2];

					// Descend through wrappers to the right content
					j = wrap[0];
					while ( j-- ) {
						tmp = tmp.lastChild;
					}

					// Manually add leading whitespace removed by IE
					if ( !support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
						nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) );
					}

					// Remove IE's autoinserted <tbody> from table fragments
					if ( !support.tbody ) {

						// String was a <table>, *may* have spurious <tbody>
						elem = tag === "table" && !rtbody.test( elem ) ?
							tmp.firstChild :

							// String was a bare <thead> or <tfoot>
							wrap[1] === "<table>" && !rtbody.test( elem ) ?
								tmp :
								0;

						j = elem && elem.childNodes.length;
						while ( j-- ) {
							if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) {
								elem.removeChild( tbody );
							}
						}
					}

					jQuery.merge( nodes, tmp.childNodes );

					// Fix #12392 for WebKit and IE > 9
					tmp.textContent = "";

					// Fix #12392 for oldIE
					while ( tmp.firstChild ) {
						tmp.removeChild( tmp.firstChild );
					}

					// Remember the top-level container for proper cleanup
					tmp = safe.lastChild;
				}
			}
		}

		// Fix #11356: Clear elements from fragment
		if ( tmp ) {
			safe.removeChild( tmp );
		}

		// Reset defaultChecked for any radios and checkboxes
		// about to be appended to the DOM in IE 6/7 (#8060)
		if ( !support.appendChecked ) {
			jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked );
		}

		i = 0;
		while ( (elem = nodes[ i++ ]) ) {

			// #4087 - If origin and destination elements are the same, and this is
			// that element, do not do anything
			if ( selection && jQuery.inArray( elem, selection ) !== -1 ) {
				continue;
			}

			contains = jQuery.contains( elem.ownerDocument, elem );

			// Append to fragment
			tmp = getAll( safe.appendChild( elem ), "script" );

			// Preserve script evaluation history
			if ( contains ) {
				setGlobalEval( tmp );
			}

			// Capture executables
			if ( scripts ) {
				j = 0;
				while ( (elem = tmp[ j++ ]) ) {
					if ( rscriptType.test( elem.type || "" ) ) {
						scripts.push( elem );
					}
				}
			}
		}

		tmp = null;

		return safe;
	},

	cleanData: function( elems, /* internal */ acceptData ) {
		var elem, type, id, data,
			i = 0,
			internalKey = jQuery.expando,
			cache = jQuery.cache,
			deleteExpando = support.deleteExpando,
			special = jQuery.event.special;

		for ( ; (elem = elems[i]) != null; i++ ) {
			if ( acceptData || jQuery.acceptData( elem ) ) {

				id = elem[ internalKey ];
				data = id && cache[ id ];

				if ( data ) {
					if ( data.events ) {
						for ( type in data.events ) {
							if ( special[ type ] ) {
								jQuery.event.remove( elem, type );

							// This is a shortcut to avoid jQuery.event.remove's overhead
							} else {
								jQuery.removeEvent( elem, type, data.handle );
							}
						}
					}

					// Remove cache only if it was not already removed by jQuery.event.remove
					if ( cache[ id ] ) {

						delete cache[ id ];

						// IE does not allow us to delete expando properties from nodes,
						// nor does it have a removeAttribute function on Document nodes;
						// we must handle all of these cases
						if ( deleteExpando ) {
							delete elem[ internalKey ];

						} else if ( typeof elem.removeAttribute !== strundefined ) {
							elem.removeAttribute( internalKey );

						} else {
							elem[ internalKey ] = null;
						}

						deletedIds.push( id );
					}
				}
			}
		}
	}
});

jQuery.fn.extend({
	text: function( value ) {
		return access( this, function( value ) {
			return value === undefined ?
				jQuery.text( this ) :
				this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
		}, null, value, arguments.length );
	},

	append: function() {
		return this.domManip( arguments, function( elem ) {
			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
				var target = manipulationTarget( this, elem );
				target.appendChild( elem );
			}
		});
	},

	prepend: function() {
		return this.domManip( arguments, function( elem ) {
			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
				var target = manipulationTarget( this, elem );
				target.insertBefore( elem, target.firstChild );
			}
		});
	},

	before: function() {
		return this.domManip( arguments, function( elem ) {
			if ( this.parentNode ) {
				this.parentNode.insertBefore( elem, this );
			}
		});
	},

	after: function() {
		return this.domManip( arguments, function( elem ) {
			if ( this.parentNode ) {
				this.parentNode.insertBefore( elem, this.nextSibling );
			}
		});
	},

	remove: function( selector, keepData /* Internal Use Only */ ) {
		var elem,
			elems = selector ? jQuery.filter( selector, this ) : this,
			i = 0;

		for ( ; (elem = elems[i]) != null; i++ ) {

			if ( !keepData && elem.nodeType === 1 ) {
				jQuery.cleanData( getAll( elem ) );
			}

			if ( elem.parentNode ) {
				if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) {
					setGlobalEval( getAll( elem, "script" ) );
				}
				elem.parentNode.removeChild( elem );
			}
		}

		return this;
	},

	empty: function() {
		var elem,
			i = 0;

		for ( ; (elem = this[i]) != null; i++ ) {
			// Remove element nodes and prevent memory leaks
			if ( elem.nodeType === 1 ) {
				jQuery.cleanData( getAll( elem, false ) );
			}

			// Remove any remaining nodes
			while ( elem.firstChild ) {
				elem.removeChild( elem.firstChild );
			}

			// If this is a select, ensure that it displays empty (#12336)
			// Support: IE<9
			if ( elem.options && jQuery.nodeName( elem, "select" ) ) {
				elem.options.length = 0;
			}
		}

		return this;
	},

	clone: function( dataAndEvents, deepDataAndEvents ) {
		dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
		deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;

		return this.map(function() {
			return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
		});
	},

	html: function( value ) {
		return access( this, function( value ) {
			var elem = this[ 0 ] || {},
				i = 0,
				l = this.length;

			if ( value === undefined ) {
				return elem.nodeType === 1 ?
					elem.innerHTML.replace( rinlinejQuery, "" ) :
					undefined;
			}

			// See if we can take a shortcut and just use innerHTML
			if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
				( support.htmlSerialize || !rnoshimcache.test( value )  ) &&
				( support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
				!wrapMap[ (rtagName.exec( value ) || [ "", "" ])[ 1 ].toLowerCase() ] ) {

				value = value.replace( rxhtmlTag, "<$1></$2>" );

				try {
					for (; i < l; i++ ) {
						// Remove element nodes and prevent memory leaks
						elem = this[i] || {};
						if ( elem.nodeType === 1 ) {
							jQuery.cleanData( getAll( elem, false ) );
							elem.innerHTML = value;
						}
					}

					elem = 0;

				// If using innerHTML throws an exception, use the fallback method
				} catch(e) {}
			}

			if ( elem ) {
				this.empty().append( value );
			}
		}, null, value, arguments.length );
	},

	replaceWith: function() {
		var arg = arguments[ 0 ];

		// Make the changes, replacing each context element with the new content
		this.domManip( arguments, function( elem ) {
			arg = this.parentNode;

			jQuery.cleanData( getAll( this ) );

			if ( arg ) {
				arg.replaceChild( elem, this );
			}
		});

		// Force removal if there was no new content (e.g., from empty arguments)
		return arg && (arg.length || arg.nodeType) ? this : this.remove();
	},

	detach: function( selector ) {
		return this.remove( selector, true );
	},

	domManip: function( args, callback ) {

		// Flatten any nested arrays
		args = concat.apply( [], args );

		var first, node, hasScripts,
			scripts, doc, fragment,
			i = 0,
			l = this.length,
			set = this,
			iNoClone = l - 1,
			value = args[0],
			isFunction = jQuery.isFunction( value );

		// We can't cloneNode fragments that contain checked, in WebKit
		if ( isFunction ||
				( l > 1 && typeof value === "string" &&
					!support.checkClone && rchecked.test( value ) ) ) {
			return this.each(function( index ) {
				var self = set.eq( index );
				if ( isFunction ) {
					args[0] = value.call( this, index, self.html() );
				}
				self.domManip( args, callback );
			});
		}

		if ( l ) {
			fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this );
			first = fragment.firstChild;

			if ( fragment.childNodes.length === 1 ) {
				fragment = first;
			}

			if ( first ) {
				scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
				hasScripts = scripts.length;

				// Use the original fragment for the last item instead of the first because it can end up
				// being emptied incorrectly in certain situations (#8070).
				for ( ; i < l; i++ ) {
					node = fragment;

					if ( i !== iNoClone ) {
						node = jQuery.clone( node, true, true );

						// Keep references to cloned scripts for later restoration
						if ( hasScripts ) {
							jQuery.merge( scripts, getAll( node, "script" ) );
						}
					}

					callback.call( this[i], node, i );
				}

				if ( hasScripts ) {
					doc = scripts[ scripts.length - 1 ].ownerDocument;

					// Reenable scripts
					jQuery.map( scripts, restoreScript );

					// Evaluate executable scripts on first document insertion
					for ( i = 0; i < hasScripts; i++ ) {
						node = scripts[ i ];
						if ( rscriptType.test( node.type || "" ) &&
							!jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) {

							if ( node.src ) {
								// Optional AJAX dependency, but won't run scripts if not present
								if ( jQuery._evalUrl ) {
									jQuery._evalUrl( node.src );
								}
							} else {
								jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) );
							}
						}
					}
				}

				// Fix #11809: Avoid leaking memory
				fragment = first = null;
			}
		}

		return this;
	}
});

jQuery.each({
	appendTo: "append",
	prependTo: "prepend",
	insertBefore: "before",
	insertAfter: "after",
	replaceAll: "replaceWith"
}, function( name, original ) {
	jQuery.fn[ name ] = function( selector ) {
		var elems,
			i = 0,
			ret = [],
			insert = jQuery( selector ),
			last = insert.length - 1;

		for ( ; i <= last; i++ ) {
			elems = i === last ? this : this.clone(true);
			jQuery( insert[i] )[ original ]( elems );

			// Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get()
			push.apply( ret, elems.get() );
		}

		return this.pushStack( ret );
	};
});


var iframe,
	elemdisplay = {};

/**
 * Retrieve the actual display of a element
 * @param {String} name nodeName of the element
 * @param {Object} doc Document object
 */
// Called only from within defaultDisplay
function actualDisplay( name, doc ) {
	var style,
		elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),

		// getDefaultComputedStyle might be reliably used only on attached element
		display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ?

			// Use of this method is a temporary fix (more like optmization) until something better comes along,
			// since it was removed from specification and supported only in FF
			style.display : jQuery.css( elem[ 0 ], "display" );

	// We don't have any data stored on the element,
	// so use "detach" method as fast way to get rid of the element
	elem.detach();

	return display;
}

/**
 * Try to determine the default display value of an element
 * @param {String} nodeName
 */
function defaultDisplay( nodeName ) {
	var doc = document,
		display = elemdisplay[ nodeName ];

	if ( !display ) {
		display = actualDisplay( nodeName, doc );

		// If the simple way fails, read from inside an iframe
		if ( display === "none" || !display ) {

			// Use the already-created iframe if possible
			iframe = (iframe || jQuery( "<iframe frameborder='0' width='0' height='0'/>" )).appendTo( doc.documentElement );

			// Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
			doc = ( iframe[ 0 ].contentWindow || iframe[ 0 ].contentDocument ).document;

			// Support: IE
			doc.write();
			doc.close();

			display = actualDisplay( nodeName, doc );
			iframe.detach();
		}

		// Store the correct default display
		elemdisplay[ nodeName ] = display;
	}

	return display;
}


(function() {
	var shrinkWrapBlocksVal;

	support.shrinkWrapBlocks = function() {
		if ( shrinkWrapBlocksVal != null ) {
			return shrinkWrapBlocksVal;
		}

		// Will be changed later if needed.
		shrinkWrapBlocksVal = false;

		// Minified: var b,c,d
		var div, body, container;

		body = document.getElementsByTagName( "body" )[ 0 ];
		if ( !body || !body.style ) {
			// Test fired too early or in an unsupported environment, exit.
			return;
		}

		// Setup
		div = document.createElement( "div" );
		container = document.createElement( "div" );
		container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px";
		body.appendChild( container ).appendChild( div );

		// Support: IE6
		// Check if elements with layout shrink-wrap their children
		if ( typeof div.style.zoom !== strundefined ) {
			// Reset CSS: box-sizing; display; margin; border
			div.style.cssText =
				// Support: Firefox<29, Android 2.3
				// Vendor-prefix box-sizing
				"-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" +
				"box-sizing:content-box;display:block;margin:0;border:0;" +
				"padding:1px;width:1px;zoom:1";
			div.appendChild( document.createElement( "div" ) ).style.width = "5px";
			shrinkWrapBlocksVal = div.offsetWidth !== 3;
		}

		body.removeChild( container );

		return shrinkWrapBlocksVal;
	};

})();
var rmargin = (/^margin/);

var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );



var getStyles, curCSS,
	rposition = /^(top|right|bottom|left)$/;

if ( window.getComputedStyle ) {
	getStyles = function( elem ) {
		return elem.ownerDocument.defaultView.getComputedStyle( elem, null );
	};

	curCSS = function( elem, name, computed ) {
		var width, minWidth, maxWidth, ret,
			style = elem.style;

		computed = computed || getStyles( elem );

		// getPropertyValue is only needed for .css('filter') in IE9, see #12537
		ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined;

		if ( computed ) {

			if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
				ret = jQuery.style( elem, name );
			}

			// A tribute to the "awesome hack by Dean Edwards"
			// Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right
			// Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
			// this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
			if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {

				// Remember the original values
				width = style.width;
				minWidth = style.minWidth;
				maxWidth = style.maxWidth;

				// Put in the new values to get a computed value out
				style.minWidth = style.maxWidth = style.width = ret;
				ret = computed.width;

				// Revert the changed values
				style.width = width;
				style.minWidth = minWidth;
				style.maxWidth = maxWidth;
			}
		}

		// Support: IE
		// IE returns zIndex value as an integer.
		return ret === undefined ?
			ret :
			ret + "";
	};
} else if ( document.documentElement.currentStyle ) {
	getStyles = function( elem ) {
		return elem.currentStyle;
	};

	curCSS = function( elem, name, computed ) {
		var left, rs, rsLeft, ret,
			style = elem.style;

		computed = computed || getStyles( elem );
		ret = computed ? computed[ name ] : undefined;

		// Avoid setting ret to empty string here
		// so we don't default to auto
		if ( ret == null && style && style[ name ] ) {
			ret = style[ name ];
		}

		// From the awesome hack by Dean Edwards
		// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291

		// If we're not dealing with a regular pixel number
		// but a number that has a weird ending, we need to convert it to pixels
		// but not position css attributes, as those are proportional to the parent element instead
		// and we can't measure the parent instead because it might trigger a "stacking dolls" problem
		if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) {

			// Remember the original values
			left = style.left;
			rs = elem.runtimeStyle;
			rsLeft = rs && rs.left;

			// Put in the new values to get a computed value out
			if ( rsLeft ) {
				rs.left = elem.currentStyle.left;
			}
			style.left = name === "fontSize" ? "1em" : ret;
			ret = style.pixelLeft + "px";

			// Revert the changed values
			style.left = left;
			if ( rsLeft ) {
				rs.left = rsLeft;
			}
		}

		// Support: IE
		// IE returns zIndex value as an integer.
		return ret === undefined ?
			ret :
			ret + "" || "auto";
	};
}




function addGetHookIf( conditionFn, hookFn ) {
	// Define the hook, we'll check on the first run if it's really needed.
	return {
		get: function() {
			var condition = conditionFn();

			if ( condition == null ) {
				// The test was not ready at this point; screw the hook this time
				// but check again when needed next time.
				return;
			}

			if ( condition ) {
				// Hook not needed (or it's not possible to use it due to missing dependency),
				// remove it.
				// Since there are no other hooks for marginRight, remove the whole object.
				delete this.get;
				return;
			}

			// Hook needed; redefine it so that the support test is not executed again.

			return (this.get = hookFn).apply( this, arguments );
		}
	};
}


(function() {
	// Minified: var b,c,d,e,f,g, h,i
	var div, style, a, pixelPositionVal, boxSizingReliableVal,
		reliableHiddenOffsetsVal, reliableMarginRightVal;

	// Setup
	div = document.createElement( "div" );
	div.innerHTML = "  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
	a = div.getElementsByTagName( "a" )[ 0 ];
	style = a && a.style;

	// Finish early in limited (non-browser) environments
	if ( !style ) {
		return;
	}

	style.cssText = "float:left;opacity:.5";

	// Support: IE<9
	// Make sure that element opacity exists (as opposed to filter)
	support.opacity = style.opacity === "0.5";

	// Verify style float existence
	// (IE uses styleFloat instead of cssFloat)
	support.cssFloat = !!style.cssFloat;

	div.style.backgroundClip = "content-box";
	div.cloneNode( true ).style.backgroundClip = "";
	support.clearCloneStyle = div.style.backgroundClip === "content-box";

	// Support: Firefox<29, Android 2.3
	// Vendor-prefix box-sizing
	support.boxSizing = style.boxSizing === "" || style.MozBoxSizing === "" ||
		style.WebkitBoxSizing === "";

	jQuery.extend(support, {
		reliableHiddenOffsets: function() {
			if ( reliableHiddenOffsetsVal == null ) {
				computeStyleTests();
			}
			return reliableHiddenOffsetsVal;
		},

		boxSizingReliable: function() {
			if ( boxSizingReliableVal == null ) {
				computeStyleTests();
			}
			return boxSizingReliableVal;
		},

		pixelPosition: function() {
			if ( pixelPositionVal == null ) {
				computeStyleTests();
			}
			return pixelPositionVal;
		},

		// Support: Android 2.3
		reliableMarginRight: function() {
			if ( reliableMarginRightVal == null ) {
				computeStyleTests();
			}
			return reliableMarginRightVal;
		}
	});

	function computeStyleTests() {
		// Minified: var b,c,d,j
		var div, body, container, contents;

		body = document.getElementsByTagName( "body" )[ 0 ];
		if ( !body || !body.style ) {
			// Test fired too early or in an unsupported environment, exit.
			return;
		}

		// Setup
		div = document.createElement( "div" );
		container = document.createElement( "div" );
		container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px";
		body.appendChild( container ).appendChild( div );

		div.style.cssText =
			// Support: Firefox<29, Android 2.3
			// Vendor-prefix box-sizing
			"-webkit-box-sizing:border-box;-moz-box-sizing:border-box;" +
			"box-sizing:border-box;display:block;margin-top:1%;top:1%;" +
			"border:1px;padding:1px;width:4px;position:absolute";

		// Support: IE<9
		// Assume reasonable values in the absence of getComputedStyle
		pixelPositionVal = boxSizingReliableVal = false;
		reliableMarginRightVal = true;

		// Check for getComputedStyle so that this code is not run in IE<9.
		if ( window.getComputedStyle ) {
			pixelPositionVal = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
			boxSizingReliableVal =
				( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";

			// Support: Android 2.3
			// Div with explicit width and no margin-right incorrectly
			// gets computed margin-right based on width of container (#3333)
			// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
			contents = div.appendChild( document.createElement( "div" ) );

			// Reset CSS: box-sizing; display; margin; border; padding
			contents.style.cssText = div.style.cssText =
				// Support: Firefox<29, Android 2.3
				// Vendor-prefix box-sizing
				"-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" +
				"box-sizing:content-box;display:block;margin:0;border:0;padding:0";
			contents.style.marginRight = contents.style.width = "0";
			div.style.width = "1px";

			reliableMarginRightVal =
				!parseFloat( ( window.getComputedStyle( contents, null ) || {} ).marginRight );
		}

		// Support: IE8
		// Check if table cells still have offsetWidth/Height when they are set
		// to display:none and there are still other visible table cells in a
		// table row; if so, offsetWidth/Height are not reliable for use when
		// determining if an element has been hidden directly using
		// display:none (it is still safe to use offsets if a parent element is
		// hidden; don safety goggles and see bug #4512 for more information).
		div.innerHTML = "<table><tr><td></td><td>t</td></tr></table>";
		contents = div.getElementsByTagName( "td" );
		contents[ 0 ].style.cssText = "margin:0;border:0;padding:0;display:none";
		reliableHiddenOffsetsVal = contents[ 0 ].offsetHeight === 0;
		if ( reliableHiddenOffsetsVal ) {
			contents[ 0 ].style.display = "";
			contents[ 1 ].style.display = "none";
			reliableHiddenOffsetsVal = contents[ 0 ].offsetHeight === 0;
		}

		body.removeChild( container );
	}

})();


// A method for quickly swapping in/out CSS properties to get correct calculations.
jQuery.swap = function( elem, options, callback, args ) {
	var ret, name,
		old = {};

	// Remember the old values, and insert the new ones
	for ( name in options ) {
		old[ name ] = elem.style[ name ];
		elem.style[ name ] = options[ name ];
	}

	ret = callback.apply( elem, args || [] );

	// Revert the old values
	for ( name in options ) {
		elem.style[ name ] = old[ name ];
	}

	return ret;
};


var
		ralpha = /alpha\([^)]*\)/i,
	ropacity = /opacity\s*=\s*([^)]*)/,

	// swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
	// see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
	rdisplayswap = /^(none|table(?!-c[ea]).+)/,
	rnumsplit = new RegExp( "^(" + pnum + ")(.*)$", "i" ),
	rrelNum = new RegExp( "^([+-])=(" + pnum + ")", "i" ),

	cssShow = { position: "absolute", visibility: "hidden", display: "block" },
	cssNormalTransform = {
		letterSpacing: "0",
		fontWeight: "400"
	},

	cssPrefixes = [ "Webkit", "O", "Moz", "ms" ];


// return a css property mapped to a potentially vendor prefixed property
function vendorPropName( style, name ) {

	// shortcut for names that are not vendor prefixed
	if ( name in style ) {
		return name;
	}

	// check for vendor prefixed names
	var capName = name.charAt(0).toUpperCase() + name.slice(1),
		origName = name,
		i = cssPrefixes.length;

	while ( i-- ) {
		name = cssPrefixes[ i ] + capName;
		if ( name in style ) {
			return name;
		}
	}

	return origName;
}

function showHide( elements, show ) {
	var display, elem, hidden,
		values = [],
		index = 0,
		length = elements.length;

	for ( ; index < length; index++ ) {
		elem = elements[ index ];
		if ( !elem.style ) {
			continue;
		}

		values[ index ] = jQuery._data( elem, "olddisplay" );
		display = elem.style.display;
		if ( show ) {
			// Reset the inline display of this element to learn if it is
			// being hidden by cascaded rules or not
			if ( !values[ index ] && display === "none" ) {
				elem.style.display = "";
			}

			// Set elements which have been overridden with display: none
			// in a stylesheet to whatever the default browser style is
			// for such an element
			if ( elem.style.display === "" && isHidden( elem ) ) {
				values[ index ] = jQuery._data( elem, "olddisplay", defaultDisplay(elem.nodeName) );
			}
		} else {
			hidden = isHidden( elem );

			if ( display && display !== "none" || !hidden ) {
				jQuery._data( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) );
			}
		}
	}

	// Set the display of most of the elements in a second loop
	// to avoid the constant reflow
	for ( index = 0; index < length; index++ ) {
		elem = elements[ index ];
		if ( !elem.style ) {
			continue;
		}
		if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
			elem.style.display = show ? values[ index ] || "" : "none";
		}
	}

	return elements;
}

function setPositiveNumber( elem, value, subtract ) {
	var matches = rnumsplit.exec( value );
	return matches ?
		// Guard against undefined "subtract", e.g., when used as in cssHooks
		Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
		value;
}

function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
	var i = extra === ( isBorderBox ? "border" : "content" ) ?
		// If we already have the right measurement, avoid augmentation
		4 :
		// Otherwise initialize for horizontal or vertical properties
		name === "width" ? 1 : 0,

		val = 0;

	for ( ; i < 4; i += 2 ) {
		// both box models exclude margin, so add it if we want it
		if ( extra === "margin" ) {
			val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
		}

		if ( isBorderBox ) {
			// border-box includes padding, so remove it if we want content
			if ( extra === "content" ) {
				val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
			}

			// at this point, extra isn't border nor margin, so remove border
			if ( extra !== "margin" ) {
				val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
			}
		} else {
			// at this point, extra isn't content, so add padding
			val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );

			// at this point, extra isn't content nor padding, so add border
			if ( extra !== "padding" ) {
				val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
			}
		}
	}

	return val;
}

function getWidthOrHeight( elem, name, extra ) {

	// Start with offset property, which is equivalent to the border-box value
	var valueIsBorderBox = true,
		val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
		styles = getStyles( elem ),
		isBorderBox = support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box";

	// some non-html elements return undefined for offsetWidth, so check for null/undefined
	// svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
	// MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
	if ( val <= 0 || val == null ) {
		// Fall back to computed then uncomputed css if necessary
		val = curCSS( elem, name, styles );
		if ( val < 0 || val == null ) {
			val = elem.style[ name ];
		}

		// Computed unit is not pixels. Stop here and return.
		if ( rnumnonpx.test(val) ) {
			return val;
		}

		// we need the check for style in case a browser which returns unreliable values
		// for getComputedStyle silently falls back to the reliable elem.style
		valueIsBorderBox = isBorderBox && ( support.boxSizingReliable() || val === elem.style[ name ] );

		// Normalize "", auto, and prepare for extra
		val = parseFloat( val ) || 0;
	}

	// use the active box-sizing model to add/subtract irrelevant styles
	return ( val +
		augmentWidthOrHeight(
			elem,
			name,
			extra || ( isBorderBox ? "border" : "content" ),
			valueIsBorderBox,
			styles
		)
	) + "px";
}

jQuery.extend({
	// Add in style property hooks for overriding the default
	// behavior of getting and setting a style property
	cssHooks: {
		opacity: {
			get: function( elem, computed ) {
				if ( computed ) {
					// We should always get a number back from opacity
					var ret = curCSS( elem, "opacity" );
					return ret === "" ? "1" : ret;
				}
			}
		}
	},

	// Don't automatically add "px" to these possibly-unitless properties
	cssNumber: {
		"columnCount": true,
		"fillOpacity": true,
		"flexGrow": true,
		"flexShrink": true,
		"fontWeight": true,
		"lineHeight": true,
		"opacity": true,
		"order": true,
		"orphans": true,
		"widows": true,
		"zIndex": true,
		"zoom": true
	},

	// Add in properties whose names you wish to fix before
	// setting or getting the value
	cssProps: {
		// normalize float css property
		"float": support.cssFloat ? "cssFloat" : "styleFloat"
	},

	// Get and set the style property on a DOM Node
	style: function( elem, name, value, extra ) {
		// Don't set styles on text and comment nodes
		if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
			return;
		}

		// Make sure that we're working with the right name
		var ret, type, hooks,
			origName = jQuery.camelCase( name ),
			style = elem.style;

		name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );

		// gets hook for the prefixed version
		// followed by the unprefixed version
		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];

		// Check if we're setting a value
		if ( value !== undefined ) {
			type = typeof value;

			// convert relative number strings (+= or -=) to relative numbers. #7345
			if ( type === "string" && (ret = rrelNum.exec( value )) ) {
				value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
				// Fixes bug #9237
				type = "number";
			}

			// Make sure that null and NaN values aren't set. See: #7116
			if ( value == null || value !== value ) {
				return;
			}

			// If a number was passed in, add 'px' to the (except for certain CSS properties)
			if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
				value += "px";
			}

			// Fixes #8908, it can be done more correctly by specifing setters in cssHooks,
			// but it would mean to define eight (for every problematic property) identical functions
			if ( !support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) {
				style[ name ] = "inherit";
			}

			// If a hook was provided, use that value, otherwise just set the specified value
			if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {

				// Support: IE
				// Swallow errors from 'invalid' CSS values (#5509)
				try {
					style[ name ] = value;
				} catch(e) {}
			}

		} else {
			// If a hook was provided get the non-computed value from there
			if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
				return ret;
			}

			// Otherwise just get the value from the style object
			return style[ name ];
		}
	},

	css: function( elem, name, extra, styles ) {
		var num, val, hooks,
			origName = jQuery.camelCase( name );

		// Make sure that we're working with the right name
		name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );

		// gets hook for the prefixed version
		// followed by the unprefixed version
		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];

		// If a hook was provided get the computed value from there
		if ( hooks && "get" in hooks ) {
			val = hooks.get( elem, true, extra );
		}

		// Otherwise, if a way to get the computed value exists, use that
		if ( val === undefined ) {
			val = curCSS( elem, name, styles );
		}

		//convert "normal" to computed value
		if ( val === "normal" && name in cssNormalTransform ) {
			val = cssNormalTransform[ name ];
		}

		// Return, converting to number if forced or a qualifier was provided and val looks numeric
		if ( extra === "" || extra ) {
			num = parseFloat( val );
			return extra === true || jQuery.isNumeric( num ) ? num || 0 : val;
		}
		return val;
	}
});

jQuery.each([ "height", "width" ], function( i, name ) {
	jQuery.cssHooks[ name ] = {
		get: function( elem, computed, extra ) {
			if ( computed ) {
				// certain elements can have dimension info if we invisibly show them
				// however, it must have a current display style that would benefit from this
				return rdisplayswap.test( jQuery.css( elem, "display" ) ) && elem.offsetWidth === 0 ?
					jQuery.swap( elem, cssShow, function() {
						return getWidthOrHeight( elem, name, extra );
					}) :
					getWidthOrHeight( elem, name, extra );
			}
		},

		set: function( elem, value, extra ) {
			var styles = extra && getStyles( elem );
			return setPositiveNumber( elem, value, extra ?
				augmentWidthOrHeight(
					elem,
					name,
					extra,
					support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
					styles
				) : 0
			);
		}
	};
});

if ( !support.opacity ) {
	jQuery.cssHooks.opacity = {
		get: function( elem, computed ) {
			// IE uses filters for opacity
			return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
				( 0.01 * parseFloat( RegExp.$1 ) ) + "" :
				computed ? "1" : "";
		},

		set: function( elem, value ) {
			var style = elem.style,
				currentStyle = elem.currentStyle,
				opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
				filter = currentStyle && currentStyle.filter || style.filter || "";

			// IE has trouble with opacity if it does not have layout
			// Force it by setting the zoom level
			style.zoom = 1;

			// if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
			// if value === "", then remove inline opacity #12685
			if ( ( value >= 1 || value === "" ) &&
					jQuery.trim( filter.replace( ralpha, "" ) ) === "" &&
					style.removeAttribute ) {

				// Setting style.filter to null, "" & " " still leave "filter:" in the cssText
				// if "filter:" is present at all, clearType is disabled, we want to avoid this
				// style.removeAttribute is IE Only, but so apparently is this code path...
				style.removeAttribute( "filter" );

				// if there is no filter style applied in a css rule or unset inline opacity, we are done
				if ( value === "" || currentStyle && !currentStyle.filter ) {
					return;
				}
			}

			// otherwise, set new filter values
			style.filter = ralpha.test( filter ) ?
				filter.replace( ralpha, opacity ) :
				filter + " " + opacity;
		}
	};
}

jQuery.cssHooks.marginRight = addGetHookIf( support.reliableMarginRight,
	function( elem, computed ) {
		if ( computed ) {
			// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
			// Work around by temporarily setting element display to inline-block
			return jQuery.swap( elem, { "display": "inline-block" },
				curCSS, [ elem, "marginRight" ] );
		}
	}
);

// These hooks are used by animate to expand properties
jQuery.each({
	margin: "",
	padding: "",
	border: "Width"
}, function( prefix, suffix ) {
	jQuery.cssHooks[ prefix + suffix ] = {
		expand: function( value ) {
			var i = 0,
				expanded = {},

				// assumes a single number if not a string
				parts = typeof value === "string" ? value.split(" ") : [ value ];

			for ( ; i < 4; i++ ) {
				expanded[ prefix + cssExpand[ i ] + suffix ] =
					parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
			}

			return expanded;
		}
	};

	if ( !rmargin.test( prefix ) ) {
		jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
	}
});

jQuery.fn.extend({
	css: function( name, value ) {
		return access( this, function( elem, name, value ) {
			var styles, len,
				map = {},
				i = 0;

			if ( jQuery.isArray( name ) ) {
				styles = getStyles( elem );
				len = name.length;

				for ( ; i < len; i++ ) {
					map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
				}

				return map;
			}

			return value !== undefined ?
				jQuery.style( elem, name, value ) :
				jQuery.css( elem, name );
		}, name, value, arguments.length > 1 );
	},
	show: function() {
		return showHide( this, true );
	},
	hide: function() {
		return showHide( this );
	},
	toggle: function( state ) {
		if ( typeof state === "boolean" ) {
			return state ? this.show() : this.hide();
		}

		return this.each(function() {
			if ( isHidden( this ) ) {
				jQuery( this ).show();
			} else {
				jQuery( this ).hide();
			}
		});
	}
});


function Tween( elem, options, prop, end, easing ) {
	return new Tween.prototype.init( elem, options, prop, end, easing );
}
jQuery.Tween = Tween;

Tween.prototype = {
	constructor: Tween,
	init: function( elem, options, prop, end, easing, unit ) {
		this.elem = elem;
		this.prop = prop;
		this.easing = easing || "swing";
		this.options = options;
		this.start = this.now = this.cur();
		this.end = end;
		this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
	},
	cur: function() {
		var hooks = Tween.propHooks[ this.prop ];

		return hooks && hooks.get ?
			hooks.get( this ) :
			Tween.propHooks._default.get( this );
	},
	run: function( percent ) {
		var eased,
			hooks = Tween.propHooks[ this.prop ];

		if ( this.options.duration ) {
			this.pos = eased = jQuery.easing[ this.easing ](
				percent, this.options.duration * percent, 0, 1, this.options.duration
			);
		} else {
			this.pos = eased = percent;
		}
		this.now = ( this.end - this.start ) * eased + this.start;

		if ( this.options.step ) {
			this.options.step.call( this.elem, this.now, this );
		}

		if ( hooks && hooks.set ) {
			hooks.set( this );
		} else {
			Tween.propHooks._default.set( this );
		}
		return this;
	}
};

Tween.prototype.init.prototype = Tween.prototype;

Tween.propHooks = {
	_default: {
		get: function( tween ) {
			var result;

			if ( tween.elem[ tween.prop ] != null &&
				(!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
				return tween.elem[ tween.prop ];
			}

			// passing an empty string as a 3rd parameter to .css will automatically
			// attempt a parseFloat and fallback to a string if the parse fails
			// so, simple values such as "10px" are parsed to Float.
			// complex values such as "rotate(1rad)" are returned as is.
			result = jQuery.css( tween.elem, tween.prop, "" );
			// Empty strings, null, undefined and "auto" are converted to 0.
			return !result || result === "auto" ? 0 : result;
		},
		set: function( tween ) {
			// use step hook for back compat - use cssHook if its there - use .style if its
			// available and use plain properties where available
			if ( jQuery.fx.step[ tween.prop ] ) {
				jQuery.fx.step[ tween.prop ]( tween );
			} else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
				jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
			} else {
				tween.elem[ tween.prop ] = tween.now;
			}
		}
	}
};

// Support: IE <=9
// Panic based approach to setting things on disconnected nodes

Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
	set: function( tween ) {
		if ( tween.elem.nodeType && tween.elem.parentNode ) {
			tween.elem[ tween.prop ] = tween.now;
		}
	}
};

jQuery.easing = {
	linear: function( p ) {
		return p;
	},
	swing: function( p ) {
		return 0.5 - Math.cos( p * Math.PI ) / 2;
	}
};

jQuery.fx = Tween.prototype.init;

// Back Compat <1.8 extension point
jQuery.fx.step = {};




var
	fxNow, timerId,
	rfxtypes = /^(?:toggle|show|hide)$/,
	rfxnum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ),
	rrun = /queueHooks$/,
	animationPrefilters = [ defaultPrefilter ],
	tweeners = {
		"*": [ function( prop, value ) {
			var tween = this.createTween( prop, value ),
				target = tween.cur(),
				parts = rfxnum.exec( value ),
				unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),

				// Starting value computation is required for potential unit mismatches
				start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) &&
					rfxnum.exec( jQuery.css( tween.elem, prop ) ),
				scale = 1,
				maxIterations = 20;

			if ( start && start[ 3 ] !== unit ) {
				// Trust units reported by jQuery.css
				unit = unit || start[ 3 ];

				// Make sure we update the tween properties later on
				parts = parts || [];

				// Iteratively approximate from a nonzero starting point
				start = +target || 1;

				do {
					// If previous iteration zeroed out, double until we get *something*
					// Use a string for doubling factor so we don't accidentally see scale as unchanged below
					scale = scale || ".5";

					// Adjust and apply
					start = start / scale;
					jQuery.style( tween.elem, prop, start + unit );

				// Update scale, tolerating zero or NaN from tween.cur()
				// And breaking the loop if scale is unchanged or perfect, or if we've just had enough
				} while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
			}

			// Update tween properties
			if ( parts ) {
				start = tween.start = +start || +target || 0;
				tween.unit = unit;
				// If a +=/-= token was provided, we're doing a relative animation
				tween.end = parts[ 1 ] ?
					start + ( parts[ 1 ] + 1 ) * parts[ 2 ] :
					+parts[ 2 ];
			}

			return tween;
		} ]
	};

// Animations created synchronously will run synchronously
function createFxNow() {
	setTimeout(function() {
		fxNow = undefined;
	});
	return ( fxNow = jQuery.now() );
}

// Generate parameters to create a standard animation
function genFx( type, includeWidth ) {
	var which,
		attrs = { height: type },
		i = 0;

	// if we include width, step value is 1 to do all cssExpand values,
	// if we don't include width, step value is 2 to skip over Left and Right
	includeWidth = includeWidth ? 1 : 0;
	for ( ; i < 4 ; i += 2 - includeWidth ) {
		which = cssExpand[ i ];
		attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
	}

	if ( includeWidth ) {
		attrs.opacity = attrs.width = type;
	}

	return attrs;
}

function createTween( value, prop, animation ) {
	var tween,
		collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
		index = 0,
		length = collection.length;
	for ( ; index < length; index++ ) {
		if ( (tween = collection[ index ].call( animation, prop, value )) ) {

			// we're done with this property
			return tween;
		}
	}
}

function defaultPrefilter( elem, props, opts ) {
	/* jshint validthis: true */
	var prop, value, toggle, tween, hooks, oldfire, display, checkDisplay,
		anim = this,
		orig = {},
		style = elem.style,
		hidden = elem.nodeType && isHidden( elem ),
		dataShow = jQuery._data( elem, "fxshow" );

	// handle queue: false promises
	if ( !opts.queue ) {
		hooks = jQuery._queueHooks( elem, "fx" );
		if ( hooks.unqueued == null ) {
			hooks.unqueued = 0;
			oldfire = hooks.empty.fire;
			hooks.empty.fire = function() {
				if ( !hooks.unqueued ) {
					oldfire();
				}
			};
		}
		hooks.unqueued++;

		anim.always(function() {
			// doing this makes sure that the complete handler will be called
			// before this completes
			anim.always(function() {
				hooks.unqueued--;
				if ( !jQuery.queue( elem, "fx" ).length ) {
					hooks.empty.fire();
				}
			});
		});
	}

	// height/width overflow pass
	if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
		// Make sure that nothing sneaks out
		// Record all 3 overflow attributes because IE does not
		// change the overflow attribute when overflowX and
		// overflowY are set to the same value
		opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];

		// Set display property to inline-block for height/width
		// animations on inline elements that are having width/height animated
		display = jQuery.css( elem, "display" );

		// Test default display if display is currently "none"
		checkDisplay = display === "none" ?
			jQuery._data( elem, "olddisplay" ) || defaultDisplay( elem.nodeName ) : display;

		if ( checkDisplay === "inline" && jQuery.css( elem, "float" ) === "none" ) {

			// inline-level elements accept inline-block;
			// block-level elements need to be inline with layout
			if ( !support.inlineBlockNeedsLayout || defaultDisplay( elem.nodeName ) === "inline" ) {
				style.display = "inline-block";
			} else {
				style.zoom = 1;
			}
		}
	}

	if ( opts.overflow ) {
		style.overflow = "hidden";
		if ( !support.shrinkWrapBlocks() ) {
			anim.always(function() {
				style.overflow = opts.overflow[ 0 ];
				style.overflowX = opts.overflow[ 1 ];
				style.overflowY = opts.overflow[ 2 ];
			});
		}
	}

	// show/hide pass
	for ( prop in props ) {
		value = props[ prop ];
		if ( rfxtypes.exec( value ) ) {
			delete props[ prop ];
			toggle = toggle || value === "toggle";
			if ( value === ( hidden ? "hide" : "show" ) ) {

				// If there is dataShow left over from a stopped hide or show and we are going to proceed with show, we should pretend to be hidden
				if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
					hidden = true;
				} else {
					continue;
				}
			}
			orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );

		// Any non-fx value stops us from restoring the original display value
		} else {
			display = undefined;
		}
	}

	if ( !jQuery.isEmptyObject( orig ) ) {
		if ( dataShow ) {
			if ( "hidden" in dataShow ) {
				hidden = dataShow.hidden;
			}
		} else {
			dataShow = jQuery._data( elem, "fxshow", {} );
		}

		// store state if its toggle - enables .stop().toggle() to "reverse"
		if ( toggle ) {
			dataShow.hidden = !hidden;
		}
		if ( hidden ) {
			jQuery( elem ).show();
		} else {
			anim.done(function() {
				jQuery( elem ).hide();
			});
		}
		anim.done(function() {
			var prop;
			jQuery._removeData( elem, "fxshow" );
			for ( prop in orig ) {
				jQuery.style( elem, prop, orig[ prop ] );
			}
		});
		for ( prop in orig ) {
			tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );

			if ( !( prop in dataShow ) ) {
				dataShow[ prop ] = tween.start;
				if ( hidden ) {
					tween.end = tween.start;
					tween.start = prop === "width" || prop === "height" ? 1 : 0;
				}
			}
		}

	// If this is a noop like .hide().hide(), restore an overwritten display value
	} else if ( (display === "none" ? defaultDisplay( elem.nodeName ) : display) === "inline" ) {
		style.display = display;
	}
}

function propFilter( props, specialEasing ) {
	var index, name, easing, value, hooks;

	// camelCase, specialEasing and expand cssHook pass
	for ( index in props ) {
		name = jQuery.camelCase( index );
		easing = specialEasing[ name ];
		value = props[ index ];
		if ( jQuery.isArray( value ) ) {
			easing = value[ 1 ];
			value = props[ index ] = value[ 0 ];
		}

		if ( index !== name ) {
			props[ name ] = value;
			delete props[ index ];
		}

		hooks = jQuery.cssHooks[ name ];
		if ( hooks && "expand" in hooks ) {
			value = hooks.expand( value );
			delete props[ name ];

			// not quite $.extend, this wont overwrite keys already present.
			// also - reusing 'index' from above because we have the correct "name"
			for ( index in value ) {
				if ( !( index in props ) ) {
					props[ index ] = value[ index ];
					specialEasing[ index ] = easing;
				}
			}
		} else {
			specialEasing[ name ] = easing;
		}
	}
}

function Animation( elem, properties, options ) {
	var result,
		stopped,
		index = 0,
		length = animationPrefilters.length,
		deferred = jQuery.Deferred().always( function() {
			// don't match elem in the :animated selector
			delete tick.elem;
		}),
		tick = function() {
			if ( stopped ) {
				return false;
			}
			var currentTime = fxNow || createFxNow(),
				remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
				// archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
				temp = remaining / animation.duration || 0,
				percent = 1 - temp,
				index = 0,
				length = animation.tweens.length;

			for ( ; index < length ; index++ ) {
				animation.tweens[ index ].run( percent );
			}

			deferred.notifyWith( elem, [ animation, percent, remaining ]);

			if ( percent < 1 && length ) {
				return remaining;
			} else {
				deferred.resolveWith( elem, [ animation ] );
				return false;
			}
		},
		animation = deferred.promise({
			elem: elem,
			props: jQuery.extend( {}, properties ),
			opts: jQuery.extend( true, { specialEasing: {} }, options ),
			originalProperties: properties,
			originalOptions: options,
			startTime: fxNow || createFxNow(),
			duration: options.duration,
			tweens: [],
			createTween: function( prop, end ) {
				var tween = jQuery.Tween( elem, animation.opts, prop, end,
						animation.opts.specialEasing[ prop ] || animation.opts.easing );
				animation.tweens.push( tween );
				return tween;
			},
			stop: function( gotoEnd ) {
				var index = 0,
					// if we are going to the end, we want to run all the tweens
					// otherwise we skip this part
					length = gotoEnd ? animation.tweens.length : 0;
				if ( stopped ) {
					return this;
				}
				stopped = true;
				for ( ; index < length ; index++ ) {
					animation.tweens[ index ].run( 1 );
				}

				// resolve when we played the last frame
				// otherwise, reject
				if ( gotoEnd ) {
					deferred.resolveWith( elem, [ animation, gotoEnd ] );
				} else {
					deferred.rejectWith( elem, [ animation, gotoEnd ] );
				}
				return this;
			}
		}),
		props = animation.props;

	propFilter( props, animation.opts.specialEasing );

	for ( ; index < length ; index++ ) {
		result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
		if ( result ) {
			return result;
		}
	}

	jQuery.map( props, createTween, animation );

	if ( jQuery.isFunction( animation.opts.start ) ) {
		animation.opts.start.call( elem, animation );
	}

	jQuery.fx.timer(
		jQuery.extend( tick, {
			elem: elem,
			anim: animation,
			queue: animation.opts.queue
		})
	);

	// attach callbacks from options
	return animation.progress( animation.opts.progress )
		.done( animation.opts.done, animation.opts.complete )
		.fail( animation.opts.fail )
		.always( animation.opts.always );
}

jQuery.Animation = jQuery.extend( Animation, {
	tweener: function( props, callback ) {
		if ( jQuery.isFunction( props ) ) {
			callback = props;
			props = [ "*" ];
		} else {
			props = props.split(" ");
		}

		var prop,
			index = 0,
			length = props.length;

		for ( ; index < length ; index++ ) {
			prop = props[ index ];
			tweeners[ prop ] = tweeners[ prop ] || [];
			tweeners[ prop ].unshift( callback );
		}
	},

	prefilter: function( callback, prepend ) {
		if ( prepend ) {
			animationPrefilters.unshift( callback );
		} else {
			animationPrefilters.push( callback );
		}
	}
});

jQuery.speed = function( speed, easing, fn ) {
	var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
		complete: fn || !fn && easing ||
			jQuery.isFunction( speed ) && speed,
		duration: speed,
		easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
	};

	opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
		opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;

	// normalize opt.queue - true/undefined/null -> "fx"
	if ( opt.queue == null || opt.queue === true ) {
		opt.queue = "fx";
	}

	// Queueing
	opt.old = opt.complete;

	opt.complete = function() {
		if ( jQuery.isFunction( opt.old ) ) {
			opt.old.call( this );
		}

		if ( opt.queue ) {
			jQuery.dequeue( this, opt.queue );
		}
	};

	return opt;
};

jQuery.fn.extend({
	fadeTo: function( speed, to, easing, callback ) {

		// show any hidden elements after setting opacity to 0
		return this.filter( isHidden ).css( "opacity", 0 ).show()

			// animate to the value specified
			.end().animate({ opacity: to }, speed, easing, callback );
	},
	animate: function( prop, speed, easing, callback ) {
		var empty = jQuery.isEmptyObject( prop ),
			optall = jQuery.speed( speed, easing, callback ),
			doAnimation = function() {
				// Operate on a copy of prop so per-property easing won't be lost
				var anim = Animation( this, jQuery.extend( {}, prop ), optall );

				// Empty animations, or finishing resolves immediately
				if ( empty || jQuery._data( this, "finish" ) ) {
					anim.stop( true );
				}
			};
			doAnimation.finish = doAnimation;

		return empty || optall.queue === false ?
			this.each( doAnimation ) :
			this.queue( optall.queue, doAnimation );
	},
	stop: function( type, clearQueue, gotoEnd ) {
		var stopQueue = function( hooks ) {
			var stop = hooks.stop;
			delete hooks.stop;
			stop( gotoEnd );
		};

		if ( typeof type !== "string" ) {
			gotoEnd = clearQueue;
			clearQueue = type;
			type = undefined;
		}
		if ( clearQueue && type !== false ) {
			this.queue( type || "fx", [] );
		}

		return this.each(function() {
			var dequeue = true,
				index = type != null && type + "queueHooks",
				timers = jQuery.timers,
				data = jQuery._data( this );

			if ( index ) {
				if ( data[ index ] && data[ index ].stop ) {
					stopQueue( data[ index ] );
				}
			} else {
				for ( index in data ) {
					if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
						stopQueue( data[ index ] );
					}
				}
			}

			for ( index = timers.length; index--; ) {
				if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
					timers[ index ].anim.stop( gotoEnd );
					dequeue = false;
					timers.splice( index, 1 );
				}
			}

			// start the next in the queue if the last step wasn't forced
			// timers currently will call their complete callbacks, which will dequeue
			// but only if they were gotoEnd
			if ( dequeue || !gotoEnd ) {
				jQuery.dequeue( this, type );
			}
		});
	},
	finish: function( type ) {
		if ( type !== false ) {
			type = type || "fx";
		}
		return this.each(function() {
			var index,
				data = jQuery._data( this ),
				queue = data[ type + "queue" ],
				hooks = data[ type + "queueHooks" ],
				timers = jQuery.timers,
				length = queue ? queue.length : 0;

			// enable finishing flag on private data
			data.finish = true;

			// empty the queue first
			jQuery.queue( this, type, [] );

			if ( hooks && hooks.stop ) {
				hooks.stop.call( this, true );
			}

			// look for any active animations, and finish them
			for ( index = timers.length; index--; ) {
				if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
					timers[ index ].anim.stop( true );
					timers.splice( index, 1 );
				}
			}

			// look for any animations in the old queue and finish them
			for ( index = 0; index < length; index++ ) {
				if ( queue[ index ] && queue[ index ].finish ) {
					queue[ index ].finish.call( this );
				}
			}

			// turn off finishing flag
			delete data.finish;
		});
	}
});

jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
	var cssFn = jQuery.fn[ name ];
	jQuery.fn[ name ] = function( speed, easing, callback ) {
		return speed == null || typeof speed === "boolean" ?
			cssFn.apply( this, arguments ) :
			this.animate( genFx( name, true ), speed, easing, callback );
	};
});

// Generate shortcuts for custom animations
jQuery.each({
	slideDown: genFx("show"),
	slideUp: genFx("hide"),
	slideToggle: genFx("toggle"),
	fadeIn: { opacity: "show" },
	fadeOut: { opacity: "hide" },
	fadeToggle: { opacity: "toggle" }
}, function( name, props ) {
	jQuery.fn[ name ] = function( speed, easing, callback ) {
		return this.animate( props, speed, easing, callback );
	};
});

jQuery.timers = [];
jQuery.fx.tick = function() {
	var timer,
		timers = jQuery.timers,
		i = 0;

	fxNow = jQuery.now();

	for ( ; i < timers.length; i++ ) {
		timer = timers[ i ];
		// Checks the timer has not already been removed
		if ( !timer() && timers[ i ] === timer ) {
			timers.splice( i--, 1 );
		}
	}

	if ( !timers.length ) {
		jQuery.fx.stop();
	}
	fxNow = undefined;
};

jQuery.fx.timer = function( timer ) {
	jQuery.timers.push( timer );
	if ( timer() ) {
		jQuery.fx.start();
	} else {
		jQuery.timers.pop();
	}
};

jQuery.fx.interval = 13;

jQuery.fx.start = function() {
	if ( !timerId ) {
		timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
	}
};

jQuery.fx.stop = function() {
	clearInterval( timerId );
	timerId = null;
};

jQuery.fx.speeds = {
	slow: 600,
	fast: 200,
	// Default speed
	_default: 400
};


// Based off of the plugin by Clint Helfers, with permission.
// http://blindsignals.com/index.php/2009/07/jquery-delay/
jQuery.fn.delay = function( time, type ) {
	time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
	type = type || "fx";

	return this.queue( type, function( next, hooks ) {
		var timeout = setTimeout( next, time );
		hooks.stop = function() {
			clearTimeout( timeout );
		};
	});
};


(function() {
	// Minified: var a,b,c,d,e
	var input, div, select, a, opt;

	// Setup
	div = document.createElement( "div" );
	div.setAttribute( "className", "t" );
	div.innerHTML = "  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
	a = div.getElementsByTagName("a")[ 0 ];

	// First batch of tests.
	select = document.createElement("select");
	opt = select.appendChild( document.createElement("option") );
	input = div.getElementsByTagName("input")[ 0 ];

	a.style.cssText = "top:1px";

	// Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
	support.getSetAttribute = div.className !== "t";

	// Get the style information from getAttribute
	// (IE uses .cssText instead)
	support.style = /top/.test( a.getAttribute("style") );

	// Make sure that URLs aren't manipulated
	// (IE normalizes it by default)
	support.hrefNormalized = a.getAttribute("href") === "/a";

	// Check the default checkbox/radio value ("" on WebKit; "on" elsewhere)
	support.checkOn = !!input.value;

	// Make sure that a selected-by-default option has a working selected property.
	// (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
	support.optSelected = opt.selected;

	// Tests for enctype support on a form (#6743)
	support.enctype = !!document.createElement("form").enctype;

	// Make sure that the options inside disabled selects aren't marked as disabled
	// (WebKit marks them as disabled)
	select.disabled = true;
	support.optDisabled = !opt.disabled;

	// Support: IE8 only
	// Check if we can trust getAttribute("value")
	input = document.createElement( "input" );
	input.setAttribute( "value", "" );
	support.input = input.getAttribute( "value" ) === "";

	// Check if an input maintains its value after becoming a radio
	input.value = "t";
	input.setAttribute( "type", "radio" );
	support.radioValue = input.value === "t";
})();


var rreturn = /\r/g;

jQuery.fn.extend({
	val: function( value ) {
		var hooks, ret, isFunction,
			elem = this[0];

		if ( !arguments.length ) {
			if ( elem ) {
				hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];

				if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
					return ret;
				}

				ret = elem.value;

				return typeof ret === "string" ?
					// handle most common string cases
					ret.replace(rreturn, "") :
					// handle cases where value is null/undef or number
					ret == null ? "" : ret;
			}

			return;
		}

		isFunction = jQuery.isFunction( value );

		return this.each(function( i ) {
			var val;

			if ( this.nodeType !== 1 ) {
				return;
			}

			if ( isFunction ) {
				val = value.call( this, i, jQuery( this ).val() );
			} else {
				val = value;
			}

			// Treat null/undefined as ""; convert numbers to string
			if ( val == null ) {
				val = "";
			} else if ( typeof val === "number" ) {
				val += "";
			} else if ( jQuery.isArray( val ) ) {
				val = jQuery.map( val, function( value ) {
					return value == null ? "" : value + "";
				});
			}

			hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];

			// If set returns undefined, fall back to normal setting
			if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
				this.value = val;
			}
		});
	}
});

jQuery.extend({
	valHooks: {
		option: {
			get: function( elem ) {
				var val = jQuery.find.attr( elem, "value" );
				return val != null ?
					val :
					// Support: IE10-11+
					// option.text throws exceptions (#14686, #14858)
					jQuery.trim( jQuery.text( elem ) );
			}
		},
		select: {
			get: function( elem ) {
				var value, option,
					options = elem.options,
					index = elem.selectedIndex,
					one = elem.type === "select-one" || index < 0,
					values = one ? null : [],
					max = one ? index + 1 : options.length,
					i = index < 0 ?
						max :
						one ? index : 0;

				// Loop through all the selected options
				for ( ; i < max; i++ ) {
					option = options[ i ];

					// oldIE doesn't update selected after form reset (#2551)
					if ( ( option.selected || i === index ) &&
							// Don't return options that are disabled or in a disabled optgroup
							( support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
							( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {

						// Get the specific value for the option
						value = jQuery( option ).val();

						// We don't need an array for one selects
						if ( one ) {
							return value;
						}

						// Multi-Selects return an array
						values.push( value );
					}
				}

				return values;
			},

			set: function( elem, value ) {
				var optionSet, option,
					options = elem.options,
					values = jQuery.makeArray( value ),
					i = options.length;

				while ( i-- ) {
					option = options[ i ];

					if ( jQuery.inArray( jQuery.valHooks.option.get( option ), values ) >= 0 ) {

						// Support: IE6
						// When new option element is added to select box we need to
						// force reflow of newly added node in order to workaround delay
						// of initialization properties
						try {
							option.selected = optionSet = true;

						} catch ( _ ) {

							// Will be executed only in IE6
							option.scrollHeight;
						}

					} else {
						option.selected = false;
					}
				}

				// Force browsers to behave consistently when non-matching value is set
				if ( !optionSet ) {
					elem.selectedIndex = -1;
				}

				return options;
			}
		}
	}
});

// Radios and checkboxes getter/setter
jQuery.each([ "radio", "checkbox" ], function() {
	jQuery.valHooks[ this ] = {
		set: function( elem, value ) {
			if ( jQuery.isArray( value ) ) {
				return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
			}
		}
	};
	if ( !support.checkOn ) {
		jQuery.valHooks[ this ].get = function( elem ) {
			// Support: Webkit
			// "" is returned instead of "on" if a value isn't specified
			return elem.getAttribute("value") === null ? "on" : elem.value;
		};
	}
});




var nodeHook, boolHook,
	attrHandle = jQuery.expr.attrHandle,
	ruseDefault = /^(?:checked|selected)$/i,
	getSetAttribute = support.getSetAttribute,
	getSetInput = support.input;

jQuery.fn.extend({
	attr: function( name, value ) {
		return access( this, jQuery.attr, name, value, arguments.length > 1 );
	},

	removeAttr: function( name ) {
		return this.each(function() {
			jQuery.removeAttr( this, name );
		});
	}
});

jQuery.extend({
	attr: function( elem, name, value ) {
		var hooks, ret,
			nType = elem.nodeType;

		// don't get/set attributes on text, comment and attribute nodes
		if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
			return;
		}

		// Fallback to prop when attributes are not supported
		if ( typeof elem.getAttribute === strundefined ) {
			return jQuery.prop( elem, name, value );
		}

		// All attributes are lowercase
		// Grab necessary hook if one is defined
		if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
			name = name.toLowerCase();
			hooks = jQuery.attrHooks[ name ] ||
				( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );
		}

		if ( value !== undefined ) {

			if ( value === null ) {
				jQuery.removeAttr( elem, name );

			} else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
				return ret;

			} else {
				elem.setAttribute( name, value + "" );
				return value;
			}

		} else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
			return ret;

		} else {
			ret = jQuery.find.attr( elem, name );

			// Non-existent attributes return null, we normalize to undefined
			return ret == null ?
				undefined :
				ret;
		}
	},

	removeAttr: function( elem, value ) {
		var name, propName,
			i = 0,
			attrNames = value && value.match( rnotwhite );

		if ( attrNames && elem.nodeType === 1 ) {
			while ( (name = attrNames[i++]) ) {
				propName = jQuery.propFix[ name ] || name;

				// Boolean attributes get special treatment (#10870)
				if ( jQuery.expr.match.bool.test( name ) ) {
					// Set corresponding property to false
					if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
						elem[ propName ] = false;
					// Support: IE<9
					// Also clear defaultChecked/defaultSelected (if appropriate)
					} else {
						elem[ jQuery.camelCase( "default-" + name ) ] =
							elem[ propName ] = false;
					}

				// See #9699 for explanation of this approach (setting first, then removal)
				} else {
					jQuery.attr( elem, name, "" );
				}

				elem.removeAttribute( getSetAttribute ? name : propName );
			}
		}
	},

	attrHooks: {
		type: {
			set: function( elem, value ) {
				if ( !support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
					// Setting the type on a radio button after the value resets the value in IE6-9
					// Reset value to default in case type is set after value during creation
					var val = elem.value;
					elem.setAttribute( "type", value );
					if ( val ) {
						elem.value = val;
					}
					return value;
				}
			}
		}
	}
});

// Hook for boolean attributes
boolHook = {
	set: function( elem, value, name ) {
		if ( value === false ) {
			// Remove boolean attributes when set to false
			jQuery.removeAttr( elem, name );
		} else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
			// IE<8 needs the *property* name
			elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name );

		// Use defaultChecked and defaultSelected for oldIE
		} else {
			elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true;
		}

		return name;
	}
};

// Retrieve booleans specially
jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {

	var getter = attrHandle[ name ] || jQuery.find.attr;

	attrHandle[ name ] = getSetInput && getSetAttribute || !ruseDefault.test( name ) ?
		function( elem, name, isXML ) {
			var ret, handle;
			if ( !isXML ) {
				// Avoid an infinite loop by temporarily removing this function from the getter
				handle = attrHandle[ name ];
				attrHandle[ name ] = ret;
				ret = getter( elem, name, isXML ) != null ?
					name.toLowerCase() :
					null;
				attrHandle[ name ] = handle;
			}
			return ret;
		} :
		function( elem, name, isXML ) {
			if ( !isXML ) {
				return elem[ jQuery.camelCase( "default-" + name ) ] ?
					name.toLowerCase() :
					null;
			}
		};
});

// fix oldIE attroperties
if ( !getSetInput || !getSetAttribute ) {
	jQuery.attrHooks.value = {
		set: function( elem, value, name ) {
			if ( jQuery.nodeName( elem, "input" ) ) {
				// Does not return so that setAttribute is also used
				elem.defaultValue = value;
			} else {
				// Use nodeHook if defined (#1954); otherwise setAttribute is fine
				return nodeHook && nodeHook.set( elem, value, name );
			}
		}
	};
}

// IE6/7 do not support getting/setting some attributes with get/setAttribute
if ( !getSetAttribute ) {

	// Use this for any attribute in IE6/7
	// This fixes almost every IE6/7 issue
	nodeHook = {
		set: function( elem, value, name ) {
			// Set the existing or create a new attribute node
			var ret = elem.getAttributeNode( name );
			if ( !ret ) {
				elem.setAttributeNode(
					(ret = elem.ownerDocument.createAttribute( name ))
				);
			}

			ret.value = value += "";

			// Break association with cloned elements by also using setAttribute (#9646)
			if ( name === "value" || value === elem.getAttribute( name ) ) {
				return value;
			}
		}
	};

	// Some attributes are constructed with empty-string values when not defined
	attrHandle.id = attrHandle.name = attrHandle.coords =
		function( elem, name, isXML ) {
			var ret;
			if ( !isXML ) {
				return (ret = elem.getAttributeNode( name )) && ret.value !== "" ?
					ret.value :
					null;
			}
		};

	// Fixing value retrieval on a button requires this module
	jQuery.valHooks.button = {
		get: function( elem, name ) {
			var ret = elem.getAttributeNode( name );
			if ( ret && ret.specified ) {
				return ret.value;
			}
		},
		set: nodeHook.set
	};

	// Set contenteditable to false on removals(#10429)
	// Setting to empty string throws an error as an invalid value
	jQuery.attrHooks.contenteditable = {
		set: function( elem, value, name ) {
			nodeHook.set( elem, value === "" ? false : value, name );
		}
	};

	// Set width and height to auto instead of 0 on empty string( Bug #8150 )
	// This is for removals
	jQuery.each([ "width", "height" ], function( i, name ) {
		jQuery.attrHooks[ name ] = {
			set: function( elem, value ) {
				if ( value === "" ) {
					elem.setAttribute( name, "auto" );
					return value;
				}
			}
		};
	});
}

if ( !support.style ) {
	jQuery.attrHooks.style = {
		get: function( elem ) {
			// Return undefined in the case of empty string
			// Note: IE uppercases css property names, but if we were to .toLowerCase()
			// .cssText, that would destroy case senstitivity in URL's, like in "background"
			return elem.style.cssText || undefined;
		},
		set: function( elem, value ) {
			return ( elem.style.cssText = value + "" );
		}
	};
}




var rfocusable = /^(?:input|select|textarea|button|object)$/i,
	rclickable = /^(?:a|area)$/i;

jQuery.fn.extend({
	prop: function( name, value ) {
		return access( this, jQuery.prop, name, value, arguments.length > 1 );
	},

	removeProp: function( name ) {
		name = jQuery.propFix[ name ] || name;
		return this.each(function() {
			// try/catch handles cases where IE balks (such as removing a property on window)
			try {
				this[ name ] = undefined;
				delete this[ name ];
			} catch( e ) {}
		});
	}
});

jQuery.extend({
	propFix: {
		"for": "htmlFor",
		"class": "className"
	},

	prop: function( elem, name, value ) {
		var ret, hooks, notxml,
			nType = elem.nodeType;

		// don't get/set properties on text, comment and attribute nodes
		if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
			return;
		}

		notxml = nType !== 1 || !jQuery.isXMLDoc( elem );

		if ( notxml ) {
			// Fix name and attach hooks
			name = jQuery.propFix[ name ] || name;
			hooks = jQuery.propHooks[ name ];
		}

		if ( value !== undefined ) {
			return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ?
				ret :
				( elem[ name ] = value );

		} else {
			return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ?
				ret :
				elem[ name ];
		}
	},

	propHooks: {
		tabIndex: {
			get: function( elem ) {
				// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
				// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
				// Use proper attribute retrieval(#12072)
				var tabindex = jQuery.find.attr( elem, "tabindex" );

				return tabindex ?
					parseInt( tabindex, 10 ) :
					rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
						0 :
						-1;
			}
		}
	}
});

// Some attributes require a special call on IE
// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
if ( !support.hrefNormalized ) {
	// href/src property should get the full normalized URL (#10299/#12915)
	jQuery.each([ "href", "src" ], function( i, name ) {
		jQuery.propHooks[ name ] = {
			get: function( elem ) {
				return elem.getAttribute( name, 4 );
			}
		};
	});
}

// Support: Safari, IE9+
// mis-reports the default selected property of an option
// Accessing the parent's selectedIndex property fixes it
if ( !support.optSelected ) {
	jQuery.propHooks.selected = {
		get: function( elem ) {
			var parent = elem.parentNode;

			if ( parent ) {
				parent.selectedIndex;

				// Make sure that it also works with optgroups, see #5701
				if ( parent.parentNode ) {
					parent.parentNode.selectedIndex;
				}
			}
			return null;
		}
	};
}

jQuery.each([
	"tabIndex",
	"readOnly",
	"maxLength",
	"cellSpacing",
	"cellPadding",
	"rowSpan",
	"colSpan",
	"useMap",
	"frameBorder",
	"contentEditable"
], function() {
	jQuery.propFix[ this.toLowerCase() ] = this;
});

// IE6/7 call enctype encoding
if ( !support.enctype ) {
	jQuery.propFix.enctype = "encoding";
}




var rclass = /[\t\r\n\f]/g;

jQuery.fn.extend({
	addClass: function( value ) {
		var classes, elem, cur, clazz, j, finalValue,
			i = 0,
			len = this.length,
			proceed = typeof value === "string" && value;

		if ( jQuery.isFunction( value ) ) {
			return this.each(function( j ) {
				jQuery( this ).addClass( value.call( this, j, this.className ) );
			});
		}

		if ( proceed ) {
			// The disjunction here is for better compressibility (see removeClass)
			classes = ( value || "" ).match( rnotwhite ) || [];

			for ( ; i < len; i++ ) {
				elem = this[ i ];
				cur = elem.nodeType === 1 && ( elem.className ?
					( " " + elem.className + " " ).replace( rclass, " " ) :
					" "
				);

				if ( cur ) {
					j = 0;
					while ( (clazz = classes[j++]) ) {
						if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
							cur += clazz + " ";
						}
					}

					// only assign if different to avoid unneeded rendering.
					finalValue = jQuery.trim( cur );
					if ( elem.className !== finalValue ) {
						elem.className = finalValue;
					}
				}
			}
		}

		return this;
	},

	removeClass: function( value ) {
		var classes, elem, cur, clazz, j, finalValue,
			i = 0,
			len = this.length,
			proceed = arguments.length === 0 || typeof value === "string" && value;

		if ( jQuery.isFunction( value ) ) {
			return this.each(function( j ) {
				jQuery( this ).removeClass( value.call( this, j, this.className ) );
			});
		}
		if ( proceed ) {
			classes = ( value || "" ).match( rnotwhite ) || [];

			for ( ; i < len; i++ ) {
				elem = this[ i ];
				// This expression is here for better compressibility (see addClass)
				cur = elem.nodeType === 1 && ( elem.className ?
					( " " + elem.className + " " ).replace( rclass, " " ) :
					""
				);

				if ( cur ) {
					j = 0;
					while ( (clazz = classes[j++]) ) {
						// Remove *all* instances
						while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
							cur = cur.replace( " " + clazz + " ", " " );
						}
					}

					// only assign if different to avoid unneeded rendering.
					finalValue = value ? jQuery.trim( cur ) : "";
					if ( elem.className !== finalValue ) {
						elem.className = finalValue;
					}
				}
			}
		}

		return this;
	},

	toggleClass: function( value, stateVal ) {
		var type = typeof value;

		if ( typeof stateVal === "boolean" && type === "string" ) {
			return stateVal ? this.addClass( value ) : this.removeClass( value );
		}

		if ( jQuery.isFunction( value ) ) {
			return this.each(function( i ) {
				jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
			});
		}

		return this.each(function() {
			if ( type === "string" ) {
				// toggle individual class names
				var className,
					i = 0,
					self = jQuery( this ),
					classNames = value.match( rnotwhite ) || [];

				while ( (className = classNames[ i++ ]) ) {
					// check each className given, space separated list
					if ( self.hasClass( className ) ) {
						self.removeClass( className );
					} else {
						self.addClass( className );
					}
				}

			// Toggle whole class name
			} else if ( type === strundefined || type === "boolean" ) {
				if ( this.className ) {
					// store className if set
					jQuery._data( this, "__className__", this.className );
				}

				// If the element has a class name or if we're passed "false",
				// then remove the whole classname (if there was one, the above saved it).
				// Otherwise bring back whatever was previously saved (if anything),
				// falling back to the empty string if nothing was stored.
				this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
			}
		});
	},

	hasClass: function( selector ) {
		var className = " " + selector + " ",
			i = 0,
			l = this.length;
		for ( ; i < l; i++ ) {
			if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
				return true;
			}
		}

		return false;
	}
});




// Return jQuery for attributes-only inclusion


jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
	"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
	"change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {

	// Handle event binding
	jQuery.fn[ name ] = function( data, fn ) {
		return arguments.length > 0 ?
			this.on( name, null, data, fn ) :
			this.trigger( name );
	};
});

jQuery.fn.extend({
	hover: function( fnOver, fnOut ) {
		return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
	},

	bind: function( types, data, fn ) {
		return this.on( types, null, data, fn );
	},
	unbind: function( types, fn ) {
		return this.off( types, null, fn );
	},

	delegate: function( selector, types, data, fn ) {
		return this.on( types, selector, data, fn );
	},
	undelegate: function( selector, types, fn ) {
		// ( namespace ) or ( selector, types [, fn] )
		return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
	}
});


var nonce = jQuery.now();

var rquery = (/\?/);



var rvalidtokens = /(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;

jQuery.parseJSON = function( data ) {
	// Attempt to parse using the native JSON parser first
	if ( window.JSON && window.JSON.parse ) {
		// Support: Android 2.3
		// Workaround failure to string-cast null input
		return window.JSON.parse( data + "" );
	}

	var requireNonComma,
		depth = null,
		str = jQuery.trim( data + "" );

	// Guard against invalid (and possibly dangerous) input by ensuring that nothing remains
	// after removing valid tokens
	return str && !jQuery.trim( str.replace( rvalidtokens, function( token, comma, open, close ) {

		// Force termination if we see a misplaced comma
		if ( requireNonComma && comma ) {
			depth = 0;
		}

		// Perform no more replacements after returning to outermost depth
		if ( depth === 0 ) {
			return token;
		}

		// Commas must not follow "[", "{", or ","
		requireNonComma = open || comma;

		// Determine new depth
		// array/object open ("[" or "{"): depth += true - false (increment)
		// array/object close ("]" or "}"): depth += false - true (decrement)
		// other cases ("," or primitive): depth += true - true (numeric cast)
		depth += !close - !open;

		// Remove this token
		return "";
	}) ) ?
		( Function( "return " + str ) )() :
		jQuery.error( "Invalid JSON: " + data );
};


// Cross-browser xml parsing
jQuery.parseXML = function( data ) {
	var xml, tmp;
	if ( !data || typeof data !== "string" ) {
		return null;
	}
	try {
		if ( window.DOMParser ) { // Standard
			tmp = new DOMParser();
			xml = tmp.parseFromString( data, "text/xml" );
		} else { // IE
			xml = new ActiveXObject( "Microsoft.XMLDOM" );
			xml.async = "false";
			xml.loadXML( data );
		}
	} catch( e ) {
		xml = undefined;
	}
	if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
		jQuery.error( "Invalid XML: " + data );
	}
	return xml;
};


var
	// Document location
	ajaxLocParts,
	ajaxLocation,

	rhash = /#.*$/,
	rts = /([?&])_=[^&]*/,
	rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
	// #7653, #8125, #8152: local protocol detection
	rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
	rnoContent = /^(?:GET|HEAD)$/,
	rprotocol = /^\/\//,
	rurl = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,

	/* Prefilters
	 * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
	 * 2) These are called:
	 *    - BEFORE asking for a transport
	 *    - AFTER param serialization (s.data is a string if s.processData is true)
	 * 3) key is the dataType
	 * 4) the catchall symbol "*" can be used
	 * 5) execution will start with transport dataType and THEN continue down to "*" if needed
	 */
	prefilters = {},

	/* Transports bindings
	 * 1) key is the dataType
	 * 2) the catchall symbol "*" can be used
	 * 3) selection will start with transport dataType and THEN go to "*" if needed
	 */
	transports = {},

	// Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
	allTypes = "*/".concat("*");

// #8138, IE may throw an exception when accessing
// a field from window.location if document.domain has been set
try {
	ajaxLocation = location.href;
} catch( e ) {
	// Use the href attribute of an A element
	// since IE will modify it given document.location
	ajaxLocation = document.createElement( "a" );
	ajaxLocation.href = "";
	ajaxLocation = ajaxLocation.href;
}

// Segment location into parts
ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];

// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
function addToPrefiltersOrTransports( structure ) {

	// dataTypeExpression is optional and defaults to "*"
	return function( dataTypeExpression, func ) {

		if ( typeof dataTypeExpression !== "string" ) {
			func = dataTypeExpression;
			dataTypeExpression = "*";
		}

		var dataType,
			i = 0,
			dataTypes = dataTypeExpression.toLowerCase().match( rnotwhite ) || [];

		if ( jQuery.isFunction( func ) ) {
			// For each dataType in the dataTypeExpression
			while ( (dataType = dataTypes[i++]) ) {
				// Prepend if requested
				if ( dataType.charAt( 0 ) === "+" ) {
					dataType = dataType.slice( 1 ) || "*";
					(structure[ dataType ] = structure[ dataType ] || []).unshift( func );

				// Otherwise append
				} else {
					(structure[ dataType ] = structure[ dataType ] || []).push( func );
				}
			}
		}
	};
}

// Base inspection function for prefilters and transports
function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {

	var inspected = {},
		seekingTransport = ( structure === transports );

	function inspect( dataType ) {
		var selected;
		inspected[ dataType ] = true;
		jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
			var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
			if ( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
				options.dataTypes.unshift( dataTypeOrTransport );
				inspect( dataTypeOrTransport );
				return false;
			} else if ( seekingTransport ) {
				return !( selected = dataTypeOrTransport );
			}
		});
		return selected;
	}

	return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
}

// A special extend for ajax options
// that takes "flat" options (not to be deep extended)
// Fixes #9887
function ajaxExtend( target, src ) {
	var deep, key,
		flatOptions = jQuery.ajaxSettings.flatOptions || {};

	for ( key in src ) {
		if ( src[ key ] !== undefined ) {
			( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ];
		}
	}
	if ( deep ) {
		jQuery.extend( true, target, deep );
	}

	return target;
}

/* Handles responses to an ajax request:
 * - finds the right dataType (mediates between content-type and expected dataType)
 * - returns the corresponding response
 */
function ajaxHandleResponses( s, jqXHR, responses ) {
	var firstDataType, ct, finalDataType, type,
		contents = s.contents,
		dataTypes = s.dataTypes;

	// Remove auto dataType and get content-type in the process
	while ( dataTypes[ 0 ] === "*" ) {
		dataTypes.shift();
		if ( ct === undefined ) {
			ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
		}
	}

	// Check if we're dealing with a known content-type
	if ( ct ) {
		for ( type in contents ) {
			if ( contents[ type ] && contents[ type ].test( ct ) ) {
				dataTypes.unshift( type );
				break;
			}
		}
	}

	// Check to see if we have a response for the expected dataType
	if ( dataTypes[ 0 ] in responses ) {
		finalDataType = dataTypes[ 0 ];
	} else {
		// Try convertible dataTypes
		for ( type in responses ) {
			if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
				finalDataType = type;
				break;
			}
			if ( !firstDataType ) {
				firstDataType = type;
			}
		}
		// Or just use first one
		finalDataType = finalDataType || firstDataType;
	}

	// If we found a dataType
	// We add the dataType to the list if needed
	// and return the corresponding response
	if ( finalDataType ) {
		if ( finalDataType !== dataTypes[ 0 ] ) {
			dataTypes.unshift( finalDataType );
		}
		return responses[ finalDataType ];
	}
}

/* Chain conversions given the request and the original response
 * Also sets the responseXXX fields on the jqXHR instance
 */
function ajaxConvert( s, response, jqXHR, isSuccess ) {
	var conv2, current, conv, tmp, prev,
		converters = {},
		// Work with a copy of dataTypes in case we need to modify it for conversion
		dataTypes = s.dataTypes.slice();

	// Create converters map with lowercased keys
	if ( dataTypes[ 1 ] ) {
		for ( conv in s.converters ) {
			converters[ conv.toLowerCase() ] = s.converters[ conv ];
		}
	}

	current = dataTypes.shift();

	// Convert to each sequential dataType
	while ( current ) {

		if ( s.responseFields[ current ] ) {
			jqXHR[ s.responseFields[ current ] ] = response;
		}

		// Apply the dataFilter if provided
		if ( !prev && isSuccess && s.dataFilter ) {
			response = s.dataFilter( response, s.dataType );
		}

		prev = current;
		current = dataTypes.shift();

		if ( current ) {

			// There's only work to do if current dataType is non-auto
			if ( current === "*" ) {

				current = prev;

			// Convert response if prev dataType is non-auto and differs from current
			} else if ( prev !== "*" && prev !== current ) {

				// Seek a direct converter
				conv = converters[ prev + " " + current ] || converters[ "* " + current ];

				// If none found, seek a pair
				if ( !conv ) {
					for ( conv2 in converters ) {

						// If conv2 outputs current
						tmp = conv2.split( " " );
						if ( tmp[ 1 ] === current ) {

							// If prev can be converted to accepted input
							conv = converters[ prev + " " + tmp[ 0 ] ] ||
								converters[ "* " + tmp[ 0 ] ];
							if ( conv ) {
								// Condense equivalence converters
								if ( conv === true ) {
									conv = converters[ conv2 ];

								// Otherwise, insert the intermediate dataType
								} else if ( converters[ conv2 ] !== true ) {
									current = tmp[ 0 ];
									dataTypes.unshift( tmp[ 1 ] );
								}
								break;
							}
						}
					}
				}

				// Apply converter (if not an equivalence)
				if ( conv !== true ) {

					// Unless errors are allowed to bubble, catch and return them
					if ( conv && s[ "throws" ] ) {
						response = conv( response );
					} else {
						try {
							response = conv( response );
						} catch ( e ) {
							return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
						}
					}
				}
			}
		}
	}

	return { state: "success", data: response };
}

jQuery.extend({

	// Counter for holding the number of active queries
	active: 0,

	// Last-Modified header cache for next request
	lastModified: {},
	etag: {},

	ajaxSettings: {
		url: ajaxLocation,
		type: "GET",
		isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
		global: true,
		processData: true,
		async: true,
		contentType: "application/x-www-form-urlencoded; charset=UTF-8",
		/*
		timeout: 0,
		data: null,
		dataType: null,
		username: null,
		password: null,
		cache: null,
		throws: false,
		traditional: false,
		headers: {},
		*/

		accepts: {
			"*": allTypes,
			text: "text/plain",
			html: "text/html",
			xml: "application/xml, text/xml",
			json: "application/json, text/javascript"
		},

		contents: {
			xml: /xml/,
			html: /html/,
			json: /json/
		},

		responseFields: {
			xml: "responseXML",
			text: "responseText",
			json: "responseJSON"
		},

		// Data converters
		// Keys separate source (or catchall "*") and destination types with a single space
		converters: {

			// Convert anything to text
			"* text": String,

			// Text to html (true = no transformation)
			"text html": true,

			// Evaluate text as a json expression
			"text json": jQuery.parseJSON,

			// Parse text as xml
			"text xml": jQuery.parseXML
		},

		// For options that shouldn't be deep extended:
		// you can add your own custom options here if
		// and when you create one that shouldn't be
		// deep extended (see ajaxExtend)
		flatOptions: {
			url: true,
			context: true
		}
	},

	// Creates a full fledged settings object into target
	// with both ajaxSettings and settings fields.
	// If target is omitted, writes into ajaxSettings.
	ajaxSetup: function( target, settings ) {
		return settings ?

			// Building a settings object
			ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :

			// Extending ajaxSettings
			ajaxExtend( jQuery.ajaxSettings, target );
	},

	ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
	ajaxTransport: addToPrefiltersOrTransports( transports ),

	// Main method
	ajax: function( url, options ) {

		// If url is an object, simulate pre-1.5 signature
		if ( typeof url === "object" ) {
			options = url;
			url = undefined;
		}

		// Force options to be an object
		options = options || {};

		var // Cross-domain detection vars
			parts,
			// Loop variable
			i,
			// URL without anti-cache param
			cacheURL,
			// Response headers as string
			responseHeadersString,
			// timeout handle
			timeoutTimer,

			// To know if global events are to be dispatched
			fireGlobals,

			transport,
			// Response headers
			responseHeaders,
			// Create the final options object
			s = jQuery.ajaxSetup( {}, options ),
			// Callbacks context
			callbackContext = s.context || s,
			// Context for global events is callbackContext if it is a DOM node or jQuery collection
			globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
				jQuery( callbackContext ) :
				jQuery.event,
			// Deferreds
			deferred = jQuery.Deferred(),
			completeDeferred = jQuery.Callbacks("once memory"),
			// Status-dependent callbacks
			statusCode = s.statusCode || {},
			// Headers (they are sent all at once)
			requestHeaders = {},
			requestHeadersNames = {},
			// The jqXHR state
			state = 0,
			// Default abort message
			strAbort = "canceled",
			// Fake xhr
			jqXHR = {
				readyState: 0,

				// Builds headers hashtable if needed
				getResponseHeader: function( key ) {
					var match;
					if ( state === 2 ) {
						if ( !responseHeaders ) {
							responseHeaders = {};
							while ( (match = rheaders.exec( responseHeadersString )) ) {
								responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
							}
						}
						match = responseHeaders[ key.toLowerCase() ];
					}
					return match == null ? null : match;
				},

				// Raw string
				getAllResponseHeaders: function() {
					return state === 2 ? responseHeadersString : null;
				},

				// Caches the header
				setRequestHeader: function( name, value ) {
					var lname = name.toLowerCase();
					if ( !state ) {
						name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
						requestHeaders[ name ] = value;
					}
					return this;
				},

				// Overrides response content-type header
				overrideMimeType: function( type ) {
					if ( !state ) {
						s.mimeType = type;
					}
					return this;
				},

				// Status-dependent callbacks
				statusCode: function( map ) {
					var code;
					if ( map ) {
						if ( state < 2 ) {
							for ( code in map ) {
								// Lazy-add the new callback in a way that preserves old ones
								statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
							}
						} else {
							// Execute the appropriate callbacks
							jqXHR.always( map[ jqXHR.status ] );
						}
					}
					return this;
				},

				// Cancel the request
				abort: function( statusText ) {
					var finalText = statusText || strAbort;
					if ( transport ) {
						transport.abort( finalText );
					}
					done( 0, finalText );
					return this;
				}
			};

		// Attach deferreds
		deferred.promise( jqXHR ).complete = completeDeferred.add;
		jqXHR.success = jqXHR.done;
		jqXHR.error = jqXHR.fail;

		// Remove hash character (#7531: and string promotion)
		// Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
		// Handle falsy url in the settings object (#10093: consistency with old signature)
		// We also use the url parameter if available
		s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );

		// Alias method option to type as per ticket #12004
		s.type = options.method || options.type || s.method || s.type;

		// Extract dataTypes list
		s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ];

		// A cross-domain request is in order when we have a protocol:host:port mismatch
		if ( s.crossDomain == null ) {
			parts = rurl.exec( s.url.toLowerCase() );
			s.crossDomain = !!( parts &&
				( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
					( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !==
						( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) )
			);
		}

		// Convert data if not already a string
		if ( s.data && s.processData && typeof s.data !== "string" ) {
			s.data = jQuery.param( s.data, s.traditional );
		}

		// Apply prefilters
		inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );

		// If request was aborted inside a prefilter, stop there
		if ( state === 2 ) {
			return jqXHR;
		}

		// We can fire global events as of now if asked to
		fireGlobals = s.global;

		// Watch for a new set of requests
		if ( fireGlobals && jQuery.active++ === 0 ) {
			jQuery.event.trigger("ajaxStart");
		}

		// Uppercase the type
		s.type = s.type.toUpperCase();

		// Determine if request has content
		s.hasContent = !rnoContent.test( s.type );

		// Save the URL in case we're toying with the If-Modified-Since
		// and/or If-None-Match header later on
		cacheURL = s.url;

		// More options handling for requests with no content
		if ( !s.hasContent ) {

			// If data is available, append data to url
			if ( s.data ) {
				cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
				// #9682: remove data so that it's not used in an eventual retry
				delete s.data;
			}

			// Add anti-cache in url if needed
			if ( s.cache === false ) {
				s.url = rts.test( cacheURL ) ?

					// If there is already a '_' parameter, set its value
					cacheURL.replace( rts, "$1_=" + nonce++ ) :

					// Otherwise add one to the end
					cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++;
			}
		}

		// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
		if ( s.ifModified ) {
			if ( jQuery.lastModified[ cacheURL ] ) {
				jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
			}
			if ( jQuery.etag[ cacheURL ] ) {
				jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
			}
		}

		// Set the correct header, if data is being sent
		if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
			jqXHR.setRequestHeader( "Content-Type", s.contentType );
		}

		// Set the Accepts header for the server, depending on the dataType
		jqXHR.setRequestHeader(
			"Accept",
			s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
				s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
				s.accepts[ "*" ]
		);

		// Check for headers option
		for ( i in s.headers ) {
			jqXHR.setRequestHeader( i, s.headers[ i ] );
		}

		// Allow custom headers/mimetypes and early abort
		if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
			// Abort if not done already and return
			return jqXHR.abort();
		}

		// aborting is no longer a cancellation
		strAbort = "abort";

		// Install callbacks on deferreds
		for ( i in { success: 1, error: 1, complete: 1 } ) {
			jqXHR[ i ]( s[ i ] );
		}

		// Get transport
		transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );

		// If no transport, we auto-abort
		if ( !transport ) {
			done( -1, "No Transport" );
		} else {
			jqXHR.readyState = 1;

			// Send global event
			if ( fireGlobals ) {
				globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
			}
			// Timeout
			if ( s.async && s.timeout > 0 ) {
				timeoutTimer = setTimeout(function() {
					jqXHR.abort("timeout");
				}, s.timeout );
			}

			try {
				state = 1;
				transport.send( requestHeaders, done );
			} catch ( e ) {
				// Propagate exception as error if not done
				if ( state < 2 ) {
					done( -1, e );
				// Simply rethrow otherwise
				} else {
					throw e;
				}
			}
		}

		// Callback for when everything is done
		function done( status, nativeStatusText, responses, headers ) {
			var isSuccess, success, error, response, modified,
				statusText = nativeStatusText;

			// Called once
			if ( state === 2 ) {
				return;
			}

			// State is "done" now
			state = 2;

			// Clear timeout if it exists
			if ( timeoutTimer ) {
				clearTimeout( timeoutTimer );
			}

			// Dereference transport for early garbage collection
			// (no matter how long the jqXHR object will be used)
			transport = undefined;

			// Cache response headers
			responseHeadersString = headers || "";

			// Set readyState
			jqXHR.readyState = status > 0 ? 4 : 0;

			// Determine if successful
			isSuccess = status >= 200 && status < 300 || status === 304;

			// Get response data
			if ( responses ) {
				response = ajaxHandleResponses( s, jqXHR, responses );
			}

			// Convert no matter what (that way responseXXX fields are always set)
			response = ajaxConvert( s, response, jqXHR, isSuccess );

			// If successful, handle type chaining
			if ( isSuccess ) {

				// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
				if ( s.ifModified ) {
					modified = jqXHR.getResponseHeader("Last-Modified");
					if ( modified ) {
						jQuery.lastModified[ cacheURL ] = modified;
					}
					modified = jqXHR.getResponseHeader("etag");
					if ( modified ) {
						jQuery.etag[ cacheURL ] = modified;
					}
				}

				// if no content
				if ( status === 204 || s.type === "HEAD" ) {
					statusText = "nocontent";

				// if not modified
				} else if ( status === 304 ) {
					statusText = "notmodified";

				// If we have data, let's convert it
				} else {
					statusText = response.state;
					success = response.data;
					error = response.error;
					isSuccess = !error;
				}
			} else {
				// We extract error from statusText
				// then normalize statusText and status for non-aborts
				error = statusText;
				if ( status || !statusText ) {
					statusText = "error";
					if ( status < 0 ) {
						status = 0;
					}
				}
			}

			// Set data for the fake xhr object
			jqXHR.status = status;
			jqXHR.statusText = ( nativeStatusText || statusText ) + "";

			// Success/Error
			if ( isSuccess ) {
				deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
			} else {
				deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
			}

			// Status-dependent callbacks
			jqXHR.statusCode( statusCode );
			statusCode = undefined;

			if ( fireGlobals ) {
				globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
					[ jqXHR, s, isSuccess ? success : error ] );
			}

			// Complete
			completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );

			if ( fireGlobals ) {
				globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
				// Handle the global AJAX counter
				if ( !( --jQuery.active ) ) {
					jQuery.event.trigger("ajaxStop");
				}
			}
		}

		return jqXHR;
	},

	getJSON: function( url, data, callback ) {
		return jQuery.get( url, data, callback, "json" );
	},

	getScript: function( url, callback ) {
		return jQuery.get( url, undefined, callback, "script" );
	}
});

jQuery.each( [ "get", "post" ], function( i, method ) {
	jQuery[ method ] = function( url, data, callback, type ) {
		// shift arguments if data argument was omitted
		if ( jQuery.isFunction( data ) ) {
			type = type || callback;
			callback = data;
			data = undefined;
		}

		return jQuery.ajax({
			url: url,
			type: method,
			dataType: type,
			data: data,
			success: callback
		});
	};
});

// Attach a bunch of functions for handling common AJAX events
jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ) {
	jQuery.fn[ type ] = function( fn ) {
		return this.on( type, fn );
	};
});


jQuery._evalUrl = function( url ) {
	return jQuery.ajax({
		url: url,
		type: "GET",
		dataType: "script",
		async: false,
		global: false,
		"throws": true
	});
};


jQuery.fn.extend({
	wrapAll: function( html ) {
		if ( jQuery.isFunction( html ) ) {
			return this.each(function(i) {
				jQuery(this).wrapAll( html.call(this, i) );
			});
		}

		if ( this[0] ) {
			// The elements to wrap the target around
			var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);

			if ( this[0].parentNode ) {
				wrap.insertBefore( this[0] );
			}

			wrap.map(function() {
				var elem = this;

				while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
					elem = elem.firstChild;
				}

				return elem;
			}).append( this );
		}

		return this;
	},

	wrapInner: function( html ) {
		if ( jQuery.isFunction( html ) ) {
			return this.each(function(i) {
				jQuery(this).wrapInner( html.call(this, i) );
			});
		}

		return this.each(function() {
			var self = jQuery( this ),
				contents = self.contents();

			if ( contents.length ) {
				contents.wrapAll( html );

			} else {
				self.append( html );
			}
		});
	},

	wrap: function( html ) {
		var isFunction = jQuery.isFunction( html );

		return this.each(function(i) {
			jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
		});
	},

	unwrap: function() {
		return this.parent().each(function() {
			if ( !jQuery.nodeName( this, "body" ) ) {
				jQuery( this ).replaceWith( this.childNodes );
			}
		}).end();
	}
});


jQuery.expr.filters.hidden = function( elem ) {
	// Support: Opera <= 12.12
	// Opera reports offsetWidths and offsetHeights less than zero on some elements
	return elem.offsetWidth <= 0 && elem.offsetHeight <= 0 ||
		(!support.reliableHiddenOffsets() &&
			((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");
};

jQuery.expr.filters.visible = function( elem ) {
	return !jQuery.expr.filters.hidden( elem );
};




var r20 = /%20/g,
	rbracket = /\[\]$/,
	rCRLF = /\r?\n/g,
	rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
	rsubmittable = /^(?:input|select|textarea|keygen)/i;

function buildParams( prefix, obj, traditional, add ) {
	var name;

	if ( jQuery.isArray( obj ) ) {
		// Serialize array item.
		jQuery.each( obj, function( i, v ) {
			if ( traditional || rbracket.test( prefix ) ) {
				// Treat each array item as a scalar.
				add( prefix, v );

			} else {
				// Item is non-scalar (array or object), encode its numeric index.
				buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
			}
		});

	} else if ( !traditional && jQuery.type( obj ) === "object" ) {
		// Serialize object item.
		for ( name in obj ) {
			buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
		}

	} else {
		// Serialize scalar item.
		add( prefix, obj );
	}
}

// Serialize an array of form elements or a set of
// key/values into a query string
jQuery.param = function( a, traditional ) {
	var prefix,
		s = [],
		add = function( key, value ) {
			// If value is a function, invoke it and return its value
			value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
			s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
		};

	// Set traditional to true for jQuery <= 1.3.2 behavior.
	if ( traditional === undefined ) {
		traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
	}

	// If an array was passed in, assume that it is an array of form elements.
	if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
		// Serialize the form elements
		jQuery.each( a, function() {
			add( this.name, this.value );
		});

	} else {
		// If traditional, encode the "old" way (the way 1.3.2 or older
		// did it), otherwise encode params recursively.
		for ( prefix in a ) {
			buildParams( prefix, a[ prefix ], traditional, add );
		}
	}

	// Return the resulting serialization
	return s.join( "&" ).replace( r20, "+" );
};

jQuery.fn.extend({
	serialize: function() {
		return jQuery.param( this.serializeArray() );
	},
	serializeArray: function() {
		return this.map(function() {
			// Can add propHook for "elements" to filter or add form elements
			var elements = jQuery.prop( this, "elements" );
			return elements ? jQuery.makeArray( elements ) : this;
		})
		.filter(function() {
			var type = this.type;
			// Use .is(":disabled") so that fieldset[disabled] works
			return this.name && !jQuery( this ).is( ":disabled" ) &&
				rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
				( this.checked || !rcheckableType.test( type ) );
		})
		.map(function( i, elem ) {
			var val = jQuery( this ).val();

			return val == null ?
				null :
				jQuery.isArray( val ) ?
					jQuery.map( val, function( val ) {
						return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
					}) :
					{ name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
		}).get();
	}
});


// Create the request object
// (This is still attached to ajaxSettings for backward compatibility)
jQuery.ajaxSettings.xhr = window.ActiveXObject !== undefined ?
	// Support: IE6+
	function() {

		// XHR cannot access local files, always use ActiveX for that case
		return !this.isLocal &&

			// Support: IE7-8
			// oldIE XHR does not support non-RFC2616 methods (#13240)
			// See http://msdn.microsoft.com/en-us/library/ie/ms536648(v=vs.85).aspx
			// and http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9
			// Although this check for six methods instead of eight
			// since IE also does not support "trace" and "connect"
			/^(get|post|head|put|delete|options)$/i.test( this.type ) &&

			createStandardXHR() || createActiveXHR();
	} :
	// For all other browsers, use the standard XMLHttpRequest object
	createStandardXHR;

var xhrId = 0,
	xhrCallbacks = {},
	xhrSupported = jQuery.ajaxSettings.xhr();

// Support: IE<10
// Open requests must be manually aborted on unload (#5280)
if ( window.ActiveXObject ) {
	jQuery( window ).on( "unload", function() {
		for ( var key in xhrCallbacks ) {
			xhrCallbacks[ key ]( undefined, true );
		}
	});
}

// Determine support properties
support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
xhrSupported = support.ajax = !!xhrSupported;

// Create transport if the browser can provide an xhr
if ( xhrSupported ) {

	jQuery.ajaxTransport(function( options ) {
		// Cross domain only allowed if supported through XMLHttpRequest
		if ( !options.crossDomain || support.cors ) {

			var callback;

			return {
				send: function( headers, complete ) {
					var i,
						xhr = options.xhr(),
						id = ++xhrId;

					// Open the socket
					xhr.open( options.type, options.url, options.async, options.username, options.password );

					// Apply custom fields if provided
					if ( options.xhrFields ) {
						for ( i in options.xhrFields ) {
							xhr[ i ] = options.xhrFields[ i ];
						}
					}

					// Override mime type if needed
					if ( options.mimeType && xhr.overrideMimeType ) {
						xhr.overrideMimeType( options.mimeType );
					}

					// X-Requested-With header
					// For cross-domain requests, seeing as conditions for a preflight are
					// akin to a jigsaw puzzle, we simply never set it to be sure.
					// (it can always be set on a per-request basis or even using ajaxSetup)
					// For same-domain requests, won't change header if already provided.
					if ( !options.crossDomain && !headers["X-Requested-With"] ) {
						headers["X-Requested-With"] = "XMLHttpRequest";
					}

					// Set headers
					for ( i in headers ) {
						// Support: IE<9
						// IE's ActiveXObject throws a 'Type Mismatch' exception when setting
						// request header to a null-value.
						//
						// To keep consistent with other XHR implementations, cast the value
						// to string and ignore `undefined`.
						if ( headers[ i ] !== undefined ) {
							xhr.setRequestHeader( i, headers[ i ] + "" );
						}
					}

					// Do send the request
					// This may raise an exception which is actually
					// handled in jQuery.ajax (so no try/catch here)
					xhr.send( ( options.hasContent && options.data ) || null );

					// Listener
					callback = function( _, isAbort ) {
						var status, statusText, responses;

						// Was never called and is aborted or complete
						if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
							// Clean up
							delete xhrCallbacks[ id ];
							callback = undefined;
							xhr.onreadystatechange = jQuery.noop;

							// Abort manually if needed
							if ( isAbort ) {
								if ( xhr.readyState !== 4 ) {
									xhr.abort();
								}
							} else {
								responses = {};
								status = xhr.status;

								// Support: IE<10
								// Accessing binary-data responseText throws an exception
								// (#11426)
								if ( typeof xhr.responseText === "string" ) {
									responses.text = xhr.responseText;
								}

								// Firefox throws an exception when accessing
								// statusText for faulty cross-domain requests
								try {
									statusText = xhr.statusText;
								} catch( e ) {
									// We normalize with Webkit giving an empty statusText
									statusText = "";
								}

								// Filter status for non standard behaviors

								// If the request is local and we have data: assume a success
								// (success with no data won't get notified, that's the best we
								// can do given current implementations)
								if ( !status && options.isLocal && !options.crossDomain ) {
									status = responses.text ? 200 : 404;
								// IE - #1450: sometimes returns 1223 when it should be 204
								} else if ( status === 1223 ) {
									status = 204;
								}
							}
						}

						// Call complete if needed
						if ( responses ) {
							complete( status, statusText, responses, xhr.getAllResponseHeaders() );
						}
					};

					if ( !options.async ) {
						// if we're in sync mode we fire the callback
						callback();
					} else if ( xhr.readyState === 4 ) {
						// (IE6 & IE7) if it's in cache and has been
						// retrieved directly we need to fire the callback
						setTimeout( callback );
					} else {
						// Add to the list of active xhr callbacks
						xhr.onreadystatechange = xhrCallbacks[ id ] = callback;
					}
				},

				abort: function() {
					if ( callback ) {
						callback( undefined, true );
					}
				}
			};
		}
	});
}

// Functions to create xhrs
function createStandardXHR() {
	try {
		return new window.XMLHttpRequest();
	} catch( e ) {}
}

function createActiveXHR() {
	try {
		return new window.ActiveXObject( "Microsoft.XMLHTTP" );
	} catch( e ) {}
}




// Install script dataType
jQuery.ajaxSetup({
	accepts: {
		script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
	},
	contents: {
		script: /(?:java|ecma)script/
	},
	converters: {
		"text script": function( text ) {
			jQuery.globalEval( text );
			return text;
		}
	}
});

// Handle cache's special case and global
jQuery.ajaxPrefilter( "script", function( s ) {
	if ( s.cache === undefined ) {
		s.cache = false;
	}
	if ( s.crossDomain ) {
		s.type = "GET";
		s.global = false;
	}
});

// Bind script tag hack transport
jQuery.ajaxTransport( "script", function(s) {

	// This transport only deals with cross domain requests
	if ( s.crossDomain ) {

		var script,
			head = document.head || jQuery("head")[0] || document.documentElement;

		return {

			send: function( _, callback ) {

				script = document.createElement("script");

				script.async = true;

				if ( s.scriptCharset ) {
					script.charset = s.scriptCharset;
				}

				script.src = s.url;

				// Attach handlers for all browsers
				script.onload = script.onreadystatechange = function( _, isAbort ) {

					if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {

						// Handle memory leak in IE
						script.onload = script.onreadystatechange = null;

						// Remove the script
						if ( script.parentNode ) {
							script.parentNode.removeChild( script );
						}

						// Dereference the script
						script = null;

						// Callback if not abort
						if ( !isAbort ) {
							callback( 200, "success" );
						}
					}
				};

				// Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending
				// Use native DOM manipulation to avoid our domManip AJAX trickery
				head.insertBefore( script, head.firstChild );
			},

			abort: function() {
				if ( script ) {
					script.onload( undefined, true );
				}
			}
		};
	}
});




var oldCallbacks = [],
	rjsonp = /(=)\?(?=&|$)|\?\?/;

// Default jsonp settings
jQuery.ajaxSetup({
	jsonp: "callback",
	jsonpCallback: function() {
		var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
		this[ callback ] = true;
		return callback;
	}
});

// Detect, normalize options and install callbacks for jsonp requests
jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {

	var callbackName, overwritten, responseContainer,
		jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
			"url" :
			typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data"
		);

	// Handle iff the expected data type is "jsonp" or we have a parameter to set
	if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {

		// Get callback name, remembering preexisting value associated with it
		callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
			s.jsonpCallback() :
			s.jsonpCallback;

		// Insert callback into url or form data
		if ( jsonProp ) {
			s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
		} else if ( s.jsonp !== false ) {
			s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
		}

		// Use data converter to retrieve json after script execution
		s.converters["script json"] = function() {
			if ( !responseContainer ) {
				jQuery.error( callbackName + " was not called" );
			}
			return responseContainer[ 0 ];
		};

		// force json dataType
		s.dataTypes[ 0 ] = "json";

		// Install callback
		overwritten = window[ callbackName ];
		window[ callbackName ] = function() {
			responseContainer = arguments;
		};

		// Clean-up function (fires after converters)
		jqXHR.always(function() {
			// Restore preexisting value
			window[ callbackName ] = overwritten;

			// Save back as free
			if ( s[ callbackName ] ) {
				// make sure that re-using the options doesn't screw things around
				s.jsonpCallback = originalSettings.jsonpCallback;

				// save the callback name for future use
				oldCallbacks.push( callbackName );
			}

			// Call if it was a function and we have a response
			if ( responseContainer && jQuery.isFunction( overwritten ) ) {
				overwritten( responseContainer[ 0 ] );
			}

			responseContainer = overwritten = undefined;
		});

		// Delegate to script
		return "script";
	}
});




// data: string of html
// context (optional): If specified, the fragment will be created in this context, defaults to document
// keepScripts (optional): If true, will include scripts passed in the html string
jQuery.parseHTML = function( data, context, keepScripts ) {
	if ( !data || typeof data !== "string" ) {
		return null;
	}
	if ( typeof context === "boolean" ) {
		keepScripts = context;
		context = false;
	}
	context = context || document;

	var parsed = rsingleTag.exec( data ),
		scripts = !keepScripts && [];

	// Single tag
	if ( parsed ) {
		return [ context.createElement( parsed[1] ) ];
	}

	parsed = jQuery.buildFragment( [ data ], context, scripts );

	if ( scripts && scripts.length ) {
		jQuery( scripts ).remove();
	}

	return jQuery.merge( [], parsed.childNodes );
};


// Keep a copy of the old load method
var _load = jQuery.fn.load;

/**
 * Load a url into a page
 */
jQuery.fn.load = function( url, params, callback ) {
	if ( typeof url !== "string" && _load ) {
		return _load.apply( this, arguments );
	}

	var selector, response, type,
		self = this,
		off = url.indexOf(" ");

	if ( off >= 0 ) {
		selector = jQuery.trim( url.slice( off, url.length ) );
		url = url.slice( 0, off );
	}

	// If it's a function
	if ( jQuery.isFunction( params ) ) {

		// We assume that it's the callback
		callback = params;
		params = undefined;

	// Otherwise, build a param string
	} else if ( params && typeof params === "object" ) {
		type = "POST";
	}

	// If we have elements to modify, make the request
	if ( self.length > 0 ) {
		jQuery.ajax({
			url: url,

			// if "type" variable is undefined, then "GET" method will be used
			type: type,
			dataType: "html",
			data: params
		}).done(function( responseText ) {

			// Save response for use in complete callback
			response = arguments;

			self.html( selector ?

				// If a selector was specified, locate the right elements in a dummy div
				// Exclude scripts to avoid IE 'Permission Denied' errors
				jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) :

				// Otherwise use the full result
				responseText );

		}).complete( callback && function( jqXHR, status ) {
			self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
		});
	}

	return this;
};




jQuery.expr.filters.animated = function( elem ) {
	return jQuery.grep(jQuery.timers, function( fn ) {
		return elem === fn.elem;
	}).length;
};





var docElem = window.document.documentElement;

/**
 * Gets a window from an element
 */
function getWindow( elem ) {
	return jQuery.isWindow( elem ) ?
		elem :
		elem.nodeType === 9 ?
			elem.defaultView || elem.parentWindow :
			false;
}

jQuery.offset = {
	setOffset: function( elem, options, i ) {
		var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
			position = jQuery.css( elem, "position" ),
			curElem = jQuery( elem ),
			props = {};

		// set position first, in-case top/left are set even on static elem
		if ( position === "static" ) {
			elem.style.position = "relative";
		}

		curOffset = curElem.offset();
		curCSSTop = jQuery.css( elem, "top" );
		curCSSLeft = jQuery.css( elem, "left" );
		calculatePosition = ( position === "absolute" || position === "fixed" ) &&
			jQuery.inArray("auto", [ curCSSTop, curCSSLeft ] ) > -1;

		// need to be able to calculate position if either top or left is auto and position is either absolute or fixed
		if ( calculatePosition ) {
			curPosition = curElem.position();
			curTop = curPosition.top;
			curLeft = curPosition.left;
		} else {
			curTop = parseFloat( curCSSTop ) || 0;
			curLeft = parseFloat( curCSSLeft ) || 0;
		}

		if ( jQuery.isFunction( options ) ) {
			options = options.call( elem, i, curOffset );
		}

		if ( options.top != null ) {
			props.top = ( options.top - curOffset.top ) + curTop;
		}
		if ( options.left != null ) {
			props.left = ( options.left - curOffset.left ) + curLeft;
		}

		if ( "using" in options ) {
			options.using.call( elem, props );
		} else {
			curElem.css( props );
		}
	}
};

jQuery.fn.extend({
	offset: function( options ) {
		if ( arguments.length ) {
			return options === undefined ?
				this :
				this.each(function( i ) {
					jQuery.offset.setOffset( this, options, i );
				});
		}

		var docElem, win,
			box = { top: 0, left: 0 },
			elem = this[ 0 ],
			doc = elem && elem.ownerDocument;

		if ( !doc ) {
			return;
		}

		docElem = doc.documentElement;

		// Make sure it's not a disconnected DOM node
		if ( !jQuery.contains( docElem, elem ) ) {
			return box;
		}

		// If we don't have gBCR, just use 0,0 rather than error
		// BlackBerry 5, iOS 3 (original iPhone)
		if ( typeof elem.getBoundingClientRect !== strundefined ) {
			box = elem.getBoundingClientRect();
		}
		win = getWindow( doc );
		return {
			top: box.top  + ( win.pageYOffset || docElem.scrollTop )  - ( docElem.clientTop  || 0 ),
			left: box.left + ( win.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 )
		};
	},

	position: function() {
		if ( !this[ 0 ] ) {
			return;
		}

		var offsetParent, offset,
			parentOffset = { top: 0, left: 0 },
			elem = this[ 0 ];

		// fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is its only offset parent
		if ( jQuery.css( elem, "position" ) === "fixed" ) {
			// we assume that getBoundingClientRect is available when computed position is fixed
			offset = elem.getBoundingClientRect();
		} else {
			// Get *real* offsetParent
			offsetParent = this.offsetParent();

			// Get correct offsets
			offset = this.offset();
			if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
				parentOffset = offsetParent.offset();
			}

			// Add offsetParent borders
			parentOffset.top  += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
			parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
		}

		// Subtract parent offsets and element margins
		// note: when an element has margin: auto the offsetLeft and marginLeft
		// are the same in Safari causing offset.left to incorrectly be 0
		return {
			top:  offset.top  - parentOffset.top - jQuery.css( elem, "marginTop", true ),
			left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true)
		};
	},

	offsetParent: function() {
		return this.map(function() {
			var offsetParent = this.offsetParent || docElem;

			while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position" ) === "static" ) ) {
				offsetParent = offsetParent.offsetParent;
			}
			return offsetParent || docElem;
		});
	}
});

// Create scrollLeft and scrollTop methods
jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
	var top = /Y/.test( prop );

	jQuery.fn[ method ] = function( val ) {
		return access( this, function( elem, method, val ) {
			var win = getWindow( elem );

			if ( val === undefined ) {
				return win ? (prop in win) ? win[ prop ] :
					win.document.documentElement[ method ] :
					elem[ method ];
			}

			if ( win ) {
				win.scrollTo(
					!top ? val : jQuery( win ).scrollLeft(),
					top ? val : jQuery( win ).scrollTop()
				);

			} else {
				elem[ method ] = val;
			}
		}, method, val, arguments.length, null );
	};
});

// Add the top/left cssHooks using jQuery.fn.position
// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
// getComputedStyle returns percent when specified for top/left/bottom/right
// rather than make the css module depend on the offset module, we just check for it here
jQuery.each( [ "top", "left" ], function( i, prop ) {
	jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
		function( elem, computed ) {
			if ( computed ) {
				computed = curCSS( elem, prop );
				// if curCSS returns percentage, fallback to offset
				return rnumnonpx.test( computed ) ?
					jQuery( elem ).position()[ prop ] + "px" :
					computed;
			}
		}
	);
});


// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
	jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
		// margin is only for outerHeight, outerWidth
		jQuery.fn[ funcName ] = function( margin, value ) {
			var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
				extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );

			return access( this, function( elem, type, value ) {
				var doc;

				if ( jQuery.isWindow( elem ) ) {
					// As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
					// isn't a whole lot we can do. See pull request at this URL for discussion:
					// https://github.com/jquery/jquery/pull/764
					return elem.document.documentElement[ "client" + name ];
				}

				// Get document width or height
				if ( elem.nodeType === 9 ) {
					doc = elem.documentElement;

					// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest
					// unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.
					return Math.max(
						elem.body[ "scroll" + name ], doc[ "scroll" + name ],
						elem.body[ "offset" + name ], doc[ "offset" + name ],
						doc[ "client" + name ]
					);
				}

				return value === undefined ?
					// Get width or height on the element, requesting but not forcing parseFloat
					jQuery.css( elem, type, extra ) :

					// Set width or height on the element
					jQuery.style( elem, type, value, extra );
			}, type, chainable ? margin : undefined, chainable, null );
		};
	});
});


// The number of elements contained in the matched element set
jQuery.fn.size = function() {
	return this.length;
};

jQuery.fn.andSelf = jQuery.fn.addBack;




// Register as a named AMD module, since jQuery can be concatenated with other
// files that may use define, but not via a proper concatenation script that
// understands anonymous AMD modules. A named AMD is safest and most robust
// way to register. Lowercase jquery is used because AMD module names are
// derived from file names, and jQuery is normally delivered in a lowercase
// file name. Do this after creating the global so that if an AMD module wants
// to call noConflict to hide this version of jQuery, it will work.

// Note that for maximum portability, libraries that are not jQuery should
// declare themselves as anonymous modules, and avoid setting a global if an
// AMD loader is present. jQuery is a special case. For more information, see
// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon

if ( typeof define === "function" && define.amd ) {
	define( "jquery", [], function() {
		return jQuery;
	});
}




var
	// Map over jQuery in case of overwrite
	_jQuery = window.jQuery,

	// Map over the $ in case of overwrite
	_$ = window.$;

jQuery.noConflict = function( deep ) {
	if ( window.$ === jQuery ) {
		window.$ = _$;
	}

	if ( deep && window.jQuery === jQuery ) {
		window.jQuery = _jQuery;
	}

	return jQuery;
};

// Expose jQuery and $ identifiers, even in
// AMD (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
// and CommonJS for browser emulators (#13566)
if ( typeof noGlobal === strundefined ) {
	window.jQuery = window.$ = jQuery;
}




return jQuery;

}));
};


modules['helpcenter/primary/views/manage/article'] = function(require, exports, module, modules) {
var autosize = require('helpcenter/primary/plugins/autosize');
var redirect = require('helpcenter/primary/views/redirect');
var Dropzone = require('helpcenter/primary/plugins/dropzone');

// Load froala editor
require('helpcenter/primary/plugins/froala')($);
require('helpcenter/primary/plugins/froala/align')($);
require('helpcenter/primary/plugins/froala/char_counter')($);
require('helpcenter/primary/plugins/froala/code_beautifier')($);
require('helpcenter/primary/plugins/froala/code_view')($);
require('helpcenter/primary/plugins/froala/colors')($);
require('helpcenter/primary/plugins/froala/emoticons')($);
require('helpcenter/primary/plugins/froala/font_family')($);
require('helpcenter/primary/plugins/froala/font_size')($);
require('helpcenter/primary/plugins/froala/fullscreen')($);
require('helpcenter/primary/plugins/froala/image')($);
require('helpcenter/primary/plugins/froala/link')($);
require('helpcenter/primary/plugins/froala/lists')($);
require('helpcenter/primary/plugins/froala/paragraph_format')($);
require('helpcenter/primary/plugins/froala/quote')($);
require('helpcenter/primary/plugins/froala/table')($);

// Load tags plugin
require('helpcenter/primary/plugins/tags/core');
require('helpcenter/primary/plugins/tags/autocomplete');
require('helpcenter/primary/plugins/tags');
require('helpcenter/primary/plugins/tags/ajax');

module.exports = Backbone.View.extend({
    model: require('helpcenter/primary/models/manage/content'),

    el: $('#article'),
    initTags: false,
    titles: [],
    descriptions: [],
    fileIds: [],
    unsaved: false,
    isUnsafeHtmlOk: false,

    events: {
        'click #add-article'                     : 'add',
        'click #edit-content'                    : 'edit',
        'click #cancel-edit'                     : 'cancelEdit',
        'change .js-check'                       : 'check',
        'change #input--heading'                 : 'change',
        'click #prompt'                          : 'prompt',
        'click #cancel-delete'                   : 'cancelDelete',
        'click #cancel-unsafe-html-prompt'       : 'cancelUnsafeHtmlPrompt',
        'click #save'                            : 'update',
        'click #delete'                          : 'delete',
        'click #save-unsafe-html'                : 'saveUnsafeHtml',
        'click #update-unsafe-html'              : 'updateUnsafeHtml',
        'click .js-drill-selected, .drill .icon' : 'toggleDrilldown',
        'click .js-dd-set'                       : 'set',
        'click .js-delete-attachment'            : 'removeAttachment',
        'click .js-locale-selector'              : 'toggleLocaleMenu',
        'click .locale__item'                    : 'switchLocale'
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Gets options from router while instantiating view
     * @param  {Object}
     */
    initialize: function(options) {
        var view = this;
        this.options = options || {};
        this.itemId = this.options.itemId.split('-')[0];
        this.lastUpdated = null;

        // Initiaze froala editor
        $('#input--description').froalaEditor({
            key: 'GIBEVFBOHF1c1UNYVM==',
            htmlRemoveTags: ['style'],
            linkInsertButtons: [],
            linkEditButtons: ['linkOpen', 'linkEdit', 'linkRemove'],
            requestHeaders: { 'X-CSRF-Token': $('#csrftoken').val() },
            imageEditButtons: ['imageReplace', 'imageAlign', 'imageRemove', '|', 'imageLink', 'linkOpen', 'linkEdit', 'linkRemove', '-', 'imageDisplay', 'imageAlt', 'imageSize'],
            tableEditButtons: ['tableHeader', 'tableRemove', '|', 'tableRows', 'tableColumns', 'tableStyle', '-', 'tableCells', 'tableCellBackground', 'tableCellVerticalAlign', 'tableCellHorizontalAlign'],
            tableStyles: {
              'fr-alternate-rows': 'Alternate Rows'
            },
            toolbarButtons: ['paragraphFormat', '|', 'fontSize', 'bold', 'italic', 'underline', 'strikeThrough', 'subscript', 'superscript', 'color', 'emoticons', '|', 'align', '|', 'formatOL', 'formatUL', 'outdent', 'indent', 'quote', '|', 'insertLink', 'insertImage', 'insertTable', 'insertHR', '|', 'undo', 'redo', 'clearFormatting', 'html', 'fullscreen'],
            toolbarButtonsMD: ['paragraphFormat', '|', 'fontSize', 'bold', 'italic', 'underline', 'strikeThrough', 'subscript', 'superscript', 'color', 'emoticons', '|', 'align', '|', 'formatOL', 'formatUL', 'outdent', 'indent', 'quote', '|', 'insertLink', 'insertImage', 'insertTable', 'insertHR', '|', 'undo', 'redo', 'clearFormatting', 'html', 'fullscreen'],
            toolbarStickyOffset: 50,
            imageUploadURL: _Payload.portalpath + 'api/v1/media.json?include=*',
            imageUploadParam: 'files',
            imageDefaultWidth: 0
        });
        // Detect any content change in editor
        $('#input--description').on('froalaEditor.contentChanged', function () {
            view.change();
        });

        // Initiate drilldown
        $('.drilldown').drilldown();
        $(document).click(function() {
            $('.drilldown').removeClass('dd--open');
            $('.drill-default').removeClass('drill-focus');
            $('.js-locale-list').removeClass('is-open');
        });

        // Initiate textarea auto resizer
        autosize($('.textfield--heading'));

        // Initialize tags editor
        if (!$('.js-tags').length) {
            $('#artcle-tags').textext({
                plugins: 'tags autocomplete ajax',
                ajax: {
                    url: _Payload.portalpath + 'api/v1/autocomplete/tags.json?include=*',
                    dataType: 'json'
                }
            });
        }

        // Initialize dropzone plugin
        if ($('#files-input').length) {
            new Dropzone("#files-input", {
                url: _Payload.portalpath + 'api/v1/files?include=*',
                acceptedFiles: null,
                paramName: "files",
                uploadMultiple: true,
                parallelUploads: 1,
                createImageThumbnails: false,
                dictFileTooBig: "File is too big ({{filesize}}MB).",
                addRemoveLinks: true,
                previewsContainer: "#dz-preview-container",
                previewTemplate: "<div class=\"dz-preview dz-preview--large dz-file-preview\">\n  <div class=\"dz-image\"><img data-dz-thumbnail /></div>\n  <div class=\"dz-details\">\n<div class=\"dz-filename\"><span data-dz-name></span></div>\n  </div>\n  <div class=\"dz-progress\"><span class=\"dz-upload\" data-dz-uploadprogress></span></div>\n  <div class=\"dz-error-message\"><span data-dz-errormessage></span></div>\n  <div class=\"dz-success-mark\"></div>\n  <div class=\"dz-error-mark\"></div>\n</div>",
                sending: function() {
                    $('#save').prop('disabled', true);
                },
                success: function(file) {
                    $('#save').prop('disabled', false);
                    var fileId = JSON.parse(file.xhr.response).data.id;
                    view.fileIds.push(fileId);
                    $(file.previewElement).attr('data-id', fileId);
                    return file.previewElement.classList.add("dz-success");
                },
                removedfile: function(file) {
                  var _ref;
                  if (file.previewElement) {
                    if ((_ref = file.previewElement) != null) {
                      // Remove item from file ids array
                      var removeItem = $(file.previewElement).data('id');
                      view.fileIds = $.grep(view.fileIds, function(value) {
                        return value != removeItem;
                      });
                      // Remove item
                      _ref.parentNode.removeChild(file.previewElement);
                    }
                  }
                  return this._updateMaxFilesReachedClass();
                }
            });
        }

        // Get content for other locales
        if (!$('#add-article').length) {
            new this.model({
                'itemId': this.itemId,
                'item': 'articles',
                'query': 'update'
            }).fetch({
                success: function(response) {
                    $.each(response.attributes.data.titles, function(index, val) {
                         view.titles.push({
                                 "locale": val.locale,
                                 "translation": val.translation
                             });
                    });
                    $.each(response.attributes.data.contents, function(index, val) {
                         view.descriptions.push({
                                 "locale": val.locale,
                                 "translation": val.translation
                             });
                    });
                    $('#edit-content').removeClass('is-disabled');
                },
                error: function() {
                    // Show error message
                    $('#save-bar').addClass('admin-bar--error');
                }
            });
        }
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Checks if title and content of main locale are filled
     * @return {Boolean}
     */
    validate: function() {
        var view = this;
        var hasTitle, hasContent = false;

        // Check if primary locale title is filled
        if (view.titles.length) {
            $.each(view.titles, function(index, el) {
                if (el.locale == _Payload.defaultlocale && el.translation.length) {
                    hasTitle = true;
                }
            });
        }

        // Check if primary locale content is filled
        if (view.descriptions.length) {
            $.each(view.descriptions, function(index, el) {
                if (el.locale == _Payload.defaultlocale && el.translation.length) {
                    hasContent = true;
                }
            });
        }

        if (hasTitle && hasContent) {
            $('#add-article').prop('disabled', 'true').addClass('button--loading');
            return true;
        } else {
            // Show error message
            $('#save-bar').addClass('admin-bar--required');
            return false;
        }
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Loads the editing view
     */
    edit: function() {
    	var view = this;

        // Store last updated timestamp
        this.lastUpdated = $('#last-updated').find('span').data('time');

        // Show moderate option in the sidebar
        if ($('#moderate').length) {
            $('#moderate').show();
        } else {
            var moderateTpl = _.template($('#tpl-moderate').html());
            $('#aside--article').prepend(moderateTpl);
        }

        // Remove grey bar
        $('#edit-bar').remove();

        // Show blue bar
        var saveBarTpl = _.template($('#tpl-save-bar').html());
        $('#article').prepend(saveBarTpl({ referrer: 'article' }));

        // Show input fields in place of static content
        $('#input--heading, #input--description-container, #tags-editor, #files-input').removeClass('u-hidden');
        $('#display--heading, #display--description, #related-articles, .js-tags').addClass('u-hidden');

        // Show delete attachment link
        $('.js-delete-attachment').removeClass('u-hiddenvisually');

        // Disable uneditable content
        $('#comments-meta').addClass('u-hiddenvisually');

        // Initiate drilldown
        $('.drilldown').drilldown();

        // Initialize tags editor
        if (!view.initTags) {
            var tagItems = [];
            $('.js-tags').children().each(function() {
                tagItems.push($(this).text());
            });
            $('#artcle-tags').textext({
                plugins: 'tags autocomplete ajax',
                ajax: {
                    url: _Payload.portalpath + 'api/v1/autocomplete/tags.json?include=*',
                    dataType: 'json'
                },
                tagsItems: tagItems
            });
            view.initTags = true;
        }

        // Fill data into the editor
        var activeTitle = '',
        activeDescription = '';
        $.each(view.titles, function(index, val) {
             if (val.locale == _Payload.activelocale) {
                activeTitle = val.translation;
             }
        });
        $.each(view.descriptions, function(index, val) {
             if (val.locale == _Payload.activelocale) {
                activeDescription = val.translation;
             }
        });
        $('#input--heading').val(activeTitle);
        $('#input--description').froalaEditor('html.set', activeDescription);

    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Reverts the editing view
     */
    cancelEdit: function() {
        this.unsaved = false;

        // Remove blue bar and moderate options
        $('#save-bar').remove();
        $('#moderate').hide();

        // Show grey bar
        var editBarTpl = _.template($('#tpl-edit-bar').html());
        $('#article').prepend(editBarTpl({ referrer: 'article', lastUpdated: this.lastUpdated }));
        $(".js-timeago").timeago();

        // Show static data inplace of input fields
        $('#input--heading, #input--description-container, #tags-editor, #files-input').addClass('u-hidden');
        $('#display--heading, #display--description, #related-articles, .js-tags').removeClass('u-hidden');

        // Hide delete attachment link
        $('.js-delete-attachment').addClass('u-hiddenvisually');

        // Remove disabled class from uneditable content
        $('#comments-meta').removeClass('u-hiddenvisually');
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Sets value on checkbox to true/false
     * @param  {Object} e
     */
    check: function(e) {
        var elem = $(e.target);
        if (elem.is(':checked')) {
            elem.val('true');
        } else {
            elem.val('false');
        }
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Pushes current locale data to titles[] and desctiptions[]
     */
    pushData: function() {
        var view = this;
        var titleInArray, titleInArrayIndex, descInArray, descInArrayIndex;
        var activeLocale = $('.js-locale__item.is-active').clone().children().remove().end().text();
        $('#save-bar').removeClass('admin-bar--required');

        // Check if title is already there in array
        $.each(view.titles, function(index, value) {
            if (value.locale == activeLocale) {
                titleInArray = true;
                titleInArrayIndex = index;
            }
        });

        // Remove title from array if already present
       if (titleInArray) {
            view.titles.splice(titleInArrayIndex, 1);
       }

       // Check if description is already there in array
        $.each(view.descriptions, function(index, value) {
            if (value.locale == activeLocale) {
                descInArray = true;
                descInArrayIndex = index;
            }
        });

        // Remove description from array if already present
       if (descInArray) {
            view.descriptions.splice(descInArrayIndex, 1);
       }

        view.titles.push({
                "locale": activeLocale,
                "translation": $('#input--heading').val().trim()
            });

        // Set article content value
        var contentTextarea = $('#input--description');
        var textareaValue = contentTextarea.froalaEditor('codeView.isActive') ? contentTextarea.froalaEditor('codeView.get') : contentTextarea.froalaEditor('html.get');

        view.descriptions.push({
                "locale": activeLocale,
                "translation": textareaValue
            });
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Show alert if there are any unsaved changes
     */
    change: function() {
      var view = this;
      view.unsaved = true;
      window.onbeforeunload = function () {
          if (view.unsaved) {
              return 'You have some unsaved edits.';
          }
      };
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Creates new article
     */
    add: function() {
        var view = this;

        // Bypass unsaved alert if form is submitted
        view.unsaved = false;

        // Push current locale data
        view.pushData();

        // Send to server if valid
        if (view.validate()) {
            new this.model({
                'item': 'articles',
                'query': 'fetch'
            }).save({
                'titles'              : view.titles,
                'contents'            : view.descriptions,
                'section_id'          : $('#article-parent').val(),
                'status'              : $('#article-status').val(),
                'author_id'           : $('#author-id').val(),
                'is_featured'         : $('#article-featured').is(':checked') ? true : false,
                'allow_comments'      : $('#article-comments').is(':checked') ? true : false,
                'keywords'            : $('#keywords').val(),
                'attachment_file_ids' : view.fileIds.join(),
                'tags'                : $('#tags-array').val(),
                'proceed_anyway'      : view.isUnsafeHtmlOk
            } , {
                success: function(response) {
                    // Redirect to acticle page
                    $.each(response.attributes.data.slugs, function(index, el) {
                        if (el.locale == _Payload.activelocale) {
                            window.location.replace(_Payload.url + 'article/' + el.translation);
                        } else if (el.locale == _Payload.defaultlocale) {
                            window.location.replace(_Payload.url + 'article/' + el.translation);
                        }
                    });

                },
                error: function(model, xhr) {
                    view.notSaved(xhr.status, xhr.responseJSON.errors[0], xhr.responseJSON.notifications);
                }
            });
        }
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Updates the article in database
     */
    update: function() {
        var view = this;

        // Bypass unsaved alert if form is submitted
        view.unsaved = false;

        // Push current locale data
        view.pushData();

        // Send to server if valid
        if (view.validate()) {
            // Disable buttons and show loading animation
            $('#cancel-edit').addClass('is-disabled');
            $('#save')
                .addClass('is-disabled button--loading')
                .prop('disabled', 'true')
                .after(_.template($('#tpl-loader').html()));


            new this.model({
                'itemId': this.itemId,
                'item': 'articles',
                'query': 'update'
            }).save({
                'titles'              : view.titles,
                'contents'            : view.descriptions,
                'section_id'          : $('#article-parent').val(),
                'status'              : $('#article-status').val(),
                'author_id'           : $('#author-id').val(),
                'is_featured'         : $('#article-featured').is(':checked') ? true : false,
                'allow_comments'      : $('#article-comments').is(':checked') ? true : false,
                'keywords'            : $('#keywords').val(),
                'attachment_file_ids' : view.fileIds.join(),
                'tags'                : $('#tags-array').val(),
                'proceed_anyway'      : view.isUnsafeHtmlOk
            }, {
                success: function() {
                    // Reload the page
                    location.reload();
                },
                error: function(model, xhr) {
                    view.notSaved(xhr.status, xhr.responseJSON.errors[0], xhr.responseJSON.notifications);
                }
            });
        }
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Shows error if article fails to save
     */
    notSaved: function(errorCode, errorMsg, notifications) {
        var view = this;

        // Redirect if required
        if (errorCode == 404 || errorCode == 401) {
            redirect('article', view.options.itemId, errorCode);
        } else if (errorMsg.parameter === 'unsafe_contents') {
            var unsafeHtmlTags = {};
            if (notifications.length > 0 && notifications[0].message) {
                var unsafeHtmlTags = JSON.parse(notifications[0].message);
            }
            this.unsafeHtmlPrompt(Object.keys(unsafeHtmlTags));
        } else {
            // Show error message
            $('#save-bar').addClass('admin-bar--api-error');
            $('.admin-api-error').remove();

            var errorParameter = '';
            if(errorMsg.parameter) {
                errorParameter = '<span class="u-textbold u-capitalize">' + errorMsg.parameter + '</span>: ';
            }

            $('#save-bar__text').append('<span class="admin-api-error u-textwarning">' + errorParameter + '' + errorMsg.message + '.</span>');

            // Re-enable buttons and show loading animation
            $('#cancel-edit').removeClass('is-disabled');
            $('#save, #add-article').removeClass('is-disabled button--loading').removeAttr('disabled');
        }
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Shows the modal
     */
    prompt: function() {
        var view = this;
        var modalTpl = _.template($('#tpl-delete-modal').html());
        $('#article').append(modalTpl);
        $('#delete-modal').fadeIn(300);
        $(document).keyup(function(e) {
            if (e.keyCode == 27) {
                view.cancelDelete();
            }
        });
    },

    /**
     * @author Tushar Arora <tushar.arora@kayako.com>
     * @description Shows the unsafe html modal
     */
    unsafeHtmlPrompt: function(unsafeHtmlTags) {
        var view = this;
        var modalTpl = _.template($('#tpl-unsafe-html-modal').html());
        $('#article').append(modalTpl({ unsafeHtmlTags: unsafeHtmlTags} ));
        $('#unsafe-html-modal').fadeIn(300);
        $(document).keyup(function(e) {
            if (e.keyCode == 27) {
                view.cancelUnsafeHtmlPrompt();
                $('#cancel-edit').removeClass('is-disabled');
                $('#save, #add-article').removeClass('is-disabled button--loading').removeAttr('disabled');
            }
        });
    },

    /**
     * @author Tushar Arora <tushar.arora@kayako.com>
     * @description Save article description with unsafe html tags
     */
    saveUnsafeHtml: function (source) {
        $('.button-save-unsafe')
            .addClass('is-disabled button--loading')
            .prop('disabled', 'true')
            .after(_.template($('#tpl-loader').html()));
        this.isUnsafeHtmlOk = true;
        if (source && source === 'update') {
            this.update();
            return;
        }
        this.add();
    },

    /**
     * @author Tushar Arora <tushar.arora@kayako.com>
     * @description Calls saveUnsafeHtml with source as update
     */
    updateUnsafeHtml: function () {
        this.saveUnsafeHtml('update')
    },

    /**
     * @author Tushar Arora <tushar.arora@kayako.com>
     * @description Hides the unsafe html modal
     */
    cancelUnsafeHtmlPrompt: function() {
        $('#unsafe-html-modal').fadeOut(200, function() {
            this.isUnsafeHtmlOk = false;
            $(this).remove();
            $('#cancel-edit').removeClass('is-disabled');
            $('#save, #add-article').removeClass('is-disabled button--loading').removeAttr('disabled');
        });
    },


    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Hides the modal
     */
    cancelDelete: function() {
        $('#delete-modal').fadeOut(200, function() {
            $(this).remove();
        });
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Deletes the article from database
     */
    delete: function() {
        // Disable buttons and show loading animation
        $('#cancel-delete').addClass('is-disabled');
        $('#delete')
            .addClass('is-disabled button--loading')
            .prop('disabled', 'true')
            .after(_.template($('#tpl-loader').html()));

        var itemId = this.itemId;
        var view = this;

        new view.model({
            'itemId': itemId,
            'item': 'articles',
            'query': 'update'
        }).fetch({
            success: function(response) {
                var sectionSlug;
                $.each(response.attributes.data.section.slugs, function(index, el) {
                    if (el.locale == _Payload.activelocale) {
                        sectionSlug = el.translation;
                    } else if (el.locale == _Payload.defaultlocale) {
                        sectionSlug = el.translation;
                    }
                });
                // Delete the article from database
                new view.model({
                    'itemId': itemId,
                    'item': 'articles',
                    'query': 'update'
                }).destroy({
                    success: function() {
                        // Redirect to parent category
                        window.location.href = _Payload.url + 'section/' + sectionSlug;
                    },
                    error: function() {
                        view.modalError();
                    }
                });
            },
            error: function() {
                view.modalError();
            }
        });
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Show error message inside modal
     */
    modalError: function() {
        // Show error message
        $('.modal__footer').addClass('modal__footer--error');

        // Re-enable buttons and hide loading animation
        $('#cancel-delete').removeClass('is-disabled');
        $('#delete')
            .removeClass('is-disabled button--loading')
            .removeAttr('disabled');
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Show/hide drilldown menu options
     * @param {Object} e
     */
    toggleDrilldown: function(e) {
        e.stopPropagation();
        $(e.target).parent('.drill-default')
            .toggleClass('drill-focus')
            .next('.drilldown').toggleClass('dd--open');
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Sets the selected drilldown option to input
     * @param {Object} e
     */
    set: function(e) {
        var elem = $(e.target);
        var root = elem.parents('.drill-wrap ');
        root.find('.js-drill-selected').text(elem.text());
        root.find('#article-parent').val(elem.data('id'));
        root.find('.drilldown').removeClass('dd--open');
        root.find('.drill-default').removeClass('drill-focus');
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Deletes artcle attachment from server
     * @param  {Object} e
     */
    removeAttachment: function(e) {
        var elem = $(e.target);
        elem.addClass('is-disabled');
        var attachment = require('helpcenter/primary/models/manage/attachment');
        var articleId = this.itemId;
        new attachment({
            'articleId': articleId,
            'itemId': elem.data('id')
        }).destroy({
            success: function() {
                elem.closest('.media').fadeOut(300, function() {
                    $(this).remove();
                    if ($('[data-ui-component="attachment"]').length === 1) {
                        $('#down-all').remove();
                    }
                });
            },
            error: function() {
                // @todo: manage later
                elem.removeClass('is-disabled');
            }
        });
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Show/hide locale dropdown menu
     */
    toggleLocaleMenu: function(e) {
        e.stopPropagation();
        $('.js-locale-list').toggleClass('is-open');
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Switches among active locales (Changes data of input fields)
     * @param  {Object} e
     */
    switchLocale: function(e) {
    	var view = this;
        var titleInArray, titleInArrayIndex, descInArray, descInArrayIndex;
        var selectedTitle = '';
        var selectedDesc = '';
        var selected = $(e.target).closest('.js-locale__item');

        if (!selected.hasClass('is-active')) {
            var activeLocale = $('.js-locale__item.is-active').clone().children().remove().end().text();
            var activeTitle = $('#input--heading').val();
            var activeDesc = $('#input--description').froalaEditor('html.get').replace('--', '—');
            var selectedLocale = selected.clone().children().remove().end().text();

            // Check if title is already there in array
            $.each(view.titles, function(index, value) {
            	if (value.locale == activeLocale) {
            		titleInArray = true;
            		titleInArrayIndex = index;
            	}
            	if (value.locale == selectedLocale) {
            		selectedTitle = value.translation;
            	}
            });

            // Remove title from array if already present
           if (titleInArray) {
           		view.titles.splice(titleInArrayIndex, 1);
           }

           // Check if description is already there in array
			$.each(view.descriptions, function(index, value) {
				if (value.locale == activeLocale) {
					descInArray = true;
					descInArrayIndex = index;
				}
				if (value.locale == selectedLocale) {
					selectedDesc = value.translation;
				}
			});

            // Remove description from array if already present
           if (descInArray) {
           		view.descriptions.splice(descInArrayIndex, 1);
           }

           // Fill input fields with selected locale
           $('#input--heading').val(selectedTitle);
           $('#input--description').froalaEditor('html.set', selectedDesc);

            // Push currrent locale data into array
        	view.titles.push({
        	        "locale": activeLocale,
        	        "translation": activeTitle
        	    });
        	view.descriptions.push({
        	        "locale": activeLocale,
        	        "translation": activeDesc
        	    });

            // Tick the selected locale
            $(e.target).closest('.js-locale__item').addClass('is-active').siblings('.js-locale__item').removeClass('is-active');
            if (selectedLocale != _Payload.defaultlocale) {
            	$('#locale-icon').removeClass('icon-locale').addClass('icon-locale-alert');
            	$('#active-locale').text(selectedLocale);
            } else {
            	$('#locale-icon').removeClass('icon-locale-alert').addClass('icon-locale');
            	$('#active-locale').empty();
            }

        }
    }
});
};


modules['helpcenter/primary/views/account'] = function(require, exports, module, modules) {
/**
 * @fileoverview Sets password for new users
 */

module.exports = Backbone.View.extend({
  el: $('#password-container'),

  events: {
    'submit #setpassword, #resetpassword': 'setpassword'
  },

	/**
	 * @author Bishwanath Jha <bishwanath.jha@kayako.com>
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @param {Object} e
	 **/
  setpassword: function(e) {
    e.preventDefault();

    var hash = $('#auth_token').val();
    var password = $('#new-password').val();

		// Redirect to login page if no token found
    if (!hash) {
      window.location.href = _Payload.url + 'login';
    }

		if (password) {
			// Disable submit button
      $('#setpassword-submit')
        .prop('disabled', true)
        .addClass('button--loading');

      $.ajax({
        url: _Payload.portalpath + 'api/v1/base/profile/password.json?include=*',
        type: 'PUT',
        data: {
          hash: hash,
          new_password: password
        },
        success: function(response) {
          window.location.href = _Payload.url + 'account/success/password';
        },
        error: function(response) {
          // If we have received 401 which means AUTHENTICATION_FAILED, token is invalid or expired
          // Redirect user to login page
          if (response.status === 401 && response.responseJSON.errors[0].code == 'AUTHENTICATION_FAILED') {
            window.location.href = _Payload.url + 'login';
          } else {
            // Highlight error fields
            $('#password').closest('.textfield__label').addClass('textfield__label--error');

            // Show notification
            if (response.responseJSON.notifications) {
							// Re-enaible submit button
              $('#setpassword-submit')
                .prop('disabled', false)
                .removeClass('button--loading');

							// Remove any existing notification
              $('.alert-box').remove();

							// Show error notification
							var tpl_error = _.template($('#tpl-alert').html());
              var tpl_html = tpl_error({ notifications: response.responseJSON.notifications });
              $('#setpassword-title').after(tpl_html);
            }
          }
        }
      });
    }
  }
});
};


modules['helpcenter/primary/views/redirect'] = function(require, exports, module, modules) {
function error(item, itemId, errorCode) {
	if (errorCode === 404) {
	    window.location.href = _Payload.url + '410';
	} else if (errorCode === 401) {
	    window.location.href = _Payload.url + 'login?return=' + '/' + item + '/' + itemId;
	}
}
module.exports = error;
};


modules['helpcenter/primary/models/customize/templates'] = function(require, exports, module, modules) {
module.exports = Backbone.Model.extend({
    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @param  {Object}
     */
    initialize: function(options) {
        this.query = options.query;
        this.brandId = options.brandId;
        if (options.item) {
            this.idAttribute = 'item';
            this.item = options.item;
        }
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @return {Obejct}
     */
    url: function() {
        switch (this.query) {
            // Fetches data of all templates
            case 'allTpl':
                return _Payload.portalpath + 'api/v1/brands/' + this.brandId + '/templates?limit=20&include=*';
            // Fetches or updates single template
            case 'singleTpl':
                return _Payload.portalpath + 'api/v1/brands/' + this.brandId + '/templates/' + this.item + '?include=*';
            // Resets a template
            case 'resetTpl':
                return _Payload.portalpath + 'api/v1/brands/' + this.brandId + '/templates/' + this.item + '/restore?include=*';
        }
    }
});

};


modules['helpcenter/primary/views/customize'] = function(require, exports, module, modules) {
// Load plugins
var Dropzone = require('helpcenter/primary/plugins/dropzone');
require('helpcenter/primary/plugins/minicolors');
require('helpcenter/primary/plugins/ace');

// Load models
var optionsModel = require('helpcenter/primary/models/customize/options');
var templatesModel = require('helpcenter/primary/models/customize/templates');

module.exports = Backbone.View.extend({
    el: $('#config-view'),

    templateName   : null,
    preSelect      : null,
    detectChange   : false,
    changedOptions : [],
    helpcenterId   : _Payload.helpcenter_id,
    brandId        : null,

    events: {
        'click #show-config'          : 'openPanel',
        'click .js-config-toggle'     : 'toggleOptions',
        'click .js-close-panel'       : 'closePanel',
        'click .js-show-editor'       : 'toggleEditor',
        'click .js-close-editor'      : 'toggleEditor',
        'click .js-toggle-tpls'       : 'tplMenu',
        'click .js-config-panel'      : 'closeTplMenu',
        'click .js-tpl-selector'      : 'selectTpl',
        'click .js-tab-item'          : 'toggleTabs',
        'click #save-config'          : 'saveOptions',
        'click #prompt-reset-options' : 'promptReset',
        'click #prompt-reset-tpl'     : 'promptReset',
        'click #cancel-revert'        : 'cancelReset',
        'click #reset-options'        : 'resetOptions',
        'click #reset-tpl'            : 'resetTpl',
        'click #save-tpl'             : 'saveTpl',
        'keyup .cu-textfield'         : 'showSaveBtn',
        'change #toggle-messenger'    : 'toggleMessenger',
        'change .js-customize-setting': 'settingsChanged'
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Opens customization panel on the left
     *              and initializes required plugins
     */
    openPanel: function() {
        var view = this;
        $('.body, #show-config').addClass('config-panel-open');
        $('#customize__primary_color').minicolors();

        // Get font and color get
        view.fetchOptions();
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Fetches font and color value
     */
    fetchOptions: function() {
        var view = this;
        new optionsModel({
        'api': 'helpcenters/' + view.helpcenterId
        }).fetch({
            success: function(response) {
                var data = response.attributes.data;
                var color = data.primary_color ? data.primary_color : '#4eafcb';
                var font = data.font ? data.font : 'Source Sans Pro';
                $('#customize__title').val(data.title);
                $('#customize__primary_color').minicolors('value', color);
                $('#customize__font').val(font);
                view.changedOptions = [];
                view.detectChange = true;
                view.brandId = data.brand.id;

                // Initialize dropzone plugin
                view.dropzone(view.brandId);
                
                // Disable reset button if options are default
                if (data.primary_color == null && data.font == null) {
                    $('#prompt-reset-options').addClass('is-disabled');
                }
                // List all templates
                if ($('#config__templates').is(':empty')) {
                    view.listTemplates();
                }
            },
            error: function(model, xhr) {
                view.notSaved(xhr.status);
            }
        });
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Initializes the dropzone plugin
     * @param  {Number} brandId
     */
    dropzone: function (brandId) {
        var view = this;
        // Initialize dropzone for logo
        if (typeof logoZone === 'undefined') {
            logoZone = new Dropzone("#logo-input", {
                url: _Payload.portalpath + 'api/v1/logo?include=*',
                method: 'put',
                acceptedFiles: 'image/*',
                paramName: "image",
                maxFilesize: 2,
                dictFileTooBig: "File is too big ({{filesize}}MiB).",
                createImageThumbnails: false,
                autoProcessQueue: false,
                addRemoveLinks: true,
                init: function() {
                    this.on("addedfile", function() {
                        view.showSaveBtn();
                        if (this.files[1] != null) {
                            this.removeFile(this.files[0]);
                        }
                    });
                },
                sending: function(file, xhr, formData) {
                    formData.append("brand_id", brandId);
                },
                previewTemplate: "<div class=\"dz-preview dz-file-preview\">\n  <div class=\"dz-details\">\n   <div class=\"dz-filename\"><span data-dz-name></span></div>\n  </div>\n  <div class=\"dz-error-message\"><span data-dz-errormessage></span></div>\n</div>",
                success: function(file) {
                    view.notification('success');
                    location.reload();
                }
            });
        }

        // Initialize dropzone for favicon
        if (typeof faviconZone === 'undefined') {
            faviconZone = new Dropzone("#favicon-input", {
                url: _Payload.portalpath + 'api/v1/favicon?include=*',
                method: 'put',
                acceptedFiles: 'image/png',
                paramName: "image",
                maxFilesize: 2,
                dictFileTooBig: "File is too big ({{filesize}}MiB).",
                createImageThumbnails: false,
                autoProcessQueue: false,
                addRemoveLinks: true,
                init: function() {
                    this.on("addedfile", function() {
                        view.showSaveBtn();
                        if (this.files[1] != null) {
                            this.removeFile(this.files[0]);
                        }
                    });
                },
                sending: function(file, xhr, formData) {
                    formData.append("brand_id", brandId);
                },
                previewTemplate: "<div class=\"dz-preview dz-file-preview\">\n  <div class=\"dz-details\">\n   <div class=\"dz-filename\"><span data-dz-name></span></div>\n  </div>\n  <div class=\"dz-error-message\"><span data-dz-errormessage></span></div>\n</div>",
                success: function(file) {
                    view.notification('success');
                    location.reload();
                }
            });
        }
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Indentifies the current page
     * @param  {Object} pageData
     */
    currentPage: function(pathArray) {
        switch (pathArray[1]) {
            case '':
            this.preSelect = 'helpcenter_home';
            break;
            case 'article':
            this.preSelect = 'helpcenter_article';
            break;
            case 'section':
            this.preSelect = 'helpcenter_section';
            break;
            case 'category':
            this.preSelect = 'helpcenter_category';
            break;
            case 'conversation':
            pathArray[2] == 'new' ? this.preSelect = 'helpcenter_new_conversation' : this.preSelect = 'helpcenter_view_conversation';
            break;
            case 'conversations':
            this.preSelect = 'helpcenter_my_conversations';
            break;
            case 'search':
            this.preSelect = 'helpcenter_search';
            break;
            case 'profile':
            this.preSelect = 'helpcenter_profile';
            break;
        }
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Generates template list in dropdown and
     *              loads first template's content
     */
    listTemplates: function() {
        var view = this;
        var path = window.location.pathname;
        pathArray = path.split('/');
        view.currentPage(pathArray);

        // Initialize code editor
        el = document.getElementById("editor");
        editor = ace.edit("editor");
        ace.config.set('basePath', '/__apps/helpcenter/assets/primary/javascript/plugins/ace');
        editor.setTheme("ace/theme/monokai");
        editor.session.setMode("ace/mode/html");
        editor.getSession().setTabSize(2);
        editor.renderer.setScrollMargin(5, 5);
        editor.getSession().setUseWorker(false);
        editor.setOption("showPrintMargin", false);
        editor.textInput.getElement().tabIndex = -1;
        editor.$blockScrolling = Infinity;
        // Fix ace editor width
        setTimeout(function() {
            $('#editor').removeClass('ace_editor-size');
        }, 500);

        // Show loading animation
        $('.js-loading').addClass('is-visible');

        // Load main template content
        new templatesModel({
            'query': 'allTpl',
            'brandId': view.brandId
        }).fetch({
            success: function(response) {
                var defaultTpl, homeTpl;
                var list = $('#config__templates');
                _.each(response.attributes.data, function(el, index) {
                    if (el.name !== 'helpcenter_customcss' &&
                        el.name !== 'base_email_notification' &&
                        el.name !== 'cases_email_notification' &&
                        el.name !== 'cases_email_satisfaction' &&
                        el.name !== 'cases_email_footer') {
                        list.append('<li class="config__tpl-name cu-mtopsmall cu-clickable js-tpl-selector js-html-tpl" data-name="' + el.name + '">' + el.name.replace('helpcenter_', '').replace('_', ' ') + '</li>');
                        if (el.name == view.preSelect) {
                            defaultTpl = el;
                        } else if (el.name == 'helpcenter_home') {
                            homeTpl = el;
                        }
                    }
                });

                // Remove loading animation
                $('.js-loading').removeClass('is-visible');

                // Set editor content
                if (!defaultTpl) {
                    defaultTpl = homeTpl;
                }
                text = defaultTpl.contents;
                editor.session.setValue(text);
                templateName = defaultTpl.name;

                // Set active template name
                $('.js-active-tpl').text(': ' + defaultTpl.name.replace('helpcenter_', '').replace('_', ' '));

                // Enable "Reset" button is modified
                if (defaultTpl.is_custom) {
                    $('#prompt-reset-tpl').removeClass('is-disabled');
                }

                // Enable "Save" button when content is changed
                editor.on("input", function() {
                    if (!editor.session.getUndoManager().isClean()) {
                        $('#save-tpl').removeClass('is-disabled');
                    }
                });
            },
            error: function(model, xhr) {
                view.notSaved(xhr.status);
            }
        });
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Closes customization panel
     */
    closePanel: function() {
        $('.body, #show-config').removeClass('config-panel-open');
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Toggles customization options in a category
     * @param  {Object} e
     */
    toggleOptions: function(e) {
        $(e.target).closest('.js-config-toggle').toggleClass('is-open');
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Toggle code editor
     */
    toggleEditor: function() {
        $('.js-config-toggle').removeClass('is-open');
        $('.body').toggleClass('config-editor-open');
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Toggles templates dropdown list
     * @param  {Object} e
     */
    tplMenu: function(e) {
        e.stopPropagation();
        $('#config__templates').toggleClass('cu-hidden');
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Hides templates dropdown menu
     */
    closeTplMenu: function() {
        $('#config__templates').addClass('cu-hidden');
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Loads the content of selected template
     * @param  {Object} e
     * @param  {String} resetTpl
     */
    selectTpl: function(e, resetTpl) {
        var view = this;
        var tplName;
        if (!resetTpl) {
            this.isLoading();
            editor.session.setValue('');
            tplName = $(e.target).data('name');
            $(e.target).hasClass('js-html-tpl') ? $('.js-active-tpl').text(': ' + tplName.replace('helpcenter_', '').replace('_', ' ')) : $('.js-active-tpl').text('');
        } else {
            tplName = resetTpl;
        }

        // Get template content
        new templatesModel({
            'item': tplName,
            'query': 'singleTpl',
            'brandId': view.brandId
        }).fetch({
            success: function(response) {
                // Remove loader
                $('.js-loading').removeClass('is-visible');

                // Show tempalte content
                var selectedTpl = response.attributes.data;
                text = selectedTpl.contents;
                selectedTpl.category !== 'custom' ? editor.session.setMode("ace/mode/html") : editor.session.setMode("ace/mode/css");
                editor.session.setValue(text);
                templateName = selectedTpl.name;

                // Enable "Reset" button is modified
                if (selectedTpl.is_custom) {
                    $('#prompt-reset-tpl').removeClass('is-disabled');
                }
            },
            error: function(model, xhr) {
                view.notSaved(xhr.status);
            }
        });
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Shows save and cancel button when a change detected
     */
    showSaveBtn: function() {
        if (this.detectChange) {
            $('.js-close-panel').addClass('cu-hidden');
            $('.js-save').removeClass('cu-hidden');
            $('#save-config').removeClass('is-disabled');
        }
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Checks if a setting is changed
     * @param  {Object} e
     */
    settingsChanged: function(e) {
        this.showSaveBtn();
        var item = $(e.target).attr('id').replace('customize__', '');
        if ($.inArray(item, this.changedOptions) < 0) {
            this.changedOptions.push(item);
        }
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Swtiches between HTML or custom CSS template
     * @param  {Object} e
     */
    toggleTabs: function(e) {
        var elem = $(e.target).closest('.js-tab-item');
        elem.addClass('is-active');
        elem.siblings().removeClass('is-active');
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Saves customization options
     */
    saveOptions: function() {
        var view = this;
        $('#save-config').addClass('is-disabled');
        logoZone.processQueue();
        faviconZone.processQueue();

        // Add changed options to an object
        var params = {};
        _.each(this.changedOptions, function(item) {
            params[item] = $('#customize__' + item).val().trim();
        });

        // Trigger API request if there are any changes
        if (!_.isEmpty(params)) {
            Backbone.emulateJSON = true;
            new optionsModel({
            'id': 1,
            'api': 'helpcenters/' + view.helpcenterId
            }).save(params, {
                success: function(response) {
                    view.notification('success');
                    location.reload();
                },
                error: function(model, xhr) {
                    view.notSaved(xhr.status);
                    Backbone.emulateJSON = false;
                }
            });
        }
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Enable/disable messenger
     */
    toggleMessenger: function() {
        var view = this;
        Backbone.emulateJSON = true;
        new optionsModel({
        'id': 1,
        'api': 'helpcenters/' + view.helpcenterId
        }).save({
            'display_messenger': $('#toggle-messenger').is(':checked') ? 1 : 0
        }, {
            success: function(response) {
                view.notification('success');
            },
            error: function(model, xhr) {
                view.notSaved(xhr.status);
                Backbone.emulateJSON = false;
            }
        });
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Shows confirmation modal
     * @param  {Object} e
     */
    promptReset: function(e) {
        var tpl = _.template($('#tpl-revert-modal').html());
        $('.js-config-panel').append(tpl({ scope: e.target.id.replace('prompt-', '') }));
        $('#revert-modal').fadeIn(300);
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Hides the modal
     */
    cancelReset: function() {
        $('#revert-modal').fadeOut(200, function() {
            $(this).remove();
        });
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Resets options values to default
     */
    resetOptions: function(e) {
        var view = this;
        view.detectChange = false;
        $(e.target).addClass('is-disabled');
        $('#customize__primary_color').minicolors('value', '#4eafcb');
        $('#customize__font').val('Source Sans Pro');
        Backbone.emulateJSON = true;
        new optionsModel({
        'id': 1,
        'api': 'helpcenters/' + view.helpcenterId
        }).save({
            'primary_color': '',
            'font': ''
        }, {
            success: function(response) {
                view.cancelReset();
                view.notification('success');
                location.reload();
            },
            error: function(model, xhr) {
                view.notSaved(xhr.status);
                Backbone.emulateJSON = false;
                view.detectChange = true;
            }
        });
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Saves a template to database
     */
    saveTpl: function() {
        var view = this;
        $('#save-tpl').addClass('is-disabled');
        new templatesModel({
            'item': templateName,
            'query': 'singleTpl',
            'brandId': view.brandId
        }).save({
            'contents': editor.session.getValue()
        }, {
            success: function(response) {
                view.notification('success');
                $('#prompt-reset-tpl').removeClass('is-disabled');
            },
            error: function(model, xhr) {
                if (xhr.status === 401) {
                    window.location.href = _Payload.url + 'login';
                } else {
                    view.notification('api-error', xhr.responseJSON.notifications[0].message);
                }
            }
        });
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Resets a template to default
     * @param  {Object} e
     */
    resetTpl: function(e) {
        var view = this;

        // Clear editor content
        editor.session.setValue('');
        view.isLoading();

        // Delete template from database
        new templatesModel({
            'item': templateName,
            'query': 'resetTpl',
            'brandId': view.brandId

        }).save({}, {
            success: function() {
                view.selectTpl(e, templateName);
                view.cancelReset();
                view.notification('success');
            },
            error: function(model, xhr) {
                if (xhr.status === 401) {
                    window.location.href = _Payload.url + 'login';
                } else {
                    view.notification('error');
                    view.selectTpl(e, templateName);
                }
            }
        });
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Shows loading animation
     */
    isLoading: function() {
        // Show loading animation
        $('.js-loading').addClass('is-visible');

        // Disable "Save" and "Reset" button
        $('#save-tpl, #prompt-reset-tpl').addClass('is-disabled');
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Shows success/error notification
     * @param  {String} type
     */
    notification: function(type, msg) {
        if (type == 'api-error') {
            $('.js-config-api-error').text(msg);
        }
        var alertBox = $('.js-config-' + type);
        alertBox.addClass('is-visible');
        setTimeout(function() {
            alertBox.removeClass('is-visible');
        }, 2500);
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Handles server error
     * @param  {Number} status
     */
    notSaved: function(status) {
        if (status === 401) {
            window.location.href = _Payload.url + 'login';
        } else {
            this.notification('error');
        }
    }
});
};


modules['helpcenter/primary/plugins/tags/core'] = function(require, exports, module, modules) {
var jQuery = require('jquery');
/**
 * jQuery TextExt Plugin
 * http://textextjs.com
 *
 * @version 1.3.1
 * @copyright Copyright (C) 2011 Alex Gorbatchev. All rights reserved.
 * @license MIT License
 */
(function($, undefined)
{
	/**
	 * TextExt is the main core class which by itself doesn't provide any functionality
	 * that is user facing, however it has the underlying mechanics to bring all the
	 * plugins together under one roof and make them work with each other or on their
	 * own.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExt
	 */
	function TextExt() {};

	/**
	 * ItemManager is used to seamlessly convert between string that come from the user input to whatever 
	 * the format the item data is being passed around in. It's used by all plugins that in one way or 
	 * another operate with items, such as Tags, Filter, Autocomplete and Suggestions. Default implementation 
	 * works with `String` type. 
	 *
	 * Each instance of `TextExt` creates a new instance of default implementation of `ItemManager`
	 * unless `itemManager` option was set to another implementation.
	 *
	 * To satisfy requirements of managing items of type other than a `String`, different implementation
	 * if `ItemManager` should be supplied.
	 *
	 * If you wish to bring your own implementation, you need to create a new class and implement all the 
	 * methods that `ItemManager` has. After, you need to supply your pass via the `itemManager` option during
	 * initialization like so:
	 *
	 *     $('#input').textext({
	 *         itemManager : CustomItemManager
	 *     })
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id ItemManager
	 */
	function ItemManager() {};

	/**
	 * TextExtPlugin is a base class for all plugins. It provides common methods which are reused
	 * by majority of plugins.
	 *
	 * All plugins must register themselves by calling the `$.fn.textext.addPlugin(name, constructor)`
	 * function while providing plugin name and constructor. The plugin name is the same name that user
	 * will identify the plugin in the `plugins` option when initializing TextExt component and constructor
	 * function will create a new instance of the plugin. *Without registering, the core won't
	 * be able to see the plugin.*
	 *
	 * <span class="new label version">new in 1.2.0</span> You can get instance of each plugin from the core 
	 * via associated function with the same name as the plugin. For example:
	 *
	 *     $('#input').textext()[0].tags()
	 *     $('#input').textext()[0].autocomplete()
	 *     ...
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExtPlugin
	 */
	function TextExtPlugin() {};

	var stringify = (JSON || {}).stringify,
		slice     = Array.prototype.slice,
		p,
		UNDEFINED = 'undefined',

		/**
		 * TextExt provides a way to pass in the options to configure the core as well as
		 * each plugin that is being currently used. The jQuery exposed plugin `$().textext()` 
		 * function takes a hash object with key/value set of options. For example:
		 *
		 *     $('textarea').textext({
		 *         enabled: true
		 *     })
		 *
		 * There are multiple ways of passing in the options:
		 *
		 * 1. Options could be nested multiple levels deep and accessed using all lowercased, dot
		 * separated style, eg `foo.bar.world`. The manual is using this style for clarity and
		 * consistency. For example:
		 *
		 *        {
		 *            item: {
		 *                manager: ...
		 *            },
		 *
		 *            html: {
		 *                wrap: ...
		 *            },
		 *
		 *            autocomplete: {
		 *                enabled: ...,
		 *                dropdown: {
		 *                   position: ...
		 *                }
		 *            }
		 *        }
		 *
		 * 2. Options could be specified using camel cased names in a flat key/value fashion like so:
		 *
		 *        {
		 *            itemManager: ...,
		 *            htmlWrap: ...,
		 *            autocompleteEnabled: ...,
		 *            autocompleteDropdownPosition: ...
		 *        }
		 *
		 * 3. Finally, options could be specified in mixed style. It's important to understand that
		 * for each dot separated name, its alternative in camel case is also checked for, eg for 
		 * `foo.bar.world` it's alternatives could be `fooBarWorld`, `foo.barWorld` or `fooBar.world`, 
		 * which translates to `{ foo: { bar: { world: ... } } }`, `{ fooBarWorld: ... }`, 
		 * `{ foo : { barWorld : ... } }` or `{ fooBar: { world: ... } }` respectively. For example:
		 *
		 *        {
		 *            itemManager : ...,
		 *            htmlWrap: ...,
		 *            autocomplete: {
		 *                enabled: ...,
		 *                dropdownPosition: ...
		 *            }
		 *        }
		 *
		 * Mixed case is used through out the code, wherever it seems appropriate. However in the code, all option
		 * names are specified in the dot notation because it works both ways where as camel case is not
		 * being converted to its alternative dot notation.
		 *
		 * @author agorbatchev
		 * @date 2011/08/17
		 * @id TextExt.options
		 */

		/**
		 * Default instance of `ItemManager` which takes `String` type as default for tags.
		 *
		 * @name item.manager
		 * @default ItemManager
		 * @author agorbatchev
		 * @date 2011/08/19
		 * @id TextExt.options.item.manager
		 */
		OPT_ITEM_MANAGER = 'item.manager',
		
		/**
		 * List of plugins that should be used with the current instance of TextExt. The list could be
		 * specified as array of strings or as comma or space separated string.
		 *
		 * @name plugins
		 * @default []
		 * @author agorbatchev
		 * @date 2011/08/19
		 * @id TextExt.options.plugins
		 */
		OPT_PLUGINS = 'plugins',
		
		/**
		 * TextExt allows for overriding of virtually any method that the core or any of its plugins
		 * use. This could be accomplished through the use of the `ext` option.
		 *
		 * It's possible to specifically target the core or any plugin, as well as overwrite all the
		 * desired methods everywhere.
		 *
		 * 1. Targeting the core:
		 *
		 *        ext: {
		 *            core: {
		 *                trigger: function()
		 *                {
		 *                    console.log('TextExt.trigger', arguments);
		 *                    $.fn.textext.TextExt.prototype.trigger.apply(this, arguments);
		 *                }
		 *            }
		 *        }
		 *
		 * 2. Targeting individual plugins:
		 *
		 *        ext: {
		 *            tags: {
		 *                addTags: function(tags)
		 *                {
		 *                    console.log('TextExtTags.addTags', tags);
		 *                    $.fn.textext.TextExtTags.prototype.addTags.apply(this, arguments);
		 *                }
		 *            }
		 *        }
		 *
		 * 3. Targeting `ItemManager` instance:
		 *
		 *        ext: {
		 *            itemManager: {
		 *                stringToItem: function(str)
		 *                {
		 *                    console.log('ItemManager.stringToItem', str);
		 *                    return $.fn.textext.ItemManager.prototype.stringToItem.apply(this, arguments);
		 *                }
		 *            }
		 *        }
		 *
		 * 4. And finally, in edge cases you can extend everything at once:
		 *
		 *        ext: {
		 *            '*': {
		 *                fooBar: function() {}
		 *            }
		 *        }
		 *
		 * @name ext
		 * @default {}
		 * @author agorbatchev
		 * @date 2011/08/19
		 * @id TextExt.options.ext
		 */
		OPT_EXT = 'ext',
		
		/**
		 * HTML source that is used to generate elements necessary for the core and all other
		 * plugins to function.
		 *
		 * @name html.wrap
		 * @default '<div class="text-core"><div class="text-wrap"/></div>'
		 * @author agorbatchev
		 * @date 2011/08/19
		 * @id TextExt.options.html.wrap
		 */
		OPT_HTML_WRAP = 'html.wrap',

		/**
		 * HTML source that is used to generate hidden input value of which will be submitted 
		 * with the HTML form.
		 *
		 * @name html.hidden
		 * @default '<input type="hidden" />'
		 * @author agorbatchev
		 * @date 2011/08/20
		 * @id TextExt.options.html.hidden
		 */
		OPT_HTML_HIDDEN = 'html.hidden',
		
		/**
		 * Hash table of key codes and key names for which special events will be created
		 * by the core. For each entry a `[name]KeyDown`, `[name]KeyUp` and `[name]KeyPress` events 
		 * will be triggered along side with `anyKeyUp` and `anyKeyDown` events for every 
		 * key stroke.
		 *
		 * Here's a list of default keys:
		 *
		 *     {
		 *         8   : 'backspace',
		 *         9   : 'tab',
		 *         13  : 'enter!',
		 *         27  : 'escape!',
		 *         37  : 'left',
		 *         38  : 'up!',
		 *         39  : 'right',
		 *         40  : 'down!',
		 *         46  : 'delete',
		 *         108 : 'numpadEnter'
		 *     }
		 *
		 * Please note the `!` at the end of some keys. This tells the core that by default
		 * this keypress will be trapped and not passed on to the text input.
		 *
		 * @name keys
		 * @default { ... }
		 * @author agorbatchev
		 * @date 2011/08/19
		 * @id TextExt.options.keys
		 */
		OPT_KEYS = 'keys',

		/**
		 * The core triggers or reacts to the following events.
		 *
		 * @author agorbatchev
		 * @date 2011/08/17
		 * @id TextExt.events
		 */

		/**
		 * Core triggers `preInvalidate` event before the dimensions of padding on the text input
		 * are set.
		 *
		 * @name preInvalidate
		 * @author agorbatchev
		 * @date 2011/08/19
		 * @id TextExt.events.preInvalidate
		 */
		EVENT_PRE_INVALIDATE = 'preInvalidate',

		/**
		 * Core triggers `postInvalidate` event after the dimensions of padding on the text input
		 * are set.
		 *
		 * @name postInvalidate
		 * @author agorbatchev
		 * @date 2011/08/19
		 * @id TextExt.events.postInvalidate
		 */
		EVENT_POST_INVALIDATE = 'postInvalidate',
		
		/**
		 * Core triggers `getFormData` on every key press to collect data that will be populated
		 * into the hidden input that will be submitted with the HTML form and data that will
		 * be displayed in the input field that user is currently interacting with.
		 *
		 * All plugins that wish to affect how the data is presented or sent must react to 
		 * `getFormData` and populate the data in the following format:
		 *
		 *     {
		 *         input : {String},
		 *         form  : {Object}
		 *     }
		 *
		 * The data key must be a numeric weight which will be used to determine which data
		 * ends up being used. Data with the highest numerical weight gets the priority. This
		 * allows plugins to set the final data regardless of their initialization order, which
		 * otherwise would be impossible.
		 *
		 * For example, the Tags and Autocomplete plugins have to work side by side and Tags
		 * plugin must get priority on setting the data. Therefore the Tags plugin sets data
		 * with the weight 200 where as the Autocomplete plugin sets data with the weight 100.
		 *
		 * Here's an example of a typical `getFormData` handler:
		 * 
		 *     TextExtPlugin.prototype.onGetFormData = function(e, data, keyCode)
		 *     {
		 *         data[100] = self.formDataObject('input value', 'form value');
		 *     };
		 *
		 * Core also reacts to the `getFormData` and updates hidden input with data which will be
		 * submitted with the HTML form.
		 *
		 * @name getFormData
		 * @author agorbatchev
		 * @date 2011/08/19
		 * @id TextExt.events.getFormData
		 */
		EVENT_GET_FORM_DATA = 'getFormData',

		/**
		 * Core triggers and reacts to the `setFormData` event to update the actual value in the
		 * hidden input that will be submitted with the HTML form. Second argument can be value
		 * of any type and by default it will be JSON serialized with `TextExt.serializeData()`
		 * function.
		 *
		 * @name setFormData
		 * @author agorbatchev
		 * @date 2011/08/22
		 * @id TextExt.events.setFormData
		 */
		EVENT_SET_FORM_DATA = 'setFormData',

		/**
		 * Core triggers and reacts to the `setInputData` event to update the actual value in the
		 * text input that user is interacting with. Second argument must be of a `String` type
		 * the value of which will be set into the text input.
		 *
		 * @name setInputData
		 * @author agorbatchev
		 * @date 2011/08/22
		 * @id TextExt.events.setInputData
		 */
		EVENT_SET_INPUT_DATA = 'setInputData',
		
		/**
		 * Core triggers `postInit` event to let plugins run code after all plugins have been 
		 * created and initialized. This is a good place to set some kind of global values before 
		 * somebody gets to use them. This is not the right place to expect all plugins to finish
		 * their initialization.
		 *
		 * @name postInit
		 * @author agorbatchev
		 * @date 2011/08/19
		 * @id TextExt.events.postInit
		 */
		EVENT_POST_INIT = 'postInit',

		/**
		 * Core triggers `ready` event after all global configuration and prepearation has been
		 * done and the TextExt component is ready for use. Event handlers should expect all 
		 * values to be set and the plugins to be in the final state.
		 *
		 * @name ready
		 * @author agorbatchev
		 * @date 2011/08/19
		 * @id TextExt.events.ready
		 */
		EVENT_READY = 'ready',

		/**
		 * Core triggers `anyKeyUp` event for every key up event triggered within the component.
		 *
		 * @name anyKeyUp
		 * @author agorbatchev
		 * @date 2011/08/19
		 * @id TextExt.events.anyKeyUp
		 */

		/**
		 * Core triggers `anyKeyDown` event for every key down event triggered within the component.
		 *
		 * @name anyKeyDown
		 * @author agorbatchev
		 * @date 2011/08/19
		 * @id TextExt.events.anyKeyDown
		 */

		/**
		 * Core triggers `[name]KeyUp` event for every key specifid in the `keys` option that is 
		 * triggered within the component.
		 *
		 * @name [name]KeyUp
		 * @author agorbatchev
		 * @date 2011/08/19
		 * @id TextExt.events.[name]KeyUp
		 */

		/**
		 * Core triggers `[name]KeyDown` event for every key specified in the `keys` option that is 
		 * triggered within the component.
		 *
		 * @name [name]KeyDown
		 * @author agorbatchev
		 * @date 2011/08/19
		 * @id TextExt.events.[name]KeyDown
		 */

		/**
		 * Core triggers `[name]KeyPress` event for every key specified in the `keys` option that is 
		 * triggered within the component.
		 *
		 * @name [name]KeyPress
		 * @author agorbatchev
		 * @date 2011/08/19
		 * @id TextExt.events.[name]KeyPress
		 */

		DEFAULT_OPTS = {
			itemManager : ItemManager,

			plugins : [],
			ext : {},

			html : {
				wrap   : '<div class="text-core"><div class="text-wrap"/></div>',
				hidden : '<input type="hidden" />'
			},

			keys : {
				8   : 'backspace',
				9   : 'tab',
				13  : 'enter!',
				27  : 'escape!',
				37  : 'left',
				38  : 'up!',
				39  : 'right',
				40  : 'down!',
				46  : 'delete',
				108 : 'numpadEnter'
			}
		}
		;

	// Freak out if there's no JSON.stringify function found
	if(!stringify)
		throw new Error('JSON.stringify() not found');

	/**
	 * Returns object property by name where name is dot-separated and object is multiple levels deep.
	 * @param target Object Source object.
	 * @param name String Dot separated property name, ie `foo.bar.world`
	 * @id core.getProperty
	 */
	function getProperty(source, name)
	{
		if(typeof(name) === 'string')
			name = name.split('.');

		var fullCamelCaseName = name.join('.').replace(/\.(\w)/g, function(match, letter) { return letter.toUpperCase() }),
			nestedName        = name.shift(),
			result
			;

		if(typeof(result = source[fullCamelCaseName]) != UNDEFINED)
			result = result;

		else if(typeof(result = source[nestedName]) != UNDEFINED && name.length > 0)
			result = getProperty(result, name);

		// name.length here should be zero
		return result;
	};

	/**
	 * Hooks up specified events in the scope of the current object.
	 * @author agorbatchev
	 * @date 2011/08/09
	 */
	function hookupEvents()
	{
		var args   = slice.apply(arguments),
			self   = this,
			target = args.length === 1 ? self : args.shift(),
			event
			;

		args = args[0] || {};

		function bind(event, handler)
		{
			target.bind(event, function()
			{
				// apply handler to our PLUGIN object, not the target
				return handler.apply(self, arguments);
			});
		}

		for(event in args)
			bind(event, args[event]);
	};

	function formDataObject(input, form)
	{
		return { 'input' : input, 'form' : form };
	};

	//--------------------------------------------------------------------------------
	// ItemManager core component
	
	p = ItemManager.prototype;

	/**
	 * Initialization method called by the core during instantiation.
	 *
	 * @signature ItemManager.init(core)
	 *
	 * @param core {TextExt} Instance of the TextExt core class.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id ItemManager.init
	 */
	p.init = function(core)
	{
	};

	/**
	 * Filters out items from the list that don't match the query and returns remaining items. Default 
	 * implementation checks if the item starts with the query.
	 *
	 * @signature ItemManager.filter(list, query)
	 *
	 * @param list {Array} List of items. Default implementation works with strings.
	 * @param query {String} Query string.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id ItemManager.filter
	 */
	p.filter = function(list, query)
	{
		var result = [],
			i, item
			;

		for(i = 0; i < list.length; i++)
		{
			item = list[i];
			if(this.itemContains(item, query))
				result.push(item);
		}

		return result;
	};

	/**
	 * Returns `true` if specified item contains another string, `false` otherwise. In the default implementation 
	 * `String.indexOf()` is used to check if item string begins with the needle string.
	 *
	 * @signature ItemManager.itemContains(item, needle)
	 *
	 * @param item {Object} Item to check. Default implementation works with strings.
	 * @param needle {String} Search string to be found within the item.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id ItemManager.itemContains
	 */
	p.itemContains = function(item, needle)
	{
		return this.itemToString(item).toLowerCase().indexOf(needle.toLowerCase()) == 0;
	};

	/**
	 * Converts specified string to item. Because default implemenation works with string, input string
	 * is simply returned back. To use custom objects, different implementation of this method could
	 * return something like `{ name : {String} }`.
	 *
	 * @signature ItemManager.stringToItem(str)
	 *
	 * @param str {String} Input string.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id ItemManager.stringToItem
	 */
	p.stringToItem = function(str)
	{
		return str;
	};

	/**
	 * Converts specified item to string. Because default implemenation works with string, input string
	 * is simply returned back. To use custom objects, different implementation of this method could
	 * for example return `name` field of `{ name : {String} }`.
	 *
	 * @signature ItemManager.itemToString(item)
	 *
	 * @param item {Object} Input item to be converted to string.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id ItemManager.itemToString
	 */
	p.itemToString = function(item)
	{
		return item;
	};

	/**
	 * Returns `true` if both items are equal, `false` otherwise. Because default implemenation works with 
	 * string, input items are compared as strings. To use custom objects, different implementation of this 
	 * method could for example compare `name` fields of `{ name : {String} }` type object.
	 *
	 * @signature ItemManager.compareItems(item1, item2)
	 *
	 * @param item1 {Object} First item.
	 * @param item2 {Object} Second item.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id ItemManager.compareItems
	 */
	p.compareItems = function(item1, item2)
	{
		return item1 == item2;
	};

	//--------------------------------------------------------------------------------
	// TextExt core component

	p = TextExt.prototype;
		
	/**
	 * Initializes current component instance with work with the supplied text input and options.
	 *
	 * @signature TextExt.init(input, opts)
	 *
	 * @param input {HTMLElement} Text input.
	 * @param opts {Object} Options.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExt.init
	 */
	p.init = function(input, opts)
	{
		var self = this,
			hiddenInput,
			itemManager,
			container
			;

		self._defaults    = $.extend({}, DEFAULT_OPTS);
		self._opts        = opts || {};
		self._plugins     = {};
		self._itemManager = itemManager = new (self.opts(OPT_ITEM_MANAGER))();
		input             = $(input);
		container         = $(self.opts(OPT_HTML_WRAP));
		hiddenInput       = $(self.opts(OPT_HTML_HIDDEN));

		input
			.wrap(container)
			.keydown(function(e) { return self.onKeyDown(e) })
			.keyup(function(e) { return self.onKeyUp(e) })
			.data('textext', self)
			;

		// keep references to html elements using jQuery.data() to avoid circular references
		$(self).data({
			'hiddenInput'   : hiddenInput,
			'wrapElement' : input.parents('.text-wrap').first(),
			'input'         : input
		});

		// set the name of the hidden input to the text input's name
		hiddenInput.attr('name', input.attr('name'));
		hiddenInput.attr('id', 'tags-array');
		// remove name attribute from the text input
		input.attr('name', null);
		// add hidden input to the DOM
		hiddenInput.insertAfter(input);

		$.extend(true, itemManager, self.opts(OPT_EXT + '.item.manager'));
		$.extend(true, self, self.opts(OPT_EXT + '.*'), self.opts(OPT_EXT + '.core'));
		
		self.originalWidth = input.outerWidth();

		self.invalidateBounds();

		itemManager.init(self);

		self.initPatches();
		self.initPlugins(self.opts(OPT_PLUGINS), $.fn.textext.plugins);

		self.on({
			setFormData  : self.onSetFormData,
			getFormData  : self.onGetFormData,
			setInputData : self.onSetInputData,
			anyKeyUp     : self.onAnyKeyUp
		});

		self.trigger(EVENT_POST_INIT);
		self.trigger(EVENT_READY);

		self.getFormData(0);
	};

	/**
	 * Initialized all installed patches against current instance. The patches are initialized based on their
	 * initialization priority which is returned by each patch's `initPriority()` method. Priority
	 * is a `Number` where patches with higher value gets their `init()` method called before patches
	 * with lower priority value.
	 *
	 * This facilitates initializing of patches in certain order to insure proper dependencies
	 * regardless of which order they are loaded.
	 *
	 * By default all patches have the same priority - zero, which means they will be initialized
	 * in rorder they are loaded, that is unless `initPriority()` is overriden.
	 *
	 * @signature TextExt.initPatches()
	 *
	 * @author agorbatchev
	 * @date 2011/10/11
	 * @id TextExt.initPatches
	 */
	p.initPatches = function()
	{
		var list   = [],
			source = $.fn.textext.patches,
			name
			;

		for(name in source)
			list.push(name);

		this.initPlugins(list, source);
	};

	/**
	 * Creates and initializes all specified plugins. The plugins are initialized based on their
	 * initialization priority which is returned by each plugin's `initPriority()` method. Priority
	 * is a `Number` where plugins with higher value gets their `init()` method called before plugins
	 * with lower priority value.
	 *
	 * This facilitates initializing of plugins in certain order to insure proper dependencies
	 * regardless of which order user enters them in the `plugins` option field.
	 *
	 * By default all plugins have the same priority - zero, which means they will be initialized
	 * in the same order as entered by the user.
	 *
	 * @signature TextExt.initPlugins(plugins)
	 *
	 * @param plugins {Array} List of plugin names to initialize.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExt.initPlugins
	 */
	p.initPlugins = function(plugins, source)
	{
		var self = this,
			ext, name, plugin, initList = [], i
			;

		if(typeof(plugins) == 'string')
			plugins = plugins.split(/\s*,\s*|\s+/g);

		for(i = 0; i < plugins.length; i++)
		{
			name   = plugins[i];
			plugin = source[name];

			if(plugin)
			{
				self._plugins[name] = plugin = new plugin();
				self[name] = (function(plugin) { 
				  return function(){ return plugin; } 
				})(plugin);
				initList.push(plugin);
				$.extend(true, plugin, self.opts(OPT_EXT + '.*'), self.opts(OPT_EXT + '.' + name));
			}
		}

		// sort plugins based on their priority values
		initList.sort(function(p1, p2)
		{
			p1 = p1.initPriority();
			p2 = p2.initPriority();

			return p1 === p2
				? 0
				: p1 < p2 ? 1 : -1
				;
		});

		for(i = 0; i < initList.length; i++)
			initList[i].init(self);
	};

	/**
	 * Returns true if specified plugin is was instantiated for the current instance of core.
	 *
	 * @signature TextExt.hasPlugin(name)
	 *
	 * @param name {String} Name of the plugin to check.
	 *
	 * @author agorbatchev
	 * @date 2011/12/28
	 * @id TextExt.hasPlugin
	 * @version 1.1
	 */
	p.hasPlugin = function(name)
	{
		return !!this._plugins[name];
	};

	/**
	 * Allows to add multiple event handlers which will be execued in the scope of the current object.
	 * 
	 * @signature TextExt.on([target], handlers)
	 *
	 * @param target {Object} **Optional**. Target object which has traditional `bind(event, handler)` method.
	 *                        Handler function will still be executed in the current object's scope.
	 * @param handlers {Object} Key/value pairs of event names and handlers, eg `{ event: handler }`.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExt.on
	 */
	p.on = hookupEvents;

	/**
	 * Binds an event handler to the input box that user interacts with.
	 *
	 * @signature TextExt.bind(event, handler)
	 *
	 * @param event {String} Event name.
	 * @param handler {Function} Event handler.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExt.bind
	 */
	p.bind = function(event, handler)
	{
		this.input().bind(event, handler);
	};

	/**
	 * Triggers an event on the input box that user interacts with. All core events are originated here.
	 * 
	 * @signature TextExt.trigger(event, ...args)
	 *
	 * @param event {String} Name of the event to trigger.
	 * @param ...args All remaining arguments will be passed to the event handler.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExt.trigger
	 */
	p.trigger = function()
	{
		var args = arguments;
		this.input().trigger(args[0], slice.call(args, 1));
	};

	/**
	 * Returns instance of `itemManager` that is used by the component.
	 *
	 * @signature TextExt.itemManager()
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExt.itemManager
	 */
	p.itemManager = function()
	{
		return this._itemManager;
	};

	/**
	 * Returns jQuery input element with which user is interacting with.
	 *
	 * @signature TextExt.input()
	 *
	 * @author agorbatchev
	 * @date 2011/08/10
	 * @id TextExt.input
	 */
	p.input = function()
	{
		return $(this).data('input');
	};

	/**
	 * Returns option value for the specified option by name. If the value isn't found in the user
	 * provided options, it will try looking for default value.
	 *
	 * @signature TextExt.opts(name)
	 *
	 * @param name {String} Option name as described in the options.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExt.opts
	 */
	p.opts = function(name)
	{
		var result = getProperty(this._opts, name);
		return typeof(result) == 'undefined' ? getProperty(this._defaults, name) : result;
	};

	/**
	 * Returns HTML element that was created from the `html.wrap` option. This is the top level HTML
	 * container for the text input with which user is interacting with.
	 *
	 * @signature TextExt.wrapElement()
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExt.wrapElement
	 */
	p.wrapElement = function()
	{
		return $(this).data('wrapElement');
	};

	/**
	 * Updates container to match dimensions of the text input. Triggers `preInvalidate` and `postInvalidate`
	 * events.
	 *
	 * @signature TextExt.invalidateBounds()
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExt.invalidateBounds
	 */
	p.invalidateBounds = function()
	{
		var self      = this,
			input     = self.input(),
			wrap      = self.wrapElement(),
			container = wrap.parent(),
			width     = '100%',
			height
			;

		self.trigger(EVENT_PRE_INVALIDATE);

		height = input.outerHeight() + 'px';

		// using css() method instead of width() and height() here because they don't seem to do the right thing in jQuery 1.8.x
		// https://github.com/alexgorbatchev/jquery-textext/issues/74
		input.css({ 'width' : width });
		wrap.css({ 'width' : width, 'height' : height });
		container.css({ 'height' : height }); 

		self.trigger(EVENT_POST_INVALIDATE);
	};

	/**
	 * Focuses user input on the text box.
	 *
	 * @signature TextExt.focusInput()
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExt.focusInput
	 */
	p.focusInput = function()
	{
		this.input()[0].focus();
	};

	/**
	 * Serializes data for to be set into the hidden input field and which will be submitted 
	 * with the HTML form.
	 *
	 * By default simple JSON serialization is used. It's expected that `JSON.stringify`
	 * method would be available either through built in class in most modern browsers
	 * or through JSON2 library.
	 *
	 * @signature TextExt.serializeData(data)
	 *
	 * @param data {Object} Data to serialize.
	 *
	 * @author agorbatchev
	 * @date 2011/08/09
	 * @id TextExt.serializeData
	 */
	p.serializeData = stringify;

	/**
	 * Returns the hidden input HTML element which will be submitted with the HTML form.
	 *
	 * @signature TextExt.hiddenInput()
	 *
	 * @author agorbatchev
	 * @date 2011/08/09
	 * @id TextExt.hiddenInput
	 */
	p.hiddenInput = function(value)
	{
		return $(this).data('hiddenInput');
	};

	/**
	 * Abstracted functionality to trigger an event and get the data with maximum weight set by all
	 * the event handlers. This functionality is used for the `getFormData` event.
	 *
	 * @signature TextExt.getWeightedEventResponse(event, args)
	 *
	 * @param event {String} Event name.
	 * @param args {Object} Argument to be passed with the event.
	 *
	 * @author agorbatchev
	 * @date 2011/08/22
	 * @id TextExt.getWeightedEventResponse
	 */
	p.getWeightedEventResponse = function(event, args)
	{
		var self      = this,
			data      = {},
			maxWeight = 0
			;

		self.trigger(event, data, args);

		for(var weight in data)
			maxWeight = Math.max(maxWeight, weight);

		return data[maxWeight];
	};

	/**
	 * Triggers the `getFormData` event to get all the plugins to return their data.
	 *
	 * After the data is returned, triggers `setFormData` and `setInputData` to update appopriate values.
	 *
	 * @signature TextExt.getFormData(keyCode)
	 *
	 * @param keyCode {Number} Key code number which has triggered this update. It's impotant to pass
	 * this value to the plugins because they might return different values based on the key that was 
	 * pressed. For example, the Tags plugin returns an empty string for the `input` value if the enter
	 * key was pressed, otherwise it returns whatever is currently in the text input.
	 *
	 * @author agorbatchev
	 * @date 2011/08/22
	 * @id TextExt.getFormData
	 */
	p.getFormData = function(keyCode)
	{
		var self = this,
			data = self.getWeightedEventResponse(EVENT_GET_FORM_DATA, keyCode || 0)
			;

		self.trigger(EVENT_SET_FORM_DATA  , data['form']);
		self.trigger(EVENT_SET_INPUT_DATA , data['input']);
	};

	//--------------------------------------------------------------------------------
	// Event handlers

	/**
	 * Reacts to the `anyKeyUp` event and triggers the `getFormData` to change data that will be submitted
	 * with the form. Default behaviour is that everything that is typed in will be JSON serialized, so
	 * the end result will be a JSON string.
	 *
	 * @signature TextExt.onAnyKeyUp(e)
	 *
	 * @param e {Object} jQuery event.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExt.onAnyKeyUp
	 */
	p.onAnyKeyUp = function(e, keyCode)
	{
		this.getFormData(keyCode);
	};

	/**
	 * Reacts to the `setInputData` event and populates the input text field that user is currently
	 * interacting with.
	 *
	 * @signature TextExt.onSetInputData(e, data)
	 *
	 * @param e {Event} jQuery event.
	 * @param data {String} Value to be set.
	 *
	 * @author agorbatchev
	 * @date 2011/08/22
	 * @id TextExt.onSetInputData
	 */
	p.onSetInputData = function(e, data)
	{
		this.input().val(data);
	};

	/**
	 * Reacts to the `setFormData` event and populates the hidden input with will be submitted with
	 * the HTML form. The value will be serialized with `serializeData()` method.
	 *
	 * @signature TextExt.onSetFormData(e, data)
	 *
	 * @param e {Event} jQuery event.
	 * @param data {Object} Data that will be set.
	 * 
	 * @author agorbatchev
	 * @date 2011/08/22
	 * @id TextExt.onSetFormData
	 */
	p.onSetFormData = function(e, data)
	{
		var self = this;
		self.hiddenInput().val(data);
	};

	/**
	 * Reacts to `getFormData` event triggered by the core. At the bare minimum the core will tell
	 * itself to use the current value in the text input as the data to be submitted with the HTML
	 * form.
	 *
	 * @signature TextExt.onGetFormData(e, data)
	 *
	 * @param e {Event} jQuery event.
	 *
	 * @author agorbatchev
	 * @date 2011/08/09
	 * @id TextExt.onGetFormData
	 */
	p.onGetFormData = function(e, data)
	{
		var val = this.input().val();
		data[0] = formDataObject(val, val);
	};

	//--------------------------------------------------------------------------------
	// User mouse/keyboard input

	/**
	 * Triggers `[name]KeyUp` and `[name]KeyPress` for every keystroke as described in the events.
	 *
	 * @signature TextExt.onKeyUp(e)
	 *
	 * @param e {Object} jQuery event.
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExt.onKeyUp
	 */

	/**
	 * Triggers `[name]KeyDown` for every keystroke as described in the events.
	 *
	 * @signature TextExt.onKeyDown(e)
	 *
	 * @param e {Object} jQuery event.
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExt.onKeyDown
	 */
	
	$(['Down', 'Up']).each(function()
	{
		var type = this.toString();

		p['onKey' + type] = function(e)
		{
			var self          = this,
				keyName       = self.opts(OPT_KEYS)[e.keyCode],
				defaultResult = true
				;

			if(keyName)
			{
				defaultResult = keyName.substr(-1) != '!';
				keyName       = keyName.replace('!', '');

				self.trigger(keyName + 'Key' + type);

				// manual *KeyPress event fimplementation for the function keys like Enter, Backspace, etc.
				if(type == 'Up' && self._lastKeyDown == e.keyCode)
				{
					self._lastKeyDown = null;
					self.trigger(keyName + 'KeyPress');
				}

				if(type == 'Down')
					self._lastKeyDown = e.keyCode;
			}

			self.trigger('anyKey' + type, e.keyCode);

			return defaultResult;
		};
	});

	//--------------------------------------------------------------------------------
	// Plugin Base
	
	p = TextExtPlugin.prototype;

	/**
	 * Allows to add multiple event handlers which will be execued in the scope of the current object.
	 * 
	 * @signature TextExt.on([target], handlers)
	 *
	 * @param target {Object} **Optional**. Target object which has traditional `bind(event, handler)` method.
	 *                        Handler function will still be executed in the current object's scope.
	 * @param handlers {Object} Key/value pairs of event names and handlers, eg `{ event: handler }`.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExtPlugin.on
	 */
	p.on = hookupEvents;

	/**
	 * Returns the hash object that `getFormData` triggered by the core expects.
	 *
	 * @signature TextExtPlugin.formDataObject(input, form)
	 *
	 * @param input {String} Value that will go into the text input that user is interacting with.
	 * @param form {Object} Value that will be serialized and put into the hidden that will be submitted
	 * with the HTML form.
	 *
	 * @author agorbatchev
	 * @date 2011/08/22
	 * @id TextExtPlugin.formDataObject
	 */
	p.formDataObject = formDataObject;

	/**
	 * Initialization method called by the core during plugin instantiation. This method must be implemented
	 * by each plugin individually.
	 *
	 * @signature TextExtPlugin.init(core)
	 *
	 * @param core {TextExt} Instance of the TextExt core class.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExtPlugin.init
	 */
	p.init = function(core) { throw new Error('Not implemented') };

	/**
	 * Initialization method wich should be called by the plugin during the `init()` call.
	 *
	 * @signature TextExtPlugin.baseInit(core, defaults)
	 *
	 * @param core {TextExt} Instance of the TextExt core class.
	 * @param defaults {Object} Default plugin options. These will be checked if desired value wasn't
	 * found in the options supplied by the user.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExtPlugin.baseInit
	 */
	p.baseInit = function(core, defaults)
	{
		var self = this;

		core._defaults = $.extend(true, core._defaults, defaults);
		self._core     = core;
		self._timers   = {};
	};

	/**
	 * Allows starting of multiple timeout calls. Each time this method is called with the same
	 * timer name, the timer is reset. This functionality is useful in cases where an action needs
	 * to occur only after a certain period of inactivity. For example, making an AJAX call after 
	 * user stoped typing for 1 second.
	 *
	 * @signature TextExtPlugin.startTimer(name, delay, callback)
	 *
	 * @param name {String} Timer name.
	 * @param delay {Number} Delay in seconds.
	 * @param callback {Function} Callback function.
	 *
	 * @author agorbatchev
	 * @date 2011/08/25
	 * @id TextExtPlugin.startTimer
	 */
	p.startTimer = function(name, delay, callback)
	{
		var self = this;

		self.stopTimer(name);

		self._timers[name] = setTimeout(
			function()
			{
				delete self._timers[name];
				callback.apply(self);
			},
			delay * 1000
		);
	};

	/**
	 * Stops the timer by name without resetting it.
	 *
	 * @signature TextExtPlugin.stopTimer(name)
	 *
	 * @param name {String} Timer name.
	 *
	 * @author agorbatchev
	 * @date 2011/08/25
	 * @id TextExtPlugin.stopTimer
	 */
	p.stopTimer = function(name)
	{
		clearTimeout(this._timers[name]);
	};

	/**
	 * Returns instance of the `TextExt` to which current instance of the plugin is attached to.
	 *
	 * @signature TextExtPlugin.core()
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExtPlugin.core
	 */
	p.core = function()
	{
		return this._core;
	};

	/**
	 * Shortcut to the core's `opts()` method. Returns option value.
	 *
	 * @signature TextExtPlugin.opts(name)
	 * 
	 * @param name {String} Option name as described in the options.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExtPlugin.opts
	 */
	p.opts = function(name)
	{
		return this.core().opts(name);
	};

	/**
	 * Shortcut to the core's `itemManager()` method. Returns instance of the `ItemManger` that is
	 * currently in use.
	 *
	 * @signature TextExtPlugin.itemManager()
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExtPlugin.itemManager
	 */
	p.itemManager = function()
	{
		return this.core().itemManager();
	};

	/**
	 * Shortcut to the core's `input()` method. Returns instance of the HTML element that represents
	 * current text input.
	 *
	 * @signature TextExtPlugin.input()
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExtPlugin.input
	 */
	p.input = function()
	{
		return this.core().input();
	};

	/**
	 * Shortcut to the commonly used `this.input().val()` call to get or set value of the text input.
	 *
	 * @signature TextExtPlugin.val(value)
	 *
	 * @param value {String} Optional value. If specified, the value will be set, otherwise it will be
	 * returned.
	 *
	 * @author agorbatchev
	 * @date 2011/08/20
	 * @id TextExtPlugin.val
	 */
	p.val = function(value)
	{
		var input = this.input();

		if(typeof(value) === UNDEFINED)
			return input.val();
		else
			input.val(value);
	};

	/**
	 * Shortcut to the core's `trigger()` method. Triggers specified event with arguments on the
	 * component core.
	 *
	 * @signature TextExtPlugin.trigger(event, ...args)
	 *
	 * @param event {String} Name of the event to trigger.
	 * @param ...args All remaining arguments will be passed to the event handler.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExtPlugin.trigger
	 */
	p.trigger = function()
	{
		var core = this.core();
		core.trigger.apply(core, arguments);
	};

	/**
	 * Shortcut to the core's `bind()` method. Binds specified handler to the event.
	 *
	 * @signature TextExtPlugin.bind(event, handler)
	 *
	 * @param event {String} Event name.
	 * @param handler {Function} Event handler.
	 *
	 * @author agorbatchev
	 * @date 2011/08/20
	 * @id TextExtPlugin.bind
	 */
	p.bind = function(event, handler)
	{
		this.core().bind(event, handler);
	};

	/**
	 * Returns initialization priority for this plugin. If current plugin depends upon some other plugin
	 * to be initialized before or after, priority needs to be adjusted accordingly. Plugins with higher
	 * priority initialize before plugins with lower priority.
	 *
	 * Default initialization priority is `0`.
	 *
	 * @signature TextExtPlugin.initPriority()
	 *
	 * @author agorbatchev
	 * @date 2011/08/22
	 * @id TextExtPlugin.initPriority
	 */
	p.initPriority = function()
	{
		return 0;
	};

	//--------------------------------------------------------------------------------
	// jQuery Integration
	
	/**
	 * TextExt integrates as a jQuery plugin available through the `$(selector).textext(opts)` call. If
	 * `opts` argument is passed, then a new instance of `TextExt` will be created for all the inputs
	 * that match the `selector`. If `opts` wasn't passed and TextExt was already intantiated for 
	 * inputs that match the `selector`, array of `TextExt` instances will be returned instead.
	 *
	 *     // will create a new instance of `TextExt` for all elements that match `.sample`
	 *     $('.sample').textext({ ... });
	 *
	 *     // will return array of all `TextExt` instances
	 *     var list = $('.sample').textext();
	 *
	 * The following properties are also exposed through the jQuery `$.fn.textext`:
	 *
	 * * `TextExt` -- `TextExt` class.
	 * * `TextExtPlugin` -- `TextExtPlugin` class.
	 * * `ItemManager` -- `ItemManager` class.
	 * * `plugins` -- Key/value table of all registered plugins.
	 * * `addPlugin(name, constructor)` -- All plugins should register themselves using this function.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExt.jquery
	 */

	var cssInjected = false;

	var textext = $.fn.textext = function(opts)
	{
		var css;
		
		if(!cssInjected && (css = $.fn.textext.css) != null)
		{
			$('head').append('<style>' + css + '</style>');
			cssInjected = true;
		}

		return this.map(function()
		{
			var self = $(this);

			if(opts == null)
				return self.data('textext');

			var instance = new TextExt();

			instance.init(self, opts);
			self.data('textext', instance);

			return instance.input()[0];
		});
	};

	/**
	 * This static function registers a new plugin which makes it available through the `plugins` option
	 * to the end user. The name specified here is the name the end user would put in the `plugins` option
	 * to add this plugin to a new instance of TextExt.
	 * 
	 * @signature $.fn.textext.addPlugin(name, constructor)
	 *
	 * @param name {String} Name of the plugin.
	 * @param constructor {Function} Plugin constructor.
	 *
	 * @author agorbatchev
	 * @date 2011/10/11
	 * @id TextExt.addPlugin
	 */
	textext.addPlugin = function(name, constructor)
	{
		textext.plugins[name] = constructor;
		constructor.prototype = new textext.TextExtPlugin();
	};

	/**
	 * This static function registers a new patch which is added to each instance of TextExt. If you are
	 * adding a new patch, make sure to call this method.
	 * 
	 * @signature $.fn.textext.addPatch(name, constructor)
	 *
	 * @param name {String} Name of the patch.
	 * @param constructor {Function} Patch constructor.
	 *
	 * @author agorbatchev
	 * @date 2011/10/11
	 * @id TextExt.addPatch
	 */
	textext.addPatch = function(name, constructor)
	{
		textext.patches[name] = constructor;
		constructor.prototype = new textext.TextExtPlugin();
	};

	textext.TextExt       = TextExt;
	textext.TextExtPlugin = TextExtPlugin;
	textext.ItemManager   = ItemManager;
	textext.plugins       = {};
	textext.patches       = {};
})(jQuery);

(function($)
{
	function TextExtIE9Patches() {};

	$.fn.textext.TextExtIE9Patches = TextExtIE9Patches;
	$.fn.textext.addPatch('ie9',TextExtIE9Patches);

	var p = TextExtIE9Patches.prototype;

	p.init = function(core)
	{
		if(navigator.userAgent.indexOf('MSIE 9') == -1)
			return;

		var self = this;

		core.on({ postInvalidate : self.onPostInvalidate });
	};

	p.onPostInvalidate = function()
	{
		var self  = this,
			input = self.input(),
			val   = input.val()
			;

		// agorbatchev :: IE9 doesn't seem to update the padding if box-sizing is on until the
		// text box value changes, so forcing this change seems to do the trick of updating
		// IE's padding visually.
		input.val(Math.random());
		input.val(val);
	};
})(jQuery);
};


modules['helpcenter/primary/plugins/froala/link'] = function(require, exports, module, modules) {
/*!
 * froala_editor v2.3.4 (https://www.froala.com/wysiwyg-editor)
 * License https://froala.com/wysiwyg-editor/terms/
 * Copyright 2014-2016 Froala Labs
 */

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof module === 'object' && module.exports) {
        // Node/CommonJS
        module.exports = function( root, jQuery ) {
            if ( jQuery === undefined ) {
                // require('jQuery') returns a factory that requires window to
                // build a jQuery instance, we normalize how we use modules
                // that require this pattern but the window provided is a noop
                // if it's defined (how jquery works)
                if ( typeof window !== 'undefined' ) {
                    jQuery = require('jquery');
                }
                else {
                    jQuery = require('jquery')(root);
                }
            }
            factory(jQuery);
            return jQuery;
        };
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {

  'use strict';

  $.extend($.FE.POPUP_TEMPLATES, {
    'link.edit': '[_BUTTONS_]',
    'link.insert': '[_BUTTONS_][_INPUT_LAYER_]'
  })

  $.extend($.FE.DEFAULTS, {
    linkEditButtons: ['linkOpen', 'linkStyle', 'linkEdit', 'linkRemove'],
    linkInsertButtons: ['linkBack', '|', 'linkList'],
    linkAttributes: {},
    linkAutoPrefix: 'http://',
    linkStyles: {
      'fr-green': 'Green',
      'fr-strong': 'Thick'
    },
    linkMultipleStyles: true,
    linkConvertEmailAddress: true,
    linkAlwaysBlank: false,
    linkAlwaysNoFollow: false,
    linkList: [
      {
        text: 'Froala',
        href: 'https://froala.com',
        target: '_blank'
      },
      {
        text: 'Google',
        href: 'https://google.com',
        target: '_blank'
      },
      {
        displayText: 'Facebook',
        href: 'https://facebook.com'
      }
    ],
    linkText: true
  });

  $.FE.PLUGINS.link = function (editor) {
    function get () {
      var $current_image = editor.image ? editor.image.get() : null;

      if (!$current_image && editor.$wp) {
        var s_el = editor.selection.element();
        var e_el = editor.selection.endElement();

        if (s_el.tagName != 'A' && !editor.node.isElement(s_el)) {
          s_el = $(s_el).parentsUntil(editor.$el, 'a:first').get(0);
        }

        if (e_el.tagName != 'A' && !editor.node.isElement(e_el)) {
          e_el = $(e_el).parentsUntil(editor.$el, 'a:first').get(0);
        }

        if (e_el && e_el == s_el && e_el.tagName == 'A') {
          return s_el;
        }

        return null;
      }
      else if (editor.$el.get(0).tagName == 'A' && editor.core.hasFocus()) {
        return editor.$el.get(0);
      }
      else {
        if ($current_image && $current_image.get(0).parentNode && $current_image.get(0).parentNode.tagName == 'A') {
          return $current_image.get(0).parentNode;
        }
      }
    }

    function allSelected () {
      var $current_image = editor.image ? editor.image.get() : null;

      var selectedLinks = [];
      if ($current_image) {
        if ($current_image.get(0).parentNode.tagName == 'A') {
          selectedLinks.push($current_image.get(0).parentNode);
        }
      }
      else {
        var range;
        var containerEl;
        var links;
        var linkRange;

        if (editor.win.getSelection) {
          var sel = editor.win.getSelection();
          if (sel.getRangeAt && sel.rangeCount) {
            linkRange = editor.doc.createRange();
            for (var r = 0; r < sel.rangeCount; ++r) {
              range = sel.getRangeAt(r);
              containerEl = range.commonAncestorContainer;

              if (containerEl && containerEl.nodeType != 1) {
                containerEl = containerEl.parentNode;
              }

              if (containerEl && containerEl.nodeName.toLowerCase() == 'a') {
                selectedLinks.push(containerEl);
              } else {
                links = containerEl.getElementsByTagName('a');
                for (var i = 0; i < links.length; ++i) {
                  linkRange.selectNodeContents(links[i]);
                  if (linkRange.compareBoundaryPoints(range.END_TO_START, range) < 1 && linkRange.compareBoundaryPoints(range.START_TO_END, range) > -1) {
                    selectedLinks.push(links[i]);
                  }
                }
              }
            }
            // linkRange.detach();
          }
        } else if (editor.doc.selection && editor.doc.selection.type != 'Control') {
          range = editor.doc.selection.createRange();
          containerEl = range.parentElement();
          if (containerEl.nodeName.toLowerCase() == 'a') {
            selectedLinks.push(containerEl);
          } else {
            links = containerEl.getElementsByTagName('a');
            linkRange = editor.doc.body.createTextRange();
            for (var j = 0; j < links.length; ++j) {
              linkRange.moveToElementText(links[j]);
              if (linkRange.compareEndPoints('StartToEnd', range) > -1 && linkRange.compareEndPoints('EndToStart', range) < 1) {
                selectedLinks.push(links[j]);
              }
            }
          }
        }
      }

      return selectedLinks;
    }

    function _edit (e) {
      _hideEditPopup();

      setTimeout (function () {
        // No event passed.
        // Event passed and (left click or other event type).
        if (!e || (e && (e.which == 1 || e.type != 'mouseup'))) {
          var link = get();
          var $current_image = editor.image ? editor.image.get() : null;

          if (link && !$current_image) {
            if (editor.image) {
              var contents = editor.node.contents(link);

              // https://github.com/froala/wysiwyg-editor/issues/1103
              if (contents.length == 1 && contents[0].tagName == 'IMG') {
                var range = editor.selection.ranges(0);
                if (range.startOffset === 0 && range.endOffset === 0) {
                  $(link).before($.FE.MARKERS);
                }
                else {
                  $(link).after($.FE.MARKERS);
                }

                editor.selection.restore();
                return false;
              }
            }

            if (e) {
              e.stopPropagation();
            }

            _showEditPopup(link);
          }
        }
      }, editor.helpers.isIOS() ? 100 : 0);
    }

    function _showEditPopup (link) {
      var $popup = editor.popups.get('link.edit');
      if (!$popup) $popup = _initEditPopup();

      var $link = $(link);

      if (!editor.popups.isVisible('link.edit')) {
        editor.popups.refresh('link.edit');
      }

      editor.popups.setContainer('link.edit', $(editor.opts.scrollableContainer));
      var left = $link.offset().left + $(link).outerWidth() / 2;
      var top = $link.offset().top + $link.outerHeight();

      editor.popups.show('link.edit', left, top, $link.outerHeight());
    }

    function _hideEditPopup () {
      editor.popups.hide('link.edit');
    }

    function _initEditPopup () {
      // Link buttons.
      var link_buttons = '';
      if (editor.opts.linkEditButtons.length > 1) {
        if (editor.$el.get(0).tagName == 'A' && editor.opts.linkEditButtons.indexOf('linkRemove') >= 0) {
          editor.opts.linkEditButtons.splice(editor.opts.linkEditButtons.indexOf('linkRemove'), 1);
        }

        link_buttons = '<div class="fr-buttons">' + editor.button.buildList(editor.opts.linkEditButtons) + '</div>';
      }

      var template = {
        buttons: link_buttons
      };

      // Set the template in the popup.
      var $popup = editor.popups.create('link.edit', template);

      if (editor.$wp) {
        editor.events.$on(editor.$wp, 'scroll.link-edit', function () {
          if (get() && editor.popups.isVisible('link.edit')) {
            _showEditPopup(get());
          }
        });
      }

      return $popup;
    }

    /**
     * Hide link insert popup.
     */
    function _hideInsertPopup () {
    }

    function _refreshInsertPopup () {
      var $popup = editor.popups.get('link.insert');
      var link = get();

      if (link) {
        var $link = $(link);
        var text_inputs = $popup.find('input.fr-link-attr[type="text"]');
        var check_inputs = $popup.find('input.fr-link-attr[type="checkbox"]');


        var i;
        var $input;
        for (i = 0; i < text_inputs.length; i++) {
          $input = $(text_inputs[i]);
          $input.val($link.attr($input.attr('name') || ''));
        }

        check_inputs.prop('checked', false);
        for (i = 0; i < check_inputs.length; i++) {
          $input = $(check_inputs[i]);
          if ($link.attr($input.attr('name')) == $input.data('checked')) {
            $input.prop('checked', true);
          }
        }

        $popup.find('input.fr-link-attr[type="text"][name="text"]').val($link.text());
      }
      else {
        $popup.find('input.fr-link-attr[type="text"]').val('');
        $popup.find('input.fr-link-attr[type="checkbox"]').prop('checked', false);
        $popup.find('input.fr-link-attr[type="text"][name="text"]').val(editor.selection.text());
      }

      $popup.find('input.fr-link-attr').trigger('change');

      var $current_image = editor.image ? editor.image.get() : null;
      if ($current_image) {
        $popup.find('.fr-link-attr[name="text"]').parent().hide();
      }
      else {
        $popup.find('.fr-link-attr[name="text"]').parent().show();
      }
    }

    function _showInsertPopup () {
      var $btn = editor.$tb.find('.fr-command[data-cmd="insertLink"]');

      var $popup = editor.popups.get('link.insert');
      if (!$popup) $popup = _initInsertPopup();

      if (!$popup.hasClass('fr-active')) {
        editor.popups.refresh('link.insert');
        editor.popups.setContainer('link.insert', editor.$tb || $(editor.opts.scrollableContainer));

        if ($btn.is(':visible')) {
          var left = $btn.offset().left + $btn.outerWidth() / 2;
          var top = $btn.offset().top + (editor.opts.toolbarBottom ? 10 : $btn.outerHeight() - 10);
          editor.popups.show('link.insert', left, top, $btn.outerHeight());
        }
        else {
          editor.position.forSelection($popup);
          editor.popups.show('link.insert');
        }
      }
    }

    function _initInsertPopup (delayed) {
      if (delayed) {
        editor.popups.onRefresh('link.insert', _refreshInsertPopup);
        editor.popups.onHide('link.insert', _hideInsertPopup);

        return true;
      }

      // Image buttons.
      var link_buttons = '';
      if (editor.opts.linkInsertButtons.length >= 1) {
        link_buttons = '<div class="fr-buttons">' + editor.button.buildList(editor.opts.linkInsertButtons) + '</div>';
      }

      var checkmark = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="10" height="10" viewBox="0 0 32 32"><path d="M27 4l-15 15-7-7-5 5 12 12 20-20z" fill="#FFF"></path></svg>';

      // Image by url layer.
      var input_layer = '';
      var tab_idx = 0;
      input_layer = '<div class="fr-link-insert-layer fr-layer fr-active" id="fr-link-insert-layer-' + editor.id + '">';
      input_layer += '<div class="fr-input-line"><input name="href" type="text" class="fr-link-attr" placeholder="URL" tabIndex="' + (++tab_idx) + '"></div>';

      if (editor.opts.linkText) {
        input_layer += '<div class="fr-input-line"><input name="text" type="text" class="fr-link-attr" placeholder="' + editor.language.translate('Text') + '" tabIndex="' + (++tab_idx) + '"></div>';
      }

      // Add any additional fields.
      for (var attr in editor.opts.linkAttributes) {
        if (editor.opts.linkAttributes.hasOwnProperty(attr)) {
          var placeholder = editor.opts.linkAttributes[attr];
          input_layer += '<div class="fr-input-line"><input name="' + attr + '" type="text" class="fr-link-attr" placeholder="' + editor.language.translate(placeholder) + '" tabIndex="' + (++tab_idx) + '"></div>';
        }
      }

      if (!editor.opts.linkAlwaysBlank) {
        input_layer += '<div class="fr-checkbox-line"><span class="fr-checkbox"><input name="target" class="fr-link-attr" data-checked="_blank" type="checkbox" id="fr-link-target-' + editor.id + '" tabIndex="' + (++tab_idx) + '"><span>' + checkmark + '</span></span><label for="fr-link-target-' + editor.id + '">' + editor.language.translate('Open in new tab') + '</label></div>';
      }

      input_layer += '<div class="fr-action-buttons"><button class="fr-command fr-submit" data-cmd="linkInsert" href="#" tabIndex="' + (++tab_idx) + '" type="button">' + editor.language.translate('Insert') + '</button></div></div>'

      var template = {
        buttons: link_buttons,
        input_layer: input_layer
      }

      // Set the template in the popup.
      var $popup = editor.popups.create('link.insert', template);

      if (editor.$wp) {
        editor.events.$on(editor.$wp, 'scroll.link-insert', function () {
          var $current_image = editor.image ? editor.image.get() : null;
          if ($current_image && editor.popups.isVisible('link.insert')) {
            imageLink();
          }

          if (get && editor.popups.isVisible('link.insert')) {
            update();
          }
        });
      }

      return $popup;
    }

    function remove () {
      var link = get();
      var $current_image = editor.image ? editor.image.get() : null;

      if (editor.events.trigger('link.beforeRemove', [link]) === false) return false;

      if ($current_image && link) {
        $current_image.unwrap();
        editor.image.edit($current_image);
      }
      else if (link) {
        editor.selection.save();
        $(link).replaceWith($(link).html());
        editor.selection.restore();
        _hideEditPopup();
      }
    }

    function _init () {
      // Edit on keyup.
      editor.events.on('keyup', function (e) {
        if (e.which != $.FE.KEYCODE.ESC) {
          _edit(e);
        }
      });

      editor.events.on('window.mouseup', _edit);

      if (editor.helpers.isMobile()) {
        editor.events.$on(editor.$doc, 'selectionchange', _edit);
      }

      _initInsertPopup(true);

      // Init on link.
      if (editor.$el.get(0).tagName == 'A') {
        editor.$el.addClass('fr-view');
      }
    }

    function usePredefined (val) {
      var link = editor.opts.linkList[val];

      var $popup = editor.popups.get('link.insert');
      var text_inputs = $popup.find('input.fr-link-attr[type="text"]');
      var check_inputs = $popup.find('input.fr-link-attr[type="checkbox"]');

      var $input;
      var i;
      for (i = 0; i < text_inputs.length; i++) {
        $input = $(text_inputs[i]);
        if (link[$input.attr('name')]) {
          $input.val(link[$input.attr('name')]);
        }
        else if ($input.attr('name') != 'text') {
          $input.val('');
        }
      }

      for (i = 0; i < check_inputs.length; i++) {
        $input = $(check_inputs[i]);
        $input.prop('checked', $input.data('checked') == link[$input.attr('name')]);
      }
    }

    function insertCallback () {
      var $popup = editor.popups.get('link.insert');
      var text_inputs = $popup.find('input.fr-link-attr[type="text"]');
      var check_inputs = $popup.find('input.fr-link-attr[type="checkbox"]');

      var href = text_inputs.filter('[name="href"]').val();
      var text = text_inputs.filter('[name="text"]').val();

      var attrs = {};
      var $input;
      var i;
      for (i = 0; i < text_inputs.length; i++) {
        $input = $(text_inputs[i]);
        if (['href', 'text'].indexOf($input.attr('name')) < 0) {
          attrs[$input.attr('name')] = $input.val();
        }
      }

      for (i = 0; i < check_inputs.length; i++) {
        $input = $(check_inputs[i]);
        if ($input.is(':checked')) {
          attrs[$input.attr('name')] = $input.data('checked');
        }
        else {
          attrs[$input.attr('name')] = $input.data('unchecked');
        }
      }

      var t = $(editor.o_win).scrollTop();
      insert(href, text, attrs);
      $(editor.o_win).scrollTop(t);
    }

    function _split () {
      if (!editor.selection.isCollapsed()) {
        editor.selection.save();
        var markers = editor.$el.find('.fr-marker').addClass('fr-unprocessed').toArray();
        while (markers.length) {
          var $marker = $(markers.pop());
          $marker.removeClass('fr-unprocessed');

          var deep_parent = editor.node.deepestParent($marker.get(0));
          if (deep_parent) {
            var node = $marker.get(0);
            var close_str = '';
            var open_str = '';
            do {
              node = node.parentNode;
              if (!editor.node.isBlock(node)) {
                close_str = close_str + editor.node.closeTagString(node);
                open_str = editor.node.openTagString(node) + open_str;
              }
            } while (node != deep_parent);

            var marker_str = editor.node.openTagString($marker.get(0)) + $marker.html() +  editor.node.closeTagString($marker.get(0));

            $marker.replaceWith('<span id="fr-break"></span>');
            var h = $(deep_parent).html();
            h = h.replace(/<span id="fr-break"><\/span>/g, close_str + marker_str + open_str);

            $(deep_parent).html(h);
          }

          markers = editor.$el.find('.fr-marker.fr-unprocessed').toArray();
        }

        editor.selection.restore();
      }
    }

    /**
     * Insert link into the editor.
     */
    function insert (href, text, attrs) {
      if (typeof attrs == 'undefined') attrs = {};

      // Get image if we have one selected.
      var $current_image = editor.image ? editor.image.get() : null;

      if (!$current_image && editor.$el.get(0).tagName != 'A') {
        editor.selection.restore();
        editor.popups.hide('link.insert');
      }
      else if (editor.$el.get(0).tagName == 'A') {
        editor.$el.focus();
      }

      var original_href = href;

      // Convert email address.
      if (editor.opts.linkConvertEmailAddress) {
        var regex = /^[\w._]+@[a-z\u00a1-\uffff0-9_-]+?\.[a-z\u00a1-\uffff0-9]{2,}$/i;

        if (regex.test(href) && !/^mailto:.*/i.test(href)) {
          href = 'mailto:' + href;
        }
      }

      // Add autoprefix.
      if (editor.opts.linkAutoPrefix !== '' && !/^(mailto|tel|sms|notes|data):.*/i.test(href) && !/^data:image.*/i.test(href) && !/^(https?:|ftps?:|file:|)\/\//i.test(href)) {
        // Do prefix only if starting character is not absolute.
        if (['/', '{', '[', '#', '('].indexOf((href || '')[0]) < 0) {
          href = editor.opts.linkAutoPrefix + href;
        }
      }

      // Sanitize the URL.
      href = editor.helpers.sanitizeURL(href);

      // Default attributs.
      if (editor.opts.linkAlwaysBlank) attrs.target = '_blank';
      if (editor.opts.linkAlwaysNoFollow) attrs.rel = 'nofollow';

      // Format text.
      text = text || '';

      if (href === editor.opts.linkAutoPrefix) {
        var $popup = editor.popups.get('link.insert');
        $popup.find('input[name="href"]').addClass('fr-error');
        editor.events.trigger('link.bad', [original_href]);
        return false;
      }

      // Check if we have selection only in one link.
      var link = get();
      var $link;

      if (link) {
        $link = $(link);

        // Clear attributes.
        var a_list = editor.node.rawAttributes(link);
        for (var attr in a_list) {
          if (a_list.hasOwnProperty(attr)) {
            if (attr != 'class' && attr != 'style') {
              $link.removeAttr(attr);
            }
          }
        }

        $link.attr('href', href);

        // Change text if it is different.
        if (text.length > 0 && $link.text() != text && !$current_image) {
          $link.text(text);
        }

        if (!$current_image) {
          $link
            .prepend($.FE.START_MARKER)
            .append($.FE.END_MARKER);
        }

        // Set attributes.
        $link.attr(attrs);

        if (!$current_image) {
          editor.selection.restore();
        }
      }
      else {
        // We don't have any image selected.
        if (!$current_image) {
          // Remove current links.
          editor.format.remove('a');

          // Nothing is selected.
          if (editor.selection.isCollapsed()) {
            text = (text.length === 0 ? original_href : text);
            editor.html.insert('<a href="' + href + '">' + $.FE.START_MARKER + text + $.FE.END_MARKER + '</a>');
            editor.selection.restore();
          }
          else {
            if (text.length > 0 && text != editor.selection.text().replace(/\n/g, '')) {
              editor.selection.remove();
              editor.html.insert('<a href="' + href + '">' + $.FE.START_MARKER + text + $.FE.END_MARKER + '</a>');
              editor.selection.restore();
            }
            else {
              _split();

              // Add link.
              editor.format.apply('a', { href: href });
            }
          }
        }
        else {
          // Just wrap current image with a link.
          $current_image.wrap('<a href="' + href + '"></a>');
        }

        // Set attributes.
        var links = allSelected();
        for (var i = 0; i < links.length; i++) {
          $link = $(links[i]);
          $link.attr(attrs);
          $link.removeAttr('_moz_dirty');
        }

        // Show link edit if only one link.
        if (links.length == 1 && editor.$wp && !$current_image) {
          $(links[0])
            .prepend($.FE.START_MARKER)
            .append($.FE.END_MARKER);

          editor.selection.restore();
        }
      }

      // Hide popup and try to edit.
      if (!$current_image) {
        _edit();
      }
      else {
        var $pop = editor.popups.get('link.insert');
        $pop.find('input:focus').blur();
        editor.image.edit($current_image);
      }
    }

    function update () {
      _hideEditPopup();

      var link = get();
      if (link) {
        var $popup = editor.popups.get('link.insert');
        if (!$popup) $popup = _initInsertPopup();

        if (!editor.popups.isVisible('link.insert')) {
          editor.popups.refresh('link.insert');
          editor.selection.save();

          if (editor.helpers.isMobile()) {
            editor.events.disableBlur();
            editor.$el.blur();
            editor.events.enableBlur();
          }
        }

        editor.popups.setContainer('link.insert', $(editor.opts.scrollableContainer));
        var $ref = (editor.image ? editor.image.get() : null) || $(link);
        var left = $ref.offset().left + $ref.outerWidth() / 2;
        var top = $ref.offset().top + $ref.outerHeight();

        editor.popups.show('link.insert', left, top, $ref.outerHeight());
      }
    }

    function back () {
      var $current_image = editor.image ? editor.image.get() : null;
      if (!$current_image) {
        editor.events.disableBlur();
        editor.selection.restore();
        editor.events.enableBlur();

        var link = get();

        if (link && editor.$wp) {
          editor.selection.restore();
          _hideEditPopup();
          _edit();
        }
        else if (editor.$el.get(0).tagName == 'A') {
          editor.$el.focus();
          _edit();
        }
        else {
          editor.popups.hide('link.insert');
          editor.toolbar.showInline();
        }
      }
      else {
        editor.image.back();
      }
    }

    function imageLink () {
      var $current_image = editor.image ? editor.image.get() : null;

      if ($current_image) {
        var $popup = editor.popups.get('link.insert');
        if (!$popup) $popup = _initInsertPopup();

        _refreshInsertPopup(true);
        editor.popups.setContainer('link.insert', $(editor.opts.scrollableContainer));
        var left = $current_image.offset().left + $current_image.outerWidth() / 2;
        var top = $current_image.offset().top + $current_image.outerHeight();

        editor.popups.show('link.insert', left, top, $current_image.outerHeight());
      }
    }

    /**
     * Apply specific style.
     */
    function applyStyle (val, linkStyles, multipleStyles) {
      if (typeof multipleStyles == 'undefined') multipleStyles = editor.opts.linkMultipleStyles;
      if (typeof linkStyles == 'undefined') linkStyles = editor.opts.linkStyles;

      var link = get();
      if (!link) return false;

      // Remove multiple styles.
      if (!multipleStyles) {
        var styles = Object.keys(linkStyles);
        styles.splice(styles.indexOf(val), 1);
        $(link).removeClass(styles.join(' '));
      }

      $(link).toggleClass(val);

      _edit();
    }

    return {
      _init: _init,
      remove: remove,
      showInsertPopup: _showInsertPopup,
      usePredefined: usePredefined,
      insertCallback: insertCallback,
      insert: insert,
      update: update,
      get: get,
      allSelected: allSelected,
      back: back,
      imageLink: imageLink,
      applyStyle: applyStyle
    }
  }

  // Register the link command.
  $.FE.DefineIcon('insertLink', { NAME: 'link' });
  $.FE.RegisterShortcut($.FE.KEYCODE.K, 'insertLink', null, 'K');
  $.FE.RegisterCommand('insertLink', {
    title: 'Insert Link',
    undo: false,
    focus: true,
    refreshOnCallback: false,
    popup: true,
    callback: function () {
      if (!this.popups.isVisible('link.insert')) {
        this.link.showInsertPopup();
      }
      else {
        if (this.$el.find('.fr-marker')) {
          this.events.disableBlur();
          this.selection.restore();
        }
        this.popups.hide('link.insert');
      }
    },
    plugin: 'link'
  })

  $.FE.DefineIcon('linkOpen', { NAME: 'external-link' });
  $.FE.RegisterCommand('linkOpen', {
    title: 'Open Link',
    undo: false,
    refresh: function ($btn) {
      var link = this.link.get();
      if (link) {
        $btn.removeClass('fr-hidden');
      }
      else {
        $btn.addClass('fr-hidden');
      }
    },
    callback: function () {
      var link = this.link.get();
      if (link) {
        this.o_win.open(link.href);
      }
    }
  })

  $.FE.DefineIcon('linkEdit', { NAME: 'edit' });
  $.FE.RegisterCommand('linkEdit', {
    title: 'Edit Link',
    undo: false,
    refreshAfterCallback: false,
    callback: function () {
      this.link.update();
    },
    refresh: function ($btn) {
      var link = this.link.get();
      if (link) {
        $btn.removeClass('fr-hidden');
      }
      else {
        $btn.addClass('fr-hidden');
      }
    }
  })

  $.FE.DefineIcon('linkRemove', { NAME: 'unlink' });
  $.FE.RegisterCommand('linkRemove', {
    title: 'Unlink',
    callback: function () {
      this.link.remove();
    },
    refresh: function ($btn) {
      var link = this.link.get();
      if (link) {
        $btn.removeClass('fr-hidden');
      }
      else {
        $btn.addClass('fr-hidden');
      }
    }
  })

  $.FE.DefineIcon('linkBack', { NAME: 'arrow-left' });
  $.FE.RegisterCommand('linkBack', {
    title: 'Back',
    undo: false,
    focus: false,
    back: true,
    refreshAfterCallback: false,
    callback: function () {
      this.link.back();
    },
    refresh: function ($btn) {
      var link = this.link.get();
      var $current_image = this.image ? this.image.get() : null;
      if (!$current_image && !link && !this.opts.toolbarInline) {
        $btn.addClass('fr-hidden');
        $btn.next('.fr-separator').addClass('fr-hidden');
      }
      else {
        $btn.removeClass('fr-hidden');
        $btn.next('.fr-separator').removeClass('fr-hidden');
      }
    }
  });

  $.FE.DefineIcon('linkList', { NAME: 'search' });
  $.FE.RegisterCommand('linkList', {
    title: 'Choose Link',
    type: 'dropdown',
    focus: false,
    undo: false,
    refreshAfterCallback: false,
    html: function () {
      var c = '<ul class="fr-dropdown-list">';
      var options =  this.opts.linkList;
      for (var i = 0; i < options.length; i++) {
        c += '<li><a class="fr-command" data-cmd="linkList" data-param1="' + i + '">' + (options[i].displayText || options[i].text) + '</a></li>';
      }
      c += '</ul>';

      return c;
    },
    callback: function (cmd, val) {
      this.link.usePredefined(val);
    }
  })

  $.FE.RegisterCommand('linkInsert', {
    focus: false,
    refreshAfterCallback: false,
    callback: function () {
      this.link.insertCallback();
    },
    refresh: function ($btn) {
      var link = this.link.get();
      if (link) {
        $btn.text(this.language.translate('Update'));
      }
      else {
        $btn.text(this.language.translate('Insert'));
      }
    }
  })

  // Image link.
  $.FE.DefineIcon('imageLink', { NAME: 'link' })
  $.FE.RegisterCommand('imageLink', {
    title: 'Insert Link',
    undo: false,
    focus: false,
    callback: function () {
      this.link.imageLink();
    },
    refresh: function ($btn) {
      var link = this.link.get();
      var $prev;

      if (link) {
        $prev = $btn.prev();
        if ($prev.hasClass('fr-separator')) {
          $prev.removeClass('fr-hidden');
        }

        $btn.addClass('fr-hidden');
      }
      else {
        $prev = $btn.prev();
        if ($prev.hasClass('fr-separator')) {
          $prev.addClass('fr-hidden');
        }

        $btn.removeClass('fr-hidden');
      }
    }
  })

  // Link styles.
  $.FE.DefineIcon('linkStyle', { NAME: 'magic' })
  $.FE.RegisterCommand('linkStyle', {
    title: 'Style',
    type: 'dropdown',
    html: function () {
      var c = '<ul class="fr-dropdown-list">';
      var options =  this.opts.linkStyles;
      for (var cls in options) {
        if (options.hasOwnProperty(cls)) {
          c += '<li><a class="fr-command" data-cmd="linkStyle" data-param1="' + cls + '">' + this.language.translate(options[cls]) + '</a></li>';
        }
      }
      c += '</ul>';

      return c;
    },
    callback: function (cmd, val) {
      this.link.applyStyle(val);
    },
    refreshOnShow: function ($btn, $dropdown) {
      var link = this.link.get();

      if (link) {
        var $link = $(link);
        $dropdown.find('.fr-command').each (function () {
          var cls = $(this).data('param1');
          $(this).toggleClass('fr-active', $link.hasClass(cls));
        })
      }
    }
  })

}));

};


modules['helpcenter/primary/models/link-profile'] = function(require, exports, module, modules) {
module.exports = Backbone.Model.extend({
  /**
   * @author Prasanjit Singh <prasanjit.singh@kayako.com>
   * @param  {Object} options
   */
  initialize: function(options) {
    this.options = options;
  },

  /**
   * @author Prasanjit Singh <prasanjit.singh@kayako.com>
   * @return {Object}
   */
  url: function() {
    switch (this.options.query) {
      case 'connect':
        return _Payload.portalpath + 'api/v1/' + this.options.account + '/identity/link.json?include=*';
      case 'disconnect':
        return _Payload.portalpath + 'api/v1/identities/' + this.options.account + '/' + this.options.id + '.json?include=*';
      case 'verifyemail':
        return _Payload.portalpath + 'api/v1/profile/identities/emails/' + this.options.id + '/verification.json';
    }
  }
});
};


modules['helpcenter/primary/plugins/picker'] = function(require, exports, module, modules) {
$ = require('jquery');

/*!
 * pickadate.js v3.5.6, 2015/04/20
 * By Amsul, http://amsul.ca
 * Hosted on http://amsul.github.io/pickadate.js
 * Licensed under MIT
 */

(function ( factory ) {

    // AMD.
    if ( typeof define == 'function' && define.amd )
        define( 'picker', ['jquery'], factory )

    // Node.js/browserify.
    else if ( typeof exports == 'object' )
        module.exports = factory( require('jquery') )

    // Browser globals.
    else this.Picker = factory( jQuery )

}(function( $ ) {

var $window = $( window )
var $document = $( document )
var $html = $( document.documentElement )
var supportsTransitions = document.documentElement.style.transition != null


/**
 * The picker constructor that creates a blank picker.
 */
function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) {

    // If there’s no element, return the picker constructor.
    if ( !ELEMENT ) return PickerConstructor


    var
        IS_DEFAULT_THEME = false,


        // The state of the picker.
        STATE = {
            id: ELEMENT.id || 'P' + Math.abs( ~~(Math.random() * new Date()) )
        },


        // Merge the defaults and options passed.
        SETTINGS = COMPONENT ? $.extend( true, {}, COMPONENT.defaults, OPTIONS ) : OPTIONS || {},


        // Merge the default classes with the settings classes.
        CLASSES = $.extend( {}, PickerConstructor.klasses(), SETTINGS.klass ),


        // The element node wrapper into a jQuery object.
        $ELEMENT = $( ELEMENT ),


        // Pseudo picker constructor.
        PickerInstance = function() {
            return this.start()
        },


        // The picker prototype.
        P = PickerInstance.prototype = {

            constructor: PickerInstance,

            $node: $ELEMENT,


            /**
             * Initialize everything
             */
            start: function() {

                // If it’s already started, do nothing.
                if ( STATE && STATE.start ) return P


                // Update the picker states.
                STATE.methods = {}
                STATE.start = true
                STATE.open = false
                STATE.type = ELEMENT.type


                // Confirm focus state, convert into text input to remove UA stylings,
                // and set as readonly to prevent keyboard popup.
                ELEMENT.autofocus = ELEMENT == getActiveElement()
                ELEMENT.readOnly = !SETTINGS.editable
                ELEMENT.id = ELEMENT.id || STATE.id
                if ( ELEMENT.type != 'text' ) {
                    ELEMENT.type = 'text'
                }


                // Create a new picker component with the settings.
                P.component = new COMPONENT(P, SETTINGS)


                // Create the picker root and then prepare it.
                P.$root = $( '<div class="' + CLASSES.picker + '" id="' + ELEMENT.id + '_root" />' )
                prepareElementRoot()


                // Create the picker holder and then prepare it.
                P.$holder = $( createWrappedComponent() ).appendTo( P.$root )
                prepareElementHolder()


                // If there’s a format for the hidden input element, create the element.
                if ( SETTINGS.formatSubmit ) {
                    prepareElementHidden()
                }


                // Prepare the input element.
                prepareElement()


                // Insert the hidden input as specified in the settings.
                if ( SETTINGS.containerHidden ) $( SETTINGS.containerHidden ).append( P._hidden )
                else $ELEMENT.after( P._hidden )


                // Insert the root as specified in the settings.
                if ( SETTINGS.container ) $( SETTINGS.container ).append( P.$root )
                else $ELEMENT.after( P.$root )


                // Bind the default component and settings events.
                P.on({
                    start: P.component.onStart,
                    render: P.component.onRender,
                    stop: P.component.onStop,
                    open: P.component.onOpen,
                    close: P.component.onClose,
                    set: P.component.onSet
                }).on({
                    start: SETTINGS.onStart,
                    render: SETTINGS.onRender,
                    stop: SETTINGS.onStop,
                    open: SETTINGS.onOpen,
                    close: SETTINGS.onClose,
                    set: SETTINGS.onSet
                })


                // Once we’re all set, check the theme in use.
                IS_DEFAULT_THEME = isUsingDefaultTheme( P.$holder[0] )


                // If the element has autofocus, open the picker.
                if ( ELEMENT.autofocus ) {
                    P.open()
                }


                // Trigger queued the “start” and “render” events.
                return P.trigger( 'start' ).trigger( 'render' )
            }, //start


            /**
             * Render a new picker
             */
            render: function( entireComponent ) {

                // Insert a new component holder in the root or box.
                if ( entireComponent ) {
                    P.$holder = $( createWrappedComponent() )
                    prepareElementHolder()
                    P.$root.html( P.$holder )
                }
                else P.$root.find( '.' + CLASSES.box ).html( P.component.nodes( STATE.open ) )

                // Trigger the queued “render” events.
                return P.trigger( 'render' )
            }, //render


            /**
             * Destroy everything
             */
            stop: function() {

                // If it’s already stopped, do nothing.
                if ( !STATE.start ) return P

                // Then close the picker.
                P.close()

                // Remove the hidden field.
                if ( P._hidden ) {
                    P._hidden.parentNode.removeChild( P._hidden )
                }

                // Remove the root.
                P.$root.remove()

                // Remove the input class, remove the stored data, and unbind
                // the events (after a tick for IE - see `P.close`).
                $ELEMENT.removeClass( CLASSES.input ).removeData( NAME )
                setTimeout( function() {
                    $ELEMENT.off( '.' + STATE.id )
                }, 0)

                // Restore the element state
                ELEMENT.type = STATE.type
                ELEMENT.readOnly = false

                // Trigger the queued “stop” events.
                P.trigger( 'stop' )

                // Reset the picker states.
                STATE.methods = {}
                STATE.start = false

                return P
            }, //stop


            /**
             * Open up the picker
             */
            open: function( dontGiveFocus ) {

                // If it’s already open, do nothing.
                if ( STATE.open ) return P

                // Add the “active” class.
                $ELEMENT.addClass( CLASSES.active )
                aria( ELEMENT, 'expanded', true )

                // * A Firefox bug, when `html` has `overflow:hidden`, results in
                //   killing transitions :(. So add the “opened” state on the next tick.
                //   Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=625289
                setTimeout( function() {

                    // Add the “opened” class to the picker root.
                    P.$root.addClass( CLASSES.opened )
                    aria( P.$root[0], 'hidden', false )

                }, 0 )

                // If we have to give focus, bind the element and doc events.
                if ( dontGiveFocus !== false ) {

                    // Set it as open.
                    STATE.open = true

                    // Prevent the page from scrolling.
                    if ( IS_DEFAULT_THEME ) {
                        $html.
                            css( 'overflow', 'hidden' ).
                            css( 'padding-right', '+=' + getScrollbarWidth() )
                    }

                    // Pass focus to the root element’s jQuery object.
                    focusPickerOnceOpened()

                    // Bind the document events.
                    $document.on( 'click.' + STATE.id + ' focusin.' + STATE.id, function( event ) {

                        var target = event.target

                        // If the target of the event is not the element, close the picker picker.
                        // * Don’t worry about clicks or focusins on the root because those don’t bubble up.
                        //   Also, for Firefox, a click on an `option` element bubbles up directly
                        //   to the doc. So make sure the target wasn't the doc.
                        // * In Firefox stopPropagation() doesn’t prevent right-click events from bubbling,
                        //   which causes the picker to unexpectedly close when right-clicking it. So make
                        //   sure the event wasn’t a right-click.
                        if ( target != ELEMENT && target != document && event.which != 3 ) {

                            // If the target was the holder that covers the screen,
                            // keep the element focused to maintain tabindex.
                            P.close( target === P.$holder[0] )
                        }

                    }).on( 'keydown.' + STATE.id, function( event ) {

                        var
                            // Get the keycode.
                            keycode = event.keyCode,

                            // Translate that to a selection change.
                            keycodeToMove = P.component.key[ keycode ],

                            // Grab the target.
                            target = event.target


                        // On escape, close the picker and give focus.
                        if ( keycode == 27 ) {
                            P.close( true )
                        }


                        // Check if there is a key movement or “enter” keypress on the element.
                        else if ( target == P.$holder[0] && ( keycodeToMove || keycode == 13 ) ) {

                            // Prevent the default action to stop page movement.
                            event.preventDefault()

                            // Trigger the key movement action.
                            if ( keycodeToMove ) {
                                PickerConstructor._.trigger( P.component.key.go, P, [ PickerConstructor._.trigger( keycodeToMove ) ] )
                            }

                            // On “enter”, if the highlighted item isn’t disabled, set the value and close.
                            else if ( !P.$root.find( '.' + CLASSES.highlighted ).hasClass( CLASSES.disabled ) ) {
                                P.set( 'select', P.component.item.highlight )
                                if ( SETTINGS.closeOnSelect ) {
                                    P.close( true )
                                }
                            }
                        }


                        // If the target is within the root and “enter” is pressed,
                        // prevent the default action and trigger a click on the target instead.
                        else if ( $.contains( P.$root[0], target ) && keycode == 13 ) {
                            event.preventDefault()
                            target.click()
                        }
                    })
                }

                // Trigger the queued “open” events.
                return P.trigger( 'open' )
            }, //open


            /**
             * Close the picker
             */
            close: function( giveFocus ) {

                // If we need to give focus, do it before changing states.
                if ( giveFocus ) {
                    if ( SETTINGS.editable ) {
                        ELEMENT.focus()
                    }
                    else {
                        // ....ah yes! It would’ve been incomplete without a crazy workaround for IE :|
                        // The focus is triggered *after* the close has completed - causing it
                        // to open again. So unbind and rebind the event at the next tick.
                        P.$holder.off( 'focus.toOpen' ).focus()
                        setTimeout( function() {
                            P.$holder.on( 'focus.toOpen', handleFocusToOpenEvent )
                        }, 0 )
                    }
                }

                // Remove the “active” class.
                $ELEMENT.removeClass( CLASSES.active )
                aria( ELEMENT, 'expanded', false )

                // * A Firefox bug, when `html` has `overflow:hidden`, results in
                //   killing transitions :(. So remove the “opened” state on the next tick.
                //   Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=625289
                setTimeout( function() {

                    // Remove the “opened” and “focused” class from the picker root.
                    P.$root.removeClass( CLASSES.opened + ' ' + CLASSES.focused )
                    aria( P.$root[0], 'hidden', true )

                }, 0 )

                // If it’s already closed, do nothing more.
                if ( !STATE.open ) return P

                // Set it as closed.
                STATE.open = false

                // Allow the page to scroll.
                if ( IS_DEFAULT_THEME ) {
                    $html.
                        css( 'overflow', '' ).
                        css( 'padding-right', '-=' + getScrollbarWidth() )
                }

                // Unbind the document events.
                $document.off( '.' + STATE.id )

                // Trigger the queued “close” events.
                return P.trigger( 'close' )
            }, //close


            /**
             * Clear the values
             */
            clear: function( options ) {
                return P.set( 'clear', null, options )
            }, //clear


            /**
             * Set something
             */
            set: function( thing, value, options ) {

                var thingItem, thingValue,
                    thingIsObject = $.isPlainObject( thing ),
                    thingObject = thingIsObject ? thing : {}

                // Make sure we have usable options.
                options = thingIsObject && $.isPlainObject( value ) ? value : options || {}

                if ( thing ) {

                    // If the thing isn’t an object, make it one.
                    if ( !thingIsObject ) {
                        thingObject[ thing ] = value
                    }

                    // Go through the things of items to set.
                    for ( thingItem in thingObject ) {

                        // Grab the value of the thing.
                        thingValue = thingObject[ thingItem ]

                        // First, if the item exists and there’s a value, set it.
                        if ( thingItem in P.component.item ) {
                            if ( thingValue === undefined ) thingValue = null
                            P.component.set( thingItem, thingValue, options )
                        }

                        // Then, check to update the element value and broadcast a change.
                        if ( thingItem == 'select' || thingItem == 'clear' ) {
                            $ELEMENT.
                                val( thingItem == 'clear' ? '' : P.get( thingItem, SETTINGS.format ) ).
                                trigger( 'change' )
                        }
                    }

                    // Render a new picker.
                    P.render()
                }

                // When the method isn’t muted, trigger queued “set” events and pass the `thingObject`.
                return options.muted ? P : P.trigger( 'set', thingObject )
            }, //set


            /**
             * Get something
             */
            get: function( thing, format ) {

                // Make sure there’s something to get.
                thing = thing || 'value'

                // If a picker state exists, return that.
                if ( STATE[ thing ] != null ) {
                    return STATE[ thing ]
                }

                // Return the submission value, if that.
                if ( thing == 'valueSubmit' ) {
                    if ( P._hidden ) {
                        return P._hidden.value
                    }
                    thing = 'value'
                }

                // Return the value, if that.
                if ( thing == 'value' ) {
                    return ELEMENT.value
                }

                // Check if a component item exists, return that.
                if ( thing in P.component.item ) {
                    if ( typeof format == 'string' ) {
                        var thingValue = P.component.get( thing )
                        return thingValue ?
                            PickerConstructor._.trigger(
                                P.component.formats.toString,
                                P.component,
                                [ format, thingValue ]
                            ) : ''
                    }
                    return P.component.get( thing )
                }
            }, //get



            /**
             * Bind events on the things.
             */
            on: function( thing, method, internal ) {

                var thingName, thingMethod,
                    thingIsObject = $.isPlainObject( thing ),
                    thingObject = thingIsObject ? thing : {}

                if ( thing ) {

                    // If the thing isn’t an object, make it one.
                    if ( !thingIsObject ) {
                        thingObject[ thing ] = method
                    }

                    // Go through the things to bind to.
                    for ( thingName in thingObject ) {

                        // Grab the method of the thing.
                        thingMethod = thingObject[ thingName ]

                        // If it was an internal binding, prefix it.
                        if ( internal ) {
                            thingName = '_' + thingName
                        }

                        // Make sure the thing methods collection exists.
                        STATE.methods[ thingName ] = STATE.methods[ thingName ] || []

                        // Add the method to the relative method collection.
                        STATE.methods[ thingName ].push( thingMethod )
                    }
                }

                return P
            }, //on



            /**
             * Unbind events on the things.
             */
            off: function() {
                var i, thingName,
                    names = arguments;
                for ( i = 0, namesCount = names.length; i < namesCount; i += 1 ) {
                    thingName = names[i]
                    if ( thingName in STATE.methods ) {
                        delete STATE.methods[thingName]
                    }
                }
                return P
            },


            /**
             * Fire off method events.
             */
            trigger: function( name, data ) {
                var _trigger = function( name ) {
                    var methodList = STATE.methods[ name ]
                    if ( methodList ) {
                        methodList.map( function( method ) {
                            PickerConstructor._.trigger( method, P, [ data ] )
                        })
                    }
                }
                _trigger( '_' + name )
                _trigger( name )
                return P
            } //trigger
        } //PickerInstance.prototype


    /**
     * Wrap the picker holder components together.
     */
    function createWrappedComponent() {

        // Create a picker wrapper holder
        return PickerConstructor._.node( 'div',

            // Create a picker wrapper node
            PickerConstructor._.node( 'div',

                // Create a picker frame
                PickerConstructor._.node( 'div',

                    // Create a picker box node
                    PickerConstructor._.node( 'div',

                        // Create the components nodes.
                        P.component.nodes( STATE.open ),

                        // The picker box class
                        CLASSES.box
                    ),

                    // Picker wrap class
                    CLASSES.wrap
                ),

                // Picker frame class
                CLASSES.frame
            ),

            // Picker holder class
            CLASSES.holder,

            'tabindex="-1"'
        ) //endreturn
    } //createWrappedComponent



    /**
     * Prepare the input element with all bindings.
     */
    function prepareElement() {

        $ELEMENT.

            // Store the picker data by component name.
            data(NAME, P).

            // Add the “input” class name.
            addClass(CLASSES.input).

            // If there’s a `data-value`, update the value of the element.
            val( $ELEMENT.data('value') ?
                P.get('select', SETTINGS.format) :
                ELEMENT.value
            )


        // Only bind keydown events if the element isn’t editable.
        if ( !SETTINGS.editable ) {

            $ELEMENT.

                // On focus/click, open the picker.
                on( 'focus.' + STATE.id + ' click.' + STATE.id, function(event) {
                    event.preventDefault()
                    P.open()
                }).

                // Handle keyboard event based on the picker being opened or not.
                on( 'keydown.' + STATE.id, handleKeydownEvent )
        }


        // Update the aria attributes.
        aria(ELEMENT, {
            haspopup: true,
            expanded: false,
            readonly: false,
            owns: ELEMENT.id + '_root'
        })
    }


    /**
     * Prepare the root picker element with all bindings.
     */
    function prepareElementRoot() {
        aria( P.$root[0], 'hidden', true )
    }


     /**
      * Prepare the holder picker element with all bindings.
      */
    function prepareElementHolder() {

        P.$holder.

            on({

                // For iOS8.
                keydown: handleKeydownEvent,

                'focus.toOpen': handleFocusToOpenEvent,

                blur: function() {
                    // Remove the “target” class.
                    $ELEMENT.removeClass( CLASSES.target )
                },

                // When something within the holder is focused, stop from bubbling
                // to the doc and remove the “focused” state from the root.
                focusin: function( event ) {
                    P.$root.removeClass( CLASSES.focused )
                    event.stopPropagation()
                },

                // When something within the holder is clicked, stop it
                // from bubbling to the doc.
                'mousedown click': function( event ) {

                    var target = event.target

                    // Make sure the target isn’t the root holder so it can bubble up.
                    if ( target != P.$holder[0] ) {

                        event.stopPropagation()

                        // * For mousedown events, cancel the default action in order to
                        //   prevent cases where focus is shifted onto external elements
                        //   when using things like jQuery mobile or MagnificPopup (ref: #249 & #120).
                        //   Also, for Firefox, don’t prevent action on the `option` element.
                        if ( event.type == 'mousedown' && !$( target ).is( 'input, select, textarea, button, option' )) {

                            event.preventDefault()

                            // Re-focus onto the holder so that users can click away
                            // from elements focused within the picker.
                            P.$holder[0].focus()
                        }
                    }
                }

            }).

            // If there’s a click on an actionable element, carry out the actions.
            on( 'click', '[data-pick], [data-nav], [data-clear], [data-close]', function() {

                var $target = $( this ),
                    targetData = $target.data(),
                    targetDisabled = $target.hasClass( CLASSES.navDisabled ) || $target.hasClass( CLASSES.disabled ),

                    // * For IE, non-focusable elements can be active elements as well
                    //   (http://stackoverflow.com/a/2684561).
                    activeElement = getActiveElement()
                    activeElement = activeElement && ( activeElement.type || activeElement.href )

                // If it’s disabled or nothing inside is actively focused, re-focus the element.
                if ( targetDisabled || activeElement && !$.contains( P.$root[0], activeElement ) ) {
                    P.$holder[0].focus()
                }

                // If something is superficially changed, update the `highlight` based on the `nav`.
                if ( !targetDisabled && targetData.nav ) {
                    P.set( 'highlight', P.component.item.highlight, { nav: targetData.nav } )
                }

                // If something is picked, set `select` then close with focus.
                else if ( !targetDisabled && 'pick' in targetData ) {
                    P.set( 'select', targetData.pick )
                    if ( SETTINGS.closeOnSelect ) {
                        P.close( true )
                    }
                }

                // If a “clear” button is pressed, empty the values and close with focus.
                else if ( targetData.clear ) {
                    P.clear()
                    if ( SETTINGS.closeOnClear ) {
                        P.close( true )
                    }
                }

                else if ( targetData.close ) {
                    P.close( true )
                }

            }) //P.$holder

    }


     /**
      * Prepare the hidden input element along with all bindings.
      */
    function prepareElementHidden() {

        var name

        if ( SETTINGS.hiddenName === true ) {
            name = ELEMENT.name
            ELEMENT.name = ''
        }
        else {
            name = [
                typeof SETTINGS.hiddenPrefix == 'string' ? SETTINGS.hiddenPrefix : '',
                typeof SETTINGS.hiddenSuffix == 'string' ? SETTINGS.hiddenSuffix : '_submit'
            ]
            name = name[0] + ELEMENT.name + name[1]
        }

        P._hidden = $(
            '<input ' +
            'type=hidden ' +

            // Create the name using the original input’s with a prefix and suffix.
            'name="' + name + '"' +

            // If the element has a value, set the hidden value as well.
            (
                $ELEMENT.data('value') || ELEMENT.value ?
                    ' value="' + P.get('select', SETTINGS.formatSubmit) + '"' :
                    ''
            ) +
            '>'
        )[0]

        $ELEMENT.

            // If the value changes, update the hidden input with the correct format.
            on('change.' + STATE.id, function() {
                P._hidden.value = ELEMENT.value ?
                    P.get('select', SETTINGS.formatSubmit) :
                    ''
            })
    }


    // Wait for transitions to end before focusing the holder. Otherwise, while
    // using the `container` option, the view jumps to the container.
    function focusPickerOnceOpened() {

        if (IS_DEFAULT_THEME && supportsTransitions) {
            P.$holder.find('.' + CLASSES.frame).one('transitionend', function() {
                P.$holder[0].focus()
            })
        }
        else {
            P.$holder[0].focus()
        }
    }


    function handleFocusToOpenEvent(event) {

        // Stop the event from propagating to the doc.
        event.stopPropagation()

        // Add the “target” class.
        $ELEMENT.addClass( CLASSES.target )

        // Add the “focused” class to the root.
        P.$root.addClass( CLASSES.focused )

        // And then finally open the picker.
        P.open()
    }


    // For iOS8.
    function handleKeydownEvent( event ) {

        var keycode = event.keyCode,

            // Check if one of the delete keys was pressed.
            isKeycodeDelete = /^(8|46)$/.test(keycode)

        // For some reason IE clears the input value on “escape”.
        if ( keycode == 27 ) {
            P.close( true )
            return false
        }

        // Check if `space` or `delete` was pressed or the picker is closed with a key movement.
        if ( keycode == 32 || isKeycodeDelete || !STATE.open && P.component.key[keycode] ) {

            // Prevent it from moving the page and bubbling to doc.
            event.preventDefault()
            event.stopPropagation()

            // If `delete` was pressed, clear the values and close the picker.
            // Otherwise open the picker.
            if ( isKeycodeDelete ) { P.clear().close() }
            else { P.open() }
        }
    }


    // Return a new picker instance.
    return new PickerInstance()
} //PickerConstructor



/**
 * The default classes and prefix to use for the HTML classes.
 */
PickerConstructor.klasses = function( prefix ) {
    prefix = prefix || 'picker'
    return {

        picker: prefix,
        opened: prefix + '--opened',
        focused: prefix + '--focused',

        input: prefix + '__input',
        active: prefix + '__input--active',
        target: prefix + '__input--target',

        holder: prefix + '__holder',

        frame: prefix + '__frame',
        wrap: prefix + '__wrap',

        box: prefix + '__box'
    }
} //PickerConstructor.klasses



/**
 * Check if the default theme is being used.
 */
function isUsingDefaultTheme( element ) {

    var theme,
        prop = 'position'

    // For IE.
    if ( element.currentStyle ) {
        theme = element.currentStyle[prop]
    }

    // For normal browsers.
    else if ( window.getComputedStyle ) {
        theme = getComputedStyle( element )[prop]
    }

    return theme == 'fixed'
}



/**
 * Get the width of the browser’s scrollbar.
 * Taken from: https://github.com/VodkaBears/Remodal/blob/master/src/jquery.remodal.js
 */
function getScrollbarWidth() {

    if ( $html.height() <= $window.height() ) {
        return 0
    }

    var $outer = $( '<div style="visibility:hidden;width:100px" />' ).
        appendTo( 'body' )

    // Get the width without scrollbars.
    var widthWithoutScroll = $outer[0].offsetWidth

    // Force adding scrollbars.
    $outer.css( 'overflow', 'scroll' )

    // Add the inner div.
    var $inner = $( '<div style="width:100%" />' ).appendTo( $outer )

    // Get the width with scrollbars.
    var widthWithScroll = $inner[0].offsetWidth

    // Remove the divs.
    $outer.remove()

    // Return the difference between the widths.
    return widthWithoutScroll - widthWithScroll
}



/**
 * PickerConstructor helper methods.
 */
PickerConstructor._ = {

    /**
     * Create a group of nodes. Expects:
     * `
        {
            min:    {Integer},
            max:    {Integer},
            i:      {Integer},
            node:   {String},
            item:   {Function}
        }
     * `
     */
    group: function( groupObject ) {

        var
            // Scope for the looped object
            loopObjectScope,

            // Create the nodes list
            nodesList = '',

            // The counter starts from the `min`
            counter = PickerConstructor._.trigger( groupObject.min, groupObject )


        // Loop from the `min` to `max`, incrementing by `i`
        for ( ; counter <= PickerConstructor._.trigger( groupObject.max, groupObject, [ counter ] ); counter += groupObject.i ) {

            // Trigger the `item` function within scope of the object
            loopObjectScope = PickerConstructor._.trigger( groupObject.item, groupObject, [ counter ] )

            // Splice the subgroup and create nodes out of the sub nodes
            nodesList += PickerConstructor._.node(
                groupObject.node,
                loopObjectScope[ 0 ],   // the node
                loopObjectScope[ 1 ],   // the classes
                loopObjectScope[ 2 ]    // the attributes
            )
        }

        // Return the list of nodes
        return nodesList
    }, //group


    /**
     * Create a dom node string
     */
    node: function( wrapper, item, klass, attribute ) {

        // If the item is false-y, just return an empty string
        if ( !item ) return ''

        // If the item is an array, do a join
        item = $.isArray( item ) ? item.join( '' ) : item

        // Check for the class
        klass = klass ? ' class="' + klass + '"' : ''

        // Check for any attributes
        attribute = attribute ? ' ' + attribute : ''

        // Return the wrapped item
        return '<' + wrapper + klass + attribute + '>' + item + '</' + wrapper + '>'
    }, //node


    /**
     * Lead numbers below 10 with a zero.
     */
    lead: function( number ) {
        return ( number < 10 ? '0': '' ) + number
    },


    /**
     * Trigger a function otherwise return the value.
     */
    trigger: function( callback, scope, args ) {
        return typeof callback == 'function' ? callback.apply( scope, args || [] ) : callback
    },


    /**
     * If the second character is a digit, length is 2 otherwise 1.
     */
    digits: function( string ) {
        return ( /\d/ ).test( string[ 1 ] ) ? 2 : 1
    },


    /**
     * Tell if something is a date object.
     */
    isDate: function( value ) {
        return {}.toString.call( value ).indexOf( 'Date' ) > -1 && this.isInteger( value.getDate() )
    },


    /**
     * Tell if something is an integer.
     */
    isInteger: function( value ) {
        return {}.toString.call( value ).indexOf( 'Number' ) > -1 && value % 1 === 0
    },


    /**
     * Create ARIA attribute strings.
     */
    ariaAttr: ariaAttr
} //PickerConstructor._



/**
 * Extend the picker with a component and defaults.
 */
PickerConstructor.extend = function( name, Component ) {

    // Extend jQuery.
    $.fn[ name ] = function( options, action ) {

        // Grab the component data.
        var componentData = this.data( name )

        // If the picker is requested, return the data object.
        if ( options == 'picker' ) {
            return componentData
        }

        // If the component data exists and `options` is a string, carry out the action.
        if ( componentData && typeof options == 'string' ) {
            return PickerConstructor._.trigger( componentData[ options ], componentData, [ action ] )
        }

        // Otherwise go through each matched element and if the component
        // doesn’t exist, create a new picker using `this` element
        // and merging the defaults and options with a deep copy.
        return this.each( function() {
            var $this = $( this )
            if ( !$this.data( name ) ) {
                new PickerConstructor( this, name, Component, options )
            }
        })
    }

    // Set the defaults.
    $.fn[ name ].defaults = Component.defaults
} //PickerConstructor.extend



function aria(element, attribute, value) {
    if ( $.isPlainObject(attribute) ) {
        for ( var key in attribute ) {
            ariaSet(element, key, attribute[key])
        }
    }
    else {
        ariaSet(element, attribute, value)
    }
}
function ariaSet(element, attribute, value) {
    element.setAttribute(
        (attribute == 'role' ? '' : 'aria-') + attribute,
        value
    )
}
function ariaAttr(attribute, data) {
    if ( !$.isPlainObject(attribute) ) {
        attribute = { attribute: data }
    }
    data = ''
    for ( var key in attribute ) {
        var attr = (key == 'role' ? '' : 'aria-') + key,
            attrVal = attribute[key]
        data += attrVal == null ? '' : attr + '="' + attribute[key] + '"'
    }
    return data
}

// IE8 bug throws an error for activeElements within iframes.
function getActiveElement() {
    try {
        return document.activeElement
    } catch ( err ) { }
}



// Expose the picker constructor.
return PickerConstructor


}));




};


modules['helpcenter/primary/plugins/moment'] = function(require, exports, module, modules) {
;(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
		typeof define === 'function' && define.amd ? define(factory) :
			global.moment = factory()
}(this, (function () { 'use strict';

	var hookCallback;

	function hooks () {
		return hookCallback.apply(null, arguments);
	}

// This is done to register the method called with moment()
// without creating circular dependencies.
	function setHookCallback (callback) {
		hookCallback = callback;
	}

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

	function isObject(input) {
		// IE8 will treat undefined and null as object if it wasn't for
		// input != null
		return input != null && Object.prototype.toString.call(input) === '[object Object]';
	}

	function isObjectEmpty(obj) {
		var k;
		for (k in obj) {
			// even if its not own property I'd still call it non-empty
			return false;
		}
		return true;
	}

	function isNumber(input) {
		return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]';
	}

	function isDate(input) {
		return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]';
	}

	function map(arr, fn) {
		var res = [], i;
		for (i = 0; i < arr.length; ++i) {
			res.push(fn(arr[i], i));
		}
		return res;
	}

	function hasOwnProp(a, b) {
		return Object.prototype.hasOwnProperty.call(a, b);
	}

	function extend(a, b) {
		for (var i in b) {
			if (hasOwnProp(b, i)) {
				a[i] = b[i];
			}
		}

		if (hasOwnProp(b, 'toString')) {
			a.toString = b.toString;
		}

		if (hasOwnProp(b, 'valueOf')) {
			a.valueOf = b.valueOf;
		}

		return a;
	}

	function createUTC (input, format, locale, strict) {
		return createLocalOrUTC(input, format, locale, strict, true).utc();
	}

	function defaultParsingFlags() {
		// We need to deep clone this object.
		return {
			empty           : false,
			unusedTokens    : [],
			unusedInput     : [],
			overflow        : -2,
			charsLeftOver   : 0,
			nullInput       : false,
			invalidMonth    : null,
			invalidFormat   : false,
			userInvalidated : false,
			iso             : false,
			parsedDateParts : [],
			meridiem        : null
		};
	}

	function getParsingFlags(m) {
		if (m._pf == null) {
			m._pf = defaultParsingFlags();
		}
		return m._pf;
	}

	var some;
	if (Array.prototype.some) {
		some = Array.prototype.some;
	} else {
		some = function (fun) {
			var t = Object(this);
			var len = t.length >>> 0;

			for (var i = 0; i < len; i++) {
				if (i in t && fun.call(this, t[i], i, t)) {
					return true;
				}
			}

			return false;
		};
	}

	var some$1 = some;

	function isValid(m) {
		if (m._isValid == null) {
			var flags = getParsingFlags(m);
			var parsedParts = some$1.call(flags.parsedDateParts, function (i) {
				return i != null;
			});
			var isNowValid = !isNaN(m._d.getTime()) &&
				flags.overflow < 0 &&
				!flags.empty &&
				!flags.invalidMonth &&
				!flags.invalidWeekday &&
				!flags.nullInput &&
				!flags.invalidFormat &&
				!flags.userInvalidated &&
				(!flags.meridiem || (flags.meridiem && parsedParts));

			if (m._strict) {
				isNowValid = isNowValid &&
					flags.charsLeftOver === 0 &&
					flags.unusedTokens.length === 0 &&
					flags.bigHour === undefined;
			}

			if (Object.isFrozen == null || !Object.isFrozen(m)) {
				m._isValid = isNowValid;
			}
			else {
				return isNowValid;
			}
		}
		return m._isValid;
	}

	function createInvalid (flags) {
		var m = createUTC(NaN);
		if (flags != null) {
			extend(getParsingFlags(m), flags);
		}
		else {
			getParsingFlags(m).userInvalidated = true;
		}

		return m;
	}

	function isUndefined(input) {
		return input === void 0;
	}

// Plugins that add properties should also add the key here (null value),
// so we can properly clone ourselves.
	var momentProperties = hooks.momentProperties = [];

	function copyConfig(to, from) {
		var i, prop, val;

		if (!isUndefined(from._isAMomentObject)) {
			to._isAMomentObject = from._isAMomentObject;
		}
		if (!isUndefined(from._i)) {
			to._i = from._i;
		}
		if (!isUndefined(from._f)) {
			to._f = from._f;
		}
		if (!isUndefined(from._l)) {
			to._l = from._l;
		}
		if (!isUndefined(from._strict)) {
			to._strict = from._strict;
		}
		if (!isUndefined(from._tzm)) {
			to._tzm = from._tzm;
		}
		if (!isUndefined(from._isUTC)) {
			to._isUTC = from._isUTC;
		}
		if (!isUndefined(from._offset)) {
			to._offset = from._offset;
		}
		if (!isUndefined(from._pf)) {
			to._pf = getParsingFlags(from);
		}
		if (!isUndefined(from._locale)) {
			to._locale = from._locale;
		}

		if (momentProperties.length > 0) {
			for (i in momentProperties) {
				prop = momentProperties[i];
				val = from[prop];
				if (!isUndefined(val)) {
					to[prop] = val;
				}
			}
		}

		return to;
	}

	var updateInProgress = false;

// Moment prototype object
	function Moment(config) {
		copyConfig(this, config);
		this._d = new Date(config._d != null ? config._d.getTime() : NaN);
		if (!this.isValid()) {
			this._d = new Date(NaN);
		}
		// Prevent infinite loop in case updateOffset creates new moment
		// objects.
		if (updateInProgress === false) {
			updateInProgress = true;
			hooks.updateOffset(this);
			updateInProgress = false;
		}
	}

	function isMoment (obj) {
		return obj instanceof Moment || (obj != null && obj._isAMomentObject != null);
	}

	function absFloor (number) {
		if (number < 0) {
			// -0 -> 0
			return Math.ceil(number) || 0;
		} else {
			return Math.floor(number);
		}
	}

	function toInt(argumentForCoercion) {
		var coercedNumber = +argumentForCoercion,
			value = 0;

		if (coercedNumber !== 0 && isFinite(coercedNumber)) {
			value = absFloor(coercedNumber);
		}

		return value;
	}

// compare two arrays, return the number of differences
	function compareArrays(array1, array2, dontConvert) {
		var len = Math.min(array1.length, array2.length),
			lengthDiff = Math.abs(array1.length - array2.length),
			diffs = 0,
			i;
		for (i = 0; i < len; i++) {
			if ((dontConvert && array1[i] !== array2[i]) ||
				(!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) {
				diffs++;
			}
		}
		return diffs + lengthDiff;
	}

	function warn(msg) {
		if (hooks.suppressDeprecationWarnings === false &&
			(typeof console !==  'undefined') && console.warn) {
			console.warn('Deprecation warning: ' + msg);
		}
	}

	function deprecate(msg, fn) {
		var firstTime = true;

		return extend(function () {
			if (hooks.deprecationHandler != null) {
				hooks.deprecationHandler(null, msg);
			}
			if (firstTime) {
				var args = [];
				var arg;
				for (var i = 0; i < arguments.length; i++) {
					arg = '';
					if (typeof arguments[i] === 'object') {
						arg += '\n[' + i + '] ';
						for (var key in arguments[0]) {
							arg += key + ': ' + arguments[0][key] + ', ';
						}
						arg = arg.slice(0, -2); // Remove trailing comma and space
					} else {
						arg = arguments[i];
					}
					args.push(arg);
				}
				warn(msg + '\nArguments: ' + Array.prototype.slice.call(args).join('') + '\n' + (new Error()).stack);
				firstTime = false;
			}
			return fn.apply(this, arguments);
		}, fn);
	}

	var deprecations = {};

	function deprecateSimple(name, msg) {
		if (hooks.deprecationHandler != null) {
			hooks.deprecationHandler(name, msg);
		}
		if (!deprecations[name]) {
			warn(msg);
			deprecations[name] = true;
		}
	}

	hooks.suppressDeprecationWarnings = false;
	hooks.deprecationHandler = null;

	function isFunction(input) {
		return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]';
	}

	function set (config) {
		var prop, i;
		for (i in config) {
			prop = config[i];
			if (isFunction(prop)) {
				this[i] = prop;
			} else {
				this['_' + i] = prop;
			}
		}
		this._config = config;
		// Lenient ordinal parsing accepts just a number in addition to
		// number + (possibly) stuff coming from _ordinalParseLenient.
		this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + (/\d{1,2}/).source);
	}

	function mergeConfigs(parentConfig, childConfig) {
		var res = extend({}, parentConfig), prop;
		for (prop in childConfig) {
			if (hasOwnProp(childConfig, prop)) {
				if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) {
					res[prop] = {};
					extend(res[prop], parentConfig[prop]);
					extend(res[prop], childConfig[prop]);
				} else if (childConfig[prop] != null) {
					res[prop] = childConfig[prop];
				} else {
					delete res[prop];
				}
			}
		}
		for (prop in parentConfig) {
			if (hasOwnProp(parentConfig, prop) &&
				!hasOwnProp(childConfig, prop) &&
				isObject(parentConfig[prop])) {
				// make sure changes to properties don't modify parent config
				res[prop] = extend({}, res[prop]);
			}
		}
		return res;
	}

	function Locale(config) {
		if (config != null) {
			this.set(config);
		}
	}

	var keys;

	if (Object.keys) {
		keys = Object.keys;
	} else {
		keys = function (obj) {
			var i, res = [];
			for (i in obj) {
				if (hasOwnProp(obj, i)) {
					res.push(i);
				}
			}
			return res;
		};
	}

	var keys$1 = keys;

	var defaultCalendar = {
		sameDay : '[Today at] LT',
		nextDay : '[Tomorrow at] LT',
		nextWeek : 'dddd [at] LT',
		lastDay : '[Yesterday at] LT',
		lastWeek : '[Last] dddd [at] LT',
		sameElse : 'L'
	};

	function calendar (key, mom, now) {
		var output = this._calendar[key] || this._calendar['sameElse'];
		return isFunction(output) ? output.call(mom, now) : output;
	}

	var defaultLongDateFormat = {
		LTS  : 'h:mm:ss A',
		LT   : 'h:mm A',
		L    : 'MM/DD/YYYY',
		LL   : 'MMMM D, YYYY',
		LLL  : 'MMMM D, YYYY h:mm A',
		LLLL : 'dddd, MMMM D, YYYY h:mm A'
	};

	function longDateFormat (key) {
		var format = this._longDateFormat[key],
			formatUpper = this._longDateFormat[key.toUpperCase()];

		if (format || !formatUpper) {
			return format;
		}

		this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) {
			return val.slice(1);
		});

		return this._longDateFormat[key];
	}

	var defaultInvalidDate = 'Invalid date';

	function invalidDate () {
		return this._invalidDate;
	}

	var defaultOrdinal = '%d';
	var defaultOrdinalParse = /\d{1,2}/;

	function ordinal (number) {
		return this._ordinal.replace('%d', number);
	}

	var defaultRelativeTime = {
		future : 'in %s',
		past   : '%s ago',
		s  : 'a few seconds',
		m  : 'a minute',
		mm : '%d minutes',
		h  : 'an hour',
		hh : '%d hours',
		d  : 'a day',
		dd : '%d days',
		M  : 'a month',
		MM : '%d months',
		y  : 'a year',
		yy : '%d years'
	};

	function relativeTime (number, withoutSuffix, string, isFuture) {
		var output = this._relativeTime[string];
		return (isFunction(output)) ?
			output(number, withoutSuffix, string, isFuture) :
			output.replace(/%d/i, number);
	}

	function pastFuture (diff, output) {
		var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
		return isFunction(format) ? format(output) : format.replace(/%s/i, output);
	}

	var aliases = {};

	function addUnitAlias (unit, shorthand) {
		var lowerCase = unit.toLowerCase();
		aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit;
	}

	function normalizeUnits(units) {
		return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined;
	}

	function normalizeObjectUnits(inputObject) {
		var normalizedInput = {},
			normalizedProp,
			prop;

		for (prop in inputObject) {
			if (hasOwnProp(inputObject, prop)) {
				normalizedProp = normalizeUnits(prop);
				if (normalizedProp) {
					normalizedInput[normalizedProp] = inputObject[prop];
				}
			}
		}

		return normalizedInput;
	}

	var priorities = {};

	function addUnitPriority(unit, priority) {
		priorities[unit] = priority;
	}

	function getPrioritizedUnits(unitsObj) {
		var units = [];
		for (var u in unitsObj) {
			units.push({unit: u, priority: priorities[u]});
		}
		units.sort(function (a, b) {
			return a.priority - b.priority;
		});
		return units;
	}

	function makeGetSet (unit, keepTime) {
		return function (value) {
			if (value != null) {
				set$1(this, unit, value);
				hooks.updateOffset(this, keepTime);
				return this;
			} else {
				return get(this, unit);
			}
		};
	}

	function get (mom, unit) {
		return mom.isValid() ?
			mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN;
	}

	function set$1 (mom, unit, value) {
		if (mom.isValid()) {
			mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value);
		}
	}

// MOMENTS

	function stringGet (units) {
		units = normalizeUnits(units);
		if (isFunction(this[units])) {
			return this[units]();
		}
		return this;
	}


	function stringSet (units, value) {
		if (typeof units === 'object') {
			units = normalizeObjectUnits(units);
			var prioritized = getPrioritizedUnits(units);
			for (var i = 0; i < prioritized.length; i++) {
				this[prioritized[i].unit](units[prioritized[i].unit]);
			}
		} else {
			units = normalizeUnits(units);
			if (isFunction(this[units])) {
				return this[units](value);
			}
		}
		return this;
	}

	function zeroFill(number, targetLength, forceSign) {
		var absNumber = '' + Math.abs(number),
			zerosToFill = targetLength - absNumber.length,
			sign = number >= 0;
		return (sign ? (forceSign ? '+' : '') : '-') +
			Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber;
	}

	var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g;

	var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g;

	var formatFunctions = {};

	var formatTokenFunctions = {};

// token:    'M'
// padded:   ['MM', 2]
// ordinal:  'Mo'
// callback: function () { this.month() + 1 }
	function addFormatToken (token, padded, ordinal, callback) {
		var func = callback;
		if (typeof callback === 'string') {
			func = function () {
				return this[callback]();
			};
		}
		if (token) {
			formatTokenFunctions[token] = func;
		}
		if (padded) {
			formatTokenFunctions[padded[0]] = function () {
				return zeroFill(func.apply(this, arguments), padded[1], padded[2]);
			};
		}
		if (ordinal) {
			formatTokenFunctions[ordinal] = function () {
				return this.localeData().ordinal(func.apply(this, arguments), token);
			};
		}
	}

	function removeFormattingTokens(input) {
		if (input.match(/\[[\s\S]/)) {
			return input.replace(/^\[|\]$/g, '');
		}
		return input.replace(/\\/g, '');
	}

	function makeFormatFunction(format) {
		var array = format.match(formattingTokens), i, length;

		for (i = 0, length = array.length; i < length; i++) {
			if (formatTokenFunctions[array[i]]) {
				array[i] = formatTokenFunctions[array[i]];
			} else {
				array[i] = removeFormattingTokens(array[i]);
			}
		}

		return function (mom) {
			var output = '', i;
			for (i = 0; i < length; i++) {
				output += array[i] instanceof Function ? array[i].call(mom, format) : array[i];
			}
			return output;
		};
	}

// format date using native date object
	function formatMoment(m, format) {
		if (!m.isValid()) {
			return m.localeData().invalidDate();
		}

		format = expandFormat(format, m.localeData());
		formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format);

		return formatFunctions[format](m);
	}

	function expandFormat(format, locale) {
		var i = 5;

		function replaceLongDateFormatTokens(input) {
			return locale.longDateFormat(input) || input;
		}

		localFormattingTokens.lastIndex = 0;
		while (i >= 0 && localFormattingTokens.test(format)) {
			format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
			localFormattingTokens.lastIndex = 0;
			i -= 1;
		}

		return format;
	}

	var match1         = /\d/;            //       0 - 9
	var match2         = /\d\d/;          //      00 - 99
	var match3         = /\d{3}/;         //     000 - 999
	var match4         = /\d{4}/;         //    0000 - 9999
	var match6         = /[+-]?\d{6}/;    // -999999 - 999999
	var match1to2      = /\d\d?/;         //       0 - 99
	var match3to4      = /\d\d\d\d?/;     //     999 - 9999
	var match5to6      = /\d\d\d\d\d\d?/; //   99999 - 999999
	var match1to3      = /\d{1,3}/;       //       0 - 999
	var match1to4      = /\d{1,4}/;       //       0 - 9999
	var match1to6      = /[+-]?\d{1,6}/;  // -999999 - 999999

	var matchUnsigned  = /\d+/;           //       0 - inf
	var matchSigned    = /[+-]?\d+/;      //    -inf - inf

	var matchOffset    = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z
	var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z

	var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123

// any word (or two) characters or numbers including two/three word month in arabic.
// includes scottish gaelic two word and hyphenated months
	var matchWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i;


	var regexes = {};

	function addRegexToken (token, regex, strictRegex) {
		regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) {
				return (isStrict && strictRegex) ? strictRegex : regex;
			};
	}

	function getParseRegexForToken (token, config) {
		if (!hasOwnProp(regexes, token)) {
			return new RegExp(unescapeFormat(token));
		}

		return regexes[token](config._strict, config._locale);
	}

// Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
	function unescapeFormat(s) {
		return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) {
			return p1 || p2 || p3 || p4;
		}));
	}

	function regexEscape(s) {
		return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
	}

	var tokens = {};

	function addParseToken (token, callback) {
		var i, func = callback;
		if (typeof token === 'string') {
			token = [token];
		}
		if (isNumber(callback)) {
			func = function (input, array) {
				array[callback] = toInt(input);
			};
		}
		for (i = 0; i < token.length; i++) {
			tokens[token[i]] = func;
		}
	}

	function addWeekParseToken (token, callback) {
		addParseToken(token, function (input, array, config, token) {
			config._w = config._w || {};
			callback(input, config._w, config, token);
		});
	}

	function addTimeToArrayFromToken(token, input, config) {
		if (input != null && hasOwnProp(tokens, token)) {
			tokens[token](input, config._a, config, token);
		}
	}

	var YEAR = 0;
	var MONTH = 1;
	var DATE = 2;
	var HOUR = 3;
	var MINUTE = 4;
	var SECOND = 5;
	var MILLISECOND = 6;
	var WEEK = 7;
	var WEEKDAY = 8;

	var indexOf;

	if (Array.prototype.indexOf) {
		indexOf = Array.prototype.indexOf;
	} else {
		indexOf = function (o) {
			// I know
			var i;
			for (i = 0; i < this.length; ++i) {
				if (this[i] === o) {
					return i;
				}
			}
			return -1;
		};
	}

	var indexOf$1 = indexOf;

	function daysInMonth(year, month) {
		return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
	}

// FORMATTING

	addFormatToken('M', ['MM', 2], 'Mo', function () {
		return this.month() + 1;
	});

	addFormatToken('MMM', 0, 0, function (format) {
		return this.localeData().monthsShort(this, format);
	});

	addFormatToken('MMMM', 0, 0, function (format) {
		return this.localeData().months(this, format);
	});

// ALIASES

	addUnitAlias('month', 'M');

// PRIORITY

	addUnitPriority('month', 8);

// PARSING

	addRegexToken('M',    match1to2);
	addRegexToken('MM',   match1to2, match2);
	addRegexToken('MMM',  function (isStrict, locale) {
		return locale.monthsShortRegex(isStrict);
	});
	addRegexToken('MMMM', function (isStrict, locale) {
		return locale.monthsRegex(isStrict);
	});

	addParseToken(['M', 'MM'], function (input, array) {
		array[MONTH] = toInt(input) - 1;
	});

	addParseToken(['MMM', 'MMMM'], function (input, array, config, token) {
		var month = config._locale.monthsParse(input, token, config._strict);
		// if we didn't find a month name, mark the date as invalid.
		if (month != null) {
			array[MONTH] = month;
		} else {
			getParsingFlags(config).invalidMonth = input;
		}
	});

// LOCALES

	var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/;
	var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_');
	function localeMonths (m, format) {
		if (!m) {
			return this._months;
		}
		return isArray(this._months) ? this._months[m.month()] :
			this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()];
	}

	var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_');
	function localeMonthsShort (m, format) {
		if (!m) {
			return this._monthsShort;
		}
		return isArray(this._monthsShort) ? this._monthsShort[m.month()] :
			this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()];
	}

	function handleStrictParse(monthName, format, strict) {
		var i, ii, mom, llc = monthName.toLocaleLowerCase();
		if (!this._monthsParse) {
			// this is not used
			this._monthsParse = [];
			this._longMonthsParse = [];
			this._shortMonthsParse = [];
			for (i = 0; i < 12; ++i) {
				mom = createUTC([2000, i]);
				this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase();
				this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase();
			}
		}

		if (strict) {
			if (format === 'MMM') {
				ii = indexOf$1.call(this._shortMonthsParse, llc);
				return ii !== -1 ? ii : null;
			} else {
				ii = indexOf$1.call(this._longMonthsParse, llc);
				return ii !== -1 ? ii : null;
			}
		} else {
			if (format === 'MMM') {
				ii = indexOf$1.call(this._shortMonthsParse, llc);
				if (ii !== -1) {
					return ii;
				}
				ii = indexOf$1.call(this._longMonthsParse, llc);
				return ii !== -1 ? ii : null;
			} else {
				ii = indexOf$1.call(this._longMonthsParse, llc);
				if (ii !== -1) {
					return ii;
				}
				ii = indexOf$1.call(this._shortMonthsParse, llc);
				return ii !== -1 ? ii : null;
			}
		}
	}

	function localeMonthsParse (monthName, format, strict) {
		var i, mom, regex;

		if (this._monthsParseExact) {
			return handleStrictParse.call(this, monthName, format, strict);
		}

		if (!this._monthsParse) {
			this._monthsParse = [];
			this._longMonthsParse = [];
			this._shortMonthsParse = [];
		}

		// TODO: add sorting
		// Sorting makes sure if one month (or abbr) is a prefix of another
		// see sorting in computeMonthsParse
		for (i = 0; i < 12; i++) {
			// make the regex if we don't have it already
			mom = createUTC([2000, i]);
			if (strict && !this._longMonthsParse[i]) {
				this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i');
				this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i');
			}
			if (!strict && !this._monthsParse[i]) {
				regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
				this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
			}
			// test the regex
			if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) {
				return i;
			} else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) {
				return i;
			} else if (!strict && this._monthsParse[i].test(monthName)) {
				return i;
			}
		}
	}

// MOMENTS

	function setMonth (mom, value) {
		var dayOfMonth;

		if (!mom.isValid()) {
			// No op
			return mom;
		}

		if (typeof value === 'string') {
			if (/^\d+$/.test(value)) {
				value = toInt(value);
			} else {
				value = mom.localeData().monthsParse(value);
				// TODO: Another silent failure?
				if (!isNumber(value)) {
					return mom;
				}
			}
		}

		dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value));
		mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth);
		return mom;
	}

	function getSetMonth (value) {
		if (value != null) {
			setMonth(this, value);
			hooks.updateOffset(this, true);
			return this;
		} else {
			return get(this, 'Month');
		}
	}

	function getDaysInMonth () {
		return daysInMonth(this.year(), this.month());
	}

	var defaultMonthsShortRegex = matchWord;
	function monthsShortRegex (isStrict) {
		if (this._monthsParseExact) {
			if (!hasOwnProp(this, '_monthsRegex')) {
				computeMonthsParse.call(this);
			}
			if (isStrict) {
				return this._monthsShortStrictRegex;
			} else {
				return this._monthsShortRegex;
			}
		} else {
			if (!hasOwnProp(this, '_monthsShortRegex')) {
				this._monthsShortRegex = defaultMonthsShortRegex;
			}
			return this._monthsShortStrictRegex && isStrict ?
				this._monthsShortStrictRegex : this._monthsShortRegex;
		}
	}

	var defaultMonthsRegex = matchWord;
	function monthsRegex (isStrict) {
		if (this._monthsParseExact) {
			if (!hasOwnProp(this, '_monthsRegex')) {
				computeMonthsParse.call(this);
			}
			if (isStrict) {
				return this._monthsStrictRegex;
			} else {
				return this._monthsRegex;
			}
		} else {
			if (!hasOwnProp(this, '_monthsRegex')) {
				this._monthsRegex = defaultMonthsRegex;
			}
			return this._monthsStrictRegex && isStrict ?
				this._monthsStrictRegex : this._monthsRegex;
		}
	}

	function computeMonthsParse () {
		function cmpLenRev(a, b) {
			return b.length - a.length;
		}

		var shortPieces = [], longPieces = [], mixedPieces = [],
			i, mom;
		for (i = 0; i < 12; i++) {
			// make the regex if we don't have it already
			mom = createUTC([2000, i]);
			shortPieces.push(this.monthsShort(mom, ''));
			longPieces.push(this.months(mom, ''));
			mixedPieces.push(this.months(mom, ''));
			mixedPieces.push(this.monthsShort(mom, ''));
		}
		// Sorting makes sure if one month (or abbr) is a prefix of another it
		// will match the longer piece.
		shortPieces.sort(cmpLenRev);
		longPieces.sort(cmpLenRev);
		mixedPieces.sort(cmpLenRev);
		for (i = 0; i < 12; i++) {
			shortPieces[i] = regexEscape(shortPieces[i]);
			longPieces[i] = regexEscape(longPieces[i]);
		}
		for (i = 0; i < 24; i++) {
			mixedPieces[i] = regexEscape(mixedPieces[i]);
		}

		this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
		this._monthsShortRegex = this._monthsRegex;
		this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
		this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
	}

// FORMATTING

	addFormatToken('Y', 0, 0, function () {
		var y = this.year();
		return y <= 9999 ? '' + y : '+' + y;
	});

	addFormatToken(0, ['YY', 2], 0, function () {
		return this.year() % 100;
	});

	addFormatToken(0, ['YYYY',   4],       0, 'year');
	addFormatToken(0, ['YYYYY',  5],       0, 'year');
	addFormatToken(0, ['YYYYYY', 6, true], 0, 'year');

// ALIASES

	addUnitAlias('year', 'y');

// PRIORITIES

	addUnitPriority('year', 1);

// PARSING

	addRegexToken('Y',      matchSigned);
	addRegexToken('YY',     match1to2, match2);
	addRegexToken('YYYY',   match1to4, match4);
	addRegexToken('YYYYY',  match1to6, match6);
	addRegexToken('YYYYYY', match1to6, match6);

	addParseToken(['YYYYY', 'YYYYYY'], YEAR);
	addParseToken('YYYY', function (input, array) {
		array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input);
	});
	addParseToken('YY', function (input, array) {
		array[YEAR] = hooks.parseTwoDigitYear(input);
	});
	addParseToken('Y', function (input, array) {
		array[YEAR] = parseInt(input, 10);
	});

// HELPERS

	function daysInYear(year) {
		return isLeapYear(year) ? 366 : 365;
	}

	function isLeapYear(year) {
		return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
	}

// HOOKS

	hooks.parseTwoDigitYear = function (input) {
		return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
	};

// MOMENTS

	var getSetYear = makeGetSet('FullYear', true);

	function getIsLeapYear () {
		return isLeapYear(this.year());
	}

	function createDate (y, m, d, h, M, s, ms) {
		//can't just apply() to create a date:
		//http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply
		var date = new Date(y, m, d, h, M, s, ms);

		//the date constructor remaps years 0-99 to 1900-1999
		if (y < 100 && y >= 0 && isFinite(date.getFullYear())) {
			date.setFullYear(y);
		}
		return date;
	}

	function createUTCDate (y) {
		var date = new Date(Date.UTC.apply(null, arguments));

		//the Date.UTC function remaps years 0-99 to 1900-1999
		if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) {
			date.setUTCFullYear(y);
		}
		return date;
	}

// start-of-first-week - start-of-year
	function firstWeekOffset(year, dow, doy) {
		var // first-week day -- which january is always in the first week (4 for iso, 1 for other)
			fwd = 7 + dow - doy,
			// first-week day local weekday -- which local weekday is fwd
			fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7;

		return -fwdlw + fwd - 1;
	}

//http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
	function dayOfYearFromWeeks(year, week, weekday, dow, doy) {
		var localWeekday = (7 + weekday - dow) % 7,
			weekOffset = firstWeekOffset(year, dow, doy),
			dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset,
			resYear, resDayOfYear;

		if (dayOfYear <= 0) {
			resYear = year - 1;
			resDayOfYear = daysInYear(resYear) + dayOfYear;
		} else if (dayOfYear > daysInYear(year)) {
			resYear = year + 1;
			resDayOfYear = dayOfYear - daysInYear(year);
		} else {
			resYear = year;
			resDayOfYear = dayOfYear;
		}

		return {
			year: resYear,
			dayOfYear: resDayOfYear
		};
	}

	function weekOfYear(mom, dow, doy) {
		var weekOffset = firstWeekOffset(mom.year(), dow, doy),
			week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1,
			resWeek, resYear;

		if (week < 1) {
			resYear = mom.year() - 1;
			resWeek = week + weeksInYear(resYear, dow, doy);
		} else if (week > weeksInYear(mom.year(), dow, doy)) {
			resWeek = week - weeksInYear(mom.year(), dow, doy);
			resYear = mom.year() + 1;
		} else {
			resYear = mom.year();
			resWeek = week;
		}

		return {
			week: resWeek,
			year: resYear
		};
	}

	function weeksInYear(year, dow, doy) {
		var weekOffset = firstWeekOffset(year, dow, doy),
			weekOffsetNext = firstWeekOffset(year + 1, dow, doy);
		return (daysInYear(year) - weekOffset + weekOffsetNext) / 7;
	}

// FORMATTING

	addFormatToken('w', ['ww', 2], 'wo', 'week');
	addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek');

// ALIASES

	addUnitAlias('week', 'w');
	addUnitAlias('isoWeek', 'W');

// PRIORITIES

	addUnitPriority('week', 5);
	addUnitPriority('isoWeek', 5);

// PARSING

	addRegexToken('w',  match1to2);
	addRegexToken('ww', match1to2, match2);
	addRegexToken('W',  match1to2);
	addRegexToken('WW', match1to2, match2);

	addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) {
		week[token.substr(0, 1)] = toInt(input);
	});

// HELPERS

// LOCALES

	function localeWeek (mom) {
		return weekOfYear(mom, this._week.dow, this._week.doy).week;
	}

	var defaultLocaleWeek = {
		dow : 0, // Sunday is the first day of the week.
		doy : 6  // The week that contains Jan 1st is the first week of the year.
	};

	function localeFirstDayOfWeek () {
		return this._week.dow;
	}

	function localeFirstDayOfYear () {
		return this._week.doy;
	}

// MOMENTS

	function getSetWeek (input) {
		var week = this.localeData().week(this);
		return input == null ? week : this.add((input - week) * 7, 'd');
	}

	function getSetISOWeek (input) {
		var week = weekOfYear(this, 1, 4).week;
		return input == null ? week : this.add((input - week) * 7, 'd');
	}

// FORMATTING

	addFormatToken('d', 0, 'do', 'day');

	addFormatToken('dd', 0, 0, function (format) {
		return this.localeData().weekdaysMin(this, format);
	});

	addFormatToken('ddd', 0, 0, function (format) {
		return this.localeData().weekdaysShort(this, format);
	});

	addFormatToken('dddd', 0, 0, function (format) {
		return this.localeData().weekdays(this, format);
	});

	addFormatToken('e', 0, 0, 'weekday');
	addFormatToken('E', 0, 0, 'isoWeekday');

// ALIASES

	addUnitAlias('day', 'd');
	addUnitAlias('weekday', 'e');
	addUnitAlias('isoWeekday', 'E');

// PRIORITY
	addUnitPriority('day', 11);
	addUnitPriority('weekday', 11);
	addUnitPriority('isoWeekday', 11);

// PARSING

	addRegexToken('d',    match1to2);
	addRegexToken('e',    match1to2);
	addRegexToken('E',    match1to2);
	addRegexToken('dd',   function (isStrict, locale) {
		return locale.weekdaysMinRegex(isStrict);
	});
	addRegexToken('ddd',   function (isStrict, locale) {
		return locale.weekdaysShortRegex(isStrict);
	});
	addRegexToken('dddd',   function (isStrict, locale) {
		return locale.weekdaysRegex(isStrict);
	});

	addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) {
		var weekday = config._locale.weekdaysParse(input, token, config._strict);
		// if we didn't get a weekday name, mark the date as invalid
		if (weekday != null) {
			week.d = weekday;
		} else {
			getParsingFlags(config).invalidWeekday = input;
		}
	});

	addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) {
		week[token] = toInt(input);
	});

// HELPERS

	function parseWeekday(input, locale) {
		if (typeof input !== 'string') {
			return input;
		}

		if (!isNaN(input)) {
			return parseInt(input, 10);
		}

		input = locale.weekdaysParse(input);
		if (typeof input === 'number') {
			return input;
		}

		return null;
	}

	function parseIsoWeekday(input, locale) {
		if (typeof input === 'string') {
			return locale.weekdaysParse(input) % 7 || 7;
		}
		return isNaN(input) ? null : input;
	}

// LOCALES

	var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_');
	function localeWeekdays (m, format) {
		if (!m) {
			return this._weekdays;
		}
		return isArray(this._weekdays) ? this._weekdays[m.day()] :
			this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()];
	}

	var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_');
	function localeWeekdaysShort (m) {
		return (m) ? this._weekdaysShort[m.day()] : this._weekdaysShort;
	}

	var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_');
	function localeWeekdaysMin (m) {
		return (m) ? this._weekdaysMin[m.day()] : this._weekdaysMin;
	}

	function handleStrictParse$1(weekdayName, format, strict) {
		var i, ii, mom, llc = weekdayName.toLocaleLowerCase();
		if (!this._weekdaysParse) {
			this._weekdaysParse = [];
			this._shortWeekdaysParse = [];
			this._minWeekdaysParse = [];

			for (i = 0; i < 7; ++i) {
				mom = createUTC([2000, 1]).day(i);
				this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase();
				this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase();
				this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase();
			}
		}

		if (strict) {
			if (format === 'dddd') {
				ii = indexOf$1.call(this._weekdaysParse, llc);
				return ii !== -1 ? ii : null;
			} else if (format === 'ddd') {
				ii = indexOf$1.call(this._shortWeekdaysParse, llc);
				return ii !== -1 ? ii : null;
			} else {
				ii = indexOf$1.call(this._minWeekdaysParse, llc);
				return ii !== -1 ? ii : null;
			}
		} else {
			if (format === 'dddd') {
				ii = indexOf$1.call(this._weekdaysParse, llc);
				if (ii !== -1) {
					return ii;
				}
				ii = indexOf$1.call(this._shortWeekdaysParse, llc);
				if (ii !== -1) {
					return ii;
				}
				ii = indexOf$1.call(this._minWeekdaysParse, llc);
				return ii !== -1 ? ii : null;
			} else if (format === 'ddd') {
				ii = indexOf$1.call(this._shortWeekdaysParse, llc);
				if (ii !== -1) {
					return ii;
				}
				ii = indexOf$1.call(this._weekdaysParse, llc);
				if (ii !== -1) {
					return ii;
				}
				ii = indexOf$1.call(this._minWeekdaysParse, llc);
				return ii !== -1 ? ii : null;
			} else {
				ii = indexOf$1.call(this._minWeekdaysParse, llc);
				if (ii !== -1) {
					return ii;
				}
				ii = indexOf$1.call(this._weekdaysParse, llc);
				if (ii !== -1) {
					return ii;
				}
				ii = indexOf$1.call(this._shortWeekdaysParse, llc);
				return ii !== -1 ? ii : null;
			}
		}
	}

	function localeWeekdaysParse (weekdayName, format, strict) {
		var i, mom, regex;

		if (this._weekdaysParseExact) {
			return handleStrictParse$1.call(this, weekdayName, format, strict);
		}

		if (!this._weekdaysParse) {
			this._weekdaysParse = [];
			this._minWeekdaysParse = [];
			this._shortWeekdaysParse = [];
			this._fullWeekdaysParse = [];
		}

		for (i = 0; i < 7; i++) {
			// make the regex if we don't have it already

			mom = createUTC([2000, 1]).day(i);
			if (strict && !this._fullWeekdaysParse[i]) {
				this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\.?') + '$', 'i');
				this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\.?') + '$', 'i');
				this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\.?') + '$', 'i');
			}
			if (!this._weekdaysParse[i]) {
				regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');
				this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
			}
			// test the regex
			if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) {
				return i;
			} else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) {
				return i;
			} else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) {
				return i;
			} else if (!strict && this._weekdaysParse[i].test(weekdayName)) {
				return i;
			}
		}
	}

// MOMENTS

	function getSetDayOfWeek (input) {
		if (!this.isValid()) {
			return input != null ? this : NaN;
		}
		var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
		if (input != null) {
			input = parseWeekday(input, this.localeData());
			return this.add(input - day, 'd');
		} else {
			return day;
		}
	}

	function getSetLocaleDayOfWeek (input) {
		if (!this.isValid()) {
			return input != null ? this : NaN;
		}
		var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7;
		return input == null ? weekday : this.add(input - weekday, 'd');
	}

	function getSetISODayOfWeek (input) {
		if (!this.isValid()) {
			return input != null ? this : NaN;
		}

		// behaves the same as moment#day except
		// as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
		// as a setter, sunday should belong to the previous week.

		if (input != null) {
			var weekday = parseIsoWeekday(input, this.localeData());
			return this.day(this.day() % 7 ? weekday : weekday - 7);
		} else {
			return this.day() || 7;
		}
	}

	var defaultWeekdaysRegex = matchWord;
	function weekdaysRegex (isStrict) {
		if (this._weekdaysParseExact) {
			if (!hasOwnProp(this, '_weekdaysRegex')) {
				computeWeekdaysParse.call(this);
			}
			if (isStrict) {
				return this._weekdaysStrictRegex;
			} else {
				return this._weekdaysRegex;
			}
		} else {
			if (!hasOwnProp(this, '_weekdaysRegex')) {
				this._weekdaysRegex = defaultWeekdaysRegex;
			}
			return this._weekdaysStrictRegex && isStrict ?
				this._weekdaysStrictRegex : this._weekdaysRegex;
		}
	}

	var defaultWeekdaysShortRegex = matchWord;
	function weekdaysShortRegex (isStrict) {
		if (this._weekdaysParseExact) {
			if (!hasOwnProp(this, '_weekdaysRegex')) {
				computeWeekdaysParse.call(this);
			}
			if (isStrict) {
				return this._weekdaysShortStrictRegex;
			} else {
				return this._weekdaysShortRegex;
			}
		} else {
			if (!hasOwnProp(this, '_weekdaysShortRegex')) {
				this._weekdaysShortRegex = defaultWeekdaysShortRegex;
			}
			return this._weekdaysShortStrictRegex && isStrict ?
				this._weekdaysShortStrictRegex : this._weekdaysShortRegex;
		}
	}

	var defaultWeekdaysMinRegex = matchWord;
	function weekdaysMinRegex (isStrict) {
		if (this._weekdaysParseExact) {
			if (!hasOwnProp(this, '_weekdaysRegex')) {
				computeWeekdaysParse.call(this);
			}
			if (isStrict) {
				return this._weekdaysMinStrictRegex;
			} else {
				return this._weekdaysMinRegex;
			}
		} else {
			if (!hasOwnProp(this, '_weekdaysMinRegex')) {
				this._weekdaysMinRegex = defaultWeekdaysMinRegex;
			}
			return this._weekdaysMinStrictRegex && isStrict ?
				this._weekdaysMinStrictRegex : this._weekdaysMinRegex;
		}
	}


	function computeWeekdaysParse () {
		function cmpLenRev(a, b) {
			return b.length - a.length;
		}

		var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [],
			i, mom, minp, shortp, longp;
		for (i = 0; i < 7; i++) {
			// make the regex if we don't have it already
			mom = createUTC([2000, 1]).day(i);
			minp = this.weekdaysMin(mom, '');
			shortp = this.weekdaysShort(mom, '');
			longp = this.weekdays(mom, '');
			minPieces.push(minp);
			shortPieces.push(shortp);
			longPieces.push(longp);
			mixedPieces.push(minp);
			mixedPieces.push(shortp);
			mixedPieces.push(longp);
		}
		// Sorting makes sure if one weekday (or abbr) is a prefix of another it
		// will match the longer piece.
		minPieces.sort(cmpLenRev);
		shortPieces.sort(cmpLenRev);
		longPieces.sort(cmpLenRev);
		mixedPieces.sort(cmpLenRev);
		for (i = 0; i < 7; i++) {
			shortPieces[i] = regexEscape(shortPieces[i]);
			longPieces[i] = regexEscape(longPieces[i]);
			mixedPieces[i] = regexEscape(mixedPieces[i]);
		}

		this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
		this._weekdaysShortRegex = this._weekdaysRegex;
		this._weekdaysMinRegex = this._weekdaysRegex;

		this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
		this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
		this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i');
	}

// FORMATTING

	function hFormat() {
		return this.hours() % 12 || 12;
	}

	function kFormat() {
		return this.hours() || 24;
	}

	addFormatToken('H', ['HH', 2], 0, 'hour');
	addFormatToken('h', ['hh', 2], 0, hFormat);
	addFormatToken('k', ['kk', 2], 0, kFormat);

	addFormatToken('hmm', 0, 0, function () {
		return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2);
	});

	addFormatToken('hmmss', 0, 0, function () {
		return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) +
			zeroFill(this.seconds(), 2);
	});

	addFormatToken('Hmm', 0, 0, function () {
		return '' + this.hours() + zeroFill(this.minutes(), 2);
	});

	addFormatToken('Hmmss', 0, 0, function () {
		return '' + this.hours() + zeroFill(this.minutes(), 2) +
			zeroFill(this.seconds(), 2);
	});

	function meridiem (token, lowercase) {
		addFormatToken(token, 0, 0, function () {
			return this.localeData().meridiem(this.hours(), this.minutes(), lowercase);
		});
	}

	meridiem('a', true);
	meridiem('A', false);

// ALIASES

	addUnitAlias('hour', 'h');

// PRIORITY
	addUnitPriority('hour', 13);

// PARSING

	function matchMeridiem (isStrict, locale) {
		return locale._meridiemParse;
	}

	addRegexToken('a',  matchMeridiem);
	addRegexToken('A',  matchMeridiem);
	addRegexToken('H',  match1to2);
	addRegexToken('h',  match1to2);
	addRegexToken('HH', match1to2, match2);
	addRegexToken('hh', match1to2, match2);

	addRegexToken('hmm', match3to4);
	addRegexToken('hmmss', match5to6);
	addRegexToken('Hmm', match3to4);
	addRegexToken('Hmmss', match5to6);

	addParseToken(['H', 'HH'], HOUR);
	addParseToken(['a', 'A'], function (input, array, config) {
		config._isPm = config._locale.isPM(input);
		config._meridiem = input;
	});
	addParseToken(['h', 'hh'], function (input, array, config) {
		array[HOUR] = toInt(input);
		getParsingFlags(config).bigHour = true;
	});
	addParseToken('hmm', function (input, array, config) {
		var pos = input.length - 2;
		array[HOUR] = toInt(input.substr(0, pos));
		array[MINUTE] = toInt(input.substr(pos));
		getParsingFlags(config).bigHour = true;
	});
	addParseToken('hmmss', function (input, array, config) {
		var pos1 = input.length - 4;
		var pos2 = input.length - 2;
		array[HOUR] = toInt(input.substr(0, pos1));
		array[MINUTE] = toInt(input.substr(pos1, 2));
		array[SECOND] = toInt(input.substr(pos2));
		getParsingFlags(config).bigHour = true;
	});
	addParseToken('Hmm', function (input, array, config) {
		var pos = input.length - 2;
		array[HOUR] = toInt(input.substr(0, pos));
		array[MINUTE] = toInt(input.substr(pos));
	});
	addParseToken('Hmmss', function (input, array, config) {
		var pos1 = input.length - 4;
		var pos2 = input.length - 2;
		array[HOUR] = toInt(input.substr(0, pos1));
		array[MINUTE] = toInt(input.substr(pos1, 2));
		array[SECOND] = toInt(input.substr(pos2));
	});

// LOCALES

	function localeIsPM (input) {
		// IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays
		// Using charAt should be more compatible.
		return ((input + '').toLowerCase().charAt(0) === 'p');
	}

	var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i;
	function localeMeridiem (hours, minutes, isLower) {
		if (hours > 11) {
			return isLower ? 'pm' : 'PM';
		} else {
			return isLower ? 'am' : 'AM';
		}
	}


// MOMENTS

// Setting the hour should keep the time, because the user explicitly
// specified which hour he wants. So trying to maintain the same hour (in
// a new timezone) makes sense. Adding/subtracting hours does not follow
// this rule.
	var getSetHour = makeGetSet('Hours', true);

// months
// week
// weekdays
// meridiem
	var baseConfig = {
		calendar: defaultCalendar,
		longDateFormat: defaultLongDateFormat,
		invalidDate: defaultInvalidDate,
		ordinal: defaultOrdinal,
		ordinalParse: defaultOrdinalParse,
		relativeTime: defaultRelativeTime,

		months: defaultLocaleMonths,
		monthsShort: defaultLocaleMonthsShort,

		week: defaultLocaleWeek,

		weekdays: defaultLocaleWeekdays,
		weekdaysMin: defaultLocaleWeekdaysMin,
		weekdaysShort: defaultLocaleWeekdaysShort,

		meridiemParse: defaultLocaleMeridiemParse
	};

// internal storage for locale config files
	var locales = {};
	var localeFamilies = {};
	var globalLocale;

	function normalizeLocale(key) {
		return key ? key.toLowerCase().replace('_', '-') : key;
	}

// pick the locale from the array
// try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
// substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
	function chooseLocale(names) {
		var i = 0, j, next, locale, split;

		while (i < names.length) {
			split = normalizeLocale(names[i]).split('-');
			j = split.length;
			next = normalizeLocale(names[i + 1]);
			next = next ? next.split('-') : null;
			while (j > 0) {
				locale = loadLocale(split.slice(0, j).join('-'));
				if (locale) {
					return locale;
				}
				if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) {
					//the next array item is better than a shallower substring of this one
					break;
				}
				j--;
			}
			i++;
		}
		return null;
	}

	function loadLocale(name) {
		var oldLocale = null;
		// TODO: Find a better way to register and load all the locales in Node
		if (!locales[name] && (typeof module !== 'undefined') &&
			module && module.exports) {
			try {
				oldLocale = globalLocale._abbr;
				require('./locale/' + name);
				// because defineLocale currently also sets the global locale, we
				// want to undo that for lazy loaded locales
				getSetGlobalLocale(oldLocale);
			} catch (e) { }
		}
		return locales[name];
	}

// This function will load locale and then set the global locale.  If
// no arguments are passed in, it will simply return the current global
// locale key.
	function getSetGlobalLocale (key, values) {
		var data;
		if (key) {
			if (isUndefined(values)) {
				data = getLocale(key);
			}
			else {
				data = defineLocale(key, values);
			}

			if (data) {
				// moment.duration._locale = moment._locale = data;
				globalLocale = data;
			}
		}

		return globalLocale._abbr;
	}

	function defineLocale (name, config) {
		if (config !== null) {
			var parentConfig = baseConfig;
			config.abbr = name;
			if (locales[name] != null) {
				deprecateSimple('defineLocaleOverride',
					'use moment.updateLocale(localeName, config) to change ' +
					'an existing locale. moment.defineLocale(localeName, ' +
					'config) should only be used for creating a new locale ' +
					'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.');
				parentConfig = locales[name]._config;
			} else if (config.parentLocale != null) {
				if (locales[config.parentLocale] != null) {
					parentConfig = locales[config.parentLocale]._config;
				} else {
					if (!localeFamilies[config.parentLocale]) {
						localeFamilies[config.parentLocale] = [];
					}
					localeFamilies[config.parentLocale].push({
						name: name,
						config: config
					});
					return null;
				}
			}
			locales[name] = new Locale(mergeConfigs(parentConfig, config));

			if (localeFamilies[name]) {
				localeFamilies[name].forEach(function (x) {
					defineLocale(x.name, x.config);
				});
			}

			// backwards compat for now: also set the locale
			// make sure we set the locale AFTER all child locales have been
			// created, so we won't end up with the child locale set.
			getSetGlobalLocale(name);


			return locales[name];
		} else {
			// useful for testing
			delete locales[name];
			return null;
		}
	}

	function updateLocale(name, config) {
		if (config != null) {
			var locale, parentConfig = baseConfig;
			// MERGE
			if (locales[name] != null) {
				parentConfig = locales[name]._config;
			}
			config = mergeConfigs(parentConfig, config);
			locale = new Locale(config);
			locale.parentLocale = locales[name];
			locales[name] = locale;

			// backwards compat for now: also set the locale
			getSetGlobalLocale(name);
		} else {
			// pass null for config to unupdate, useful for tests
			if (locales[name] != null) {
				if (locales[name].parentLocale != null) {
					locales[name] = locales[name].parentLocale;
				} else if (locales[name] != null) {
					delete locales[name];
				}
			}
		}
		return locales[name];
	}

// returns locale data
	function getLocale (key) {
		var locale;

		if (key && key._locale && key._locale._abbr) {
			key = key._locale._abbr;
		}

		if (!key) {
			return globalLocale;
		}

		if (!isArray(key)) {
			//short-circuit everything else
			locale = loadLocale(key);
			if (locale) {
				return locale;
			}
			key = [key];
		}

		return chooseLocale(key);
	}

	function listLocales() {
		return keys$1(locales);
	}

	function checkOverflow (m) {
		var overflow;
		var a = m._a;

		if (a && getParsingFlags(m).overflow === -2) {
			overflow =
				a[MONTH]       < 0 || a[MONTH]       > 11  ? MONTH :
					a[DATE]        < 1 || a[DATE]        > daysInMonth(a[YEAR], a[MONTH]) ? DATE :
						a[HOUR]        < 0 || a[HOUR]        > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR :
							a[MINUTE]      < 0 || a[MINUTE]      > 59  ? MINUTE :
								a[SECOND]      < 0 || a[SECOND]      > 59  ? SECOND :
									a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND :
										-1;

			if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {
				overflow = DATE;
			}
			if (getParsingFlags(m)._overflowWeeks && overflow === -1) {
				overflow = WEEK;
			}
			if (getParsingFlags(m)._overflowWeekday && overflow === -1) {
				overflow = WEEKDAY;
			}

			getParsingFlags(m).overflow = overflow;
		}

		return m;
	}

// iso 8601 regex
// 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00)
	var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/;
	var basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/;

	var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/;

	var isoDates = [
		['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/],
		['YYYY-MM-DD', /\d{4}-\d\d-\d\d/],
		['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/],
		['GGGG-[W]WW', /\d{4}-W\d\d/, false],
		['YYYY-DDD', /\d{4}-\d{3}/],
		['YYYY-MM', /\d{4}-\d\d/, false],
		['YYYYYYMMDD', /[+-]\d{10}/],
		['YYYYMMDD', /\d{8}/],
		// YYYYMM is NOT allowed by the standard
		['GGGG[W]WWE', /\d{4}W\d{3}/],
		['GGGG[W]WW', /\d{4}W\d{2}/, false],
		['YYYYDDD', /\d{7}/]
	];

// iso time formats and regexes
	var isoTimes = [
		['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/],
		['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/],
		['HH:mm:ss', /\d\d:\d\d:\d\d/],
		['HH:mm', /\d\d:\d\d/],
		['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/],
		['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/],
		['HHmmss', /\d\d\d\d\d\d/],
		['HHmm', /\d\d\d\d/],
		['HH', /\d\d/]
	];

	var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i;

// date from iso format
	function configFromISO(config) {
		var i, l,
			string = config._i,
			match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string),
			allowTime, dateFormat, timeFormat, tzFormat;

		if (match) {
			getParsingFlags(config).iso = true;

			for (i = 0, l = isoDates.length; i < l; i++) {
				if (isoDates[i][1].exec(match[1])) {
					dateFormat = isoDates[i][0];
					allowTime = isoDates[i][2] !== false;
					break;
				}
			}
			if (dateFormat == null) {
				config._isValid = false;
				return;
			}
			if (match[3]) {
				for (i = 0, l = isoTimes.length; i < l; i++) {
					if (isoTimes[i][1].exec(match[3])) {
						// match[2] should be 'T' or space
						timeFormat = (match[2] || ' ') + isoTimes[i][0];
						break;
					}
				}
				if (timeFormat == null) {
					config._isValid = false;
					return;
				}
			}
			if (!allowTime && timeFormat != null) {
				config._isValid = false;
				return;
			}
			if (match[4]) {
				if (tzRegex.exec(match[4])) {
					tzFormat = 'Z';
				} else {
					config._isValid = false;
					return;
				}
			}
			config._f = dateFormat + (timeFormat || '') + (tzFormat || '');
			configFromStringAndFormat(config);
		} else {
			config._isValid = false;
		}
	}

// date from iso format or fallback
	function configFromString(config) {
		var matched = aspNetJsonRegex.exec(config._i);

		if (matched !== null) {
			config._d = new Date(+matched[1]);
			return;
		}

		configFromISO(config);
		if (config._isValid === false) {
			delete config._isValid;
			hooks.createFromInputFallback(config);
		}
	}

	hooks.createFromInputFallback = deprecate(
		'value provided is not in a recognized ISO format. moment construction falls back to js Date(), ' +
		'which is not reliable across all browsers and versions. Non ISO date formats are ' +
		'discouraged and will be removed in an upcoming major release. Please refer to ' +
		'http://momentjs.com/guides/#/warnings/js-date/ for more info.',
		function (config) {
			config._d = new Date(config._i + (config._useUTC ? ' UTC' : ''));
		}
	);

// Pick the first defined of two or three arguments.
	function defaults(a, b, c) {
		if (a != null) {
			return a;
		}
		if (b != null) {
			return b;
		}
		return c;
	}

	function currentDateArray(config) {
		// hooks is actually the exported moment object
		var nowValue = new Date(hooks.now());
		if (config._useUTC) {
			return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()];
		}
		return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()];
	}

// convert an array to a date.
// the array should mirror the parameters below
// note: all values past the year are optional and will default to the lowest possible value.
// [year, month, day , hour, minute, second, millisecond]
	function configFromArray (config) {
		var i, date, input = [], currentDate, yearToUse;

		if (config._d) {
			return;
		}

		currentDate = currentDateArray(config);

		//compute day of the year from weeks and weekdays
		if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
			dayOfYearFromWeekInfo(config);
		}

		//if the day of the year is set, figure out what it is
		if (config._dayOfYear) {
			yearToUse = defaults(config._a[YEAR], currentDate[YEAR]);

			if (config._dayOfYear > daysInYear(yearToUse)) {
				getParsingFlags(config)._overflowDayOfYear = true;
			}

			date = createUTCDate(yearToUse, 0, config._dayOfYear);
			config._a[MONTH] = date.getUTCMonth();
			config._a[DATE] = date.getUTCDate();
		}

		// Default to current date.
		// * if no year, month, day of month are given, default to today
		// * if day of month is given, default month and year
		// * if month is given, default only year
		// * if year is given, don't default anything
		for (i = 0; i < 3 && config._a[i] == null; ++i) {
			config._a[i] = input[i] = currentDate[i];
		}

		// Zero out whatever was not defaulted, including time
		for (; i < 7; i++) {
			config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
		}

		// Check for 24:00:00.000
		if (config._a[HOUR] === 24 &&
			config._a[MINUTE] === 0 &&
			config._a[SECOND] === 0 &&
			config._a[MILLISECOND] === 0) {
			config._nextDay = true;
			config._a[HOUR] = 0;
		}

		config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input);
		// Apply timezone offset from input. The actual utcOffset can be changed
		// with parseZone.
		if (config._tzm != null) {
			config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);
		}

		if (config._nextDay) {
			config._a[HOUR] = 24;
		}
	}

	function dayOfYearFromWeekInfo(config) {
		var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow;

		w = config._w;
		if (w.GG != null || w.W != null || w.E != null) {
			dow = 1;
			doy = 4;

			// TODO: We need to take the current isoWeekYear, but that depends on
			// how we interpret now (local, utc, fixed offset). So create
			// a now version of current config (take local/utc/offset flags, and
			// create now).
			weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year);
			week = defaults(w.W, 1);
			weekday = defaults(w.E, 1);
			if (weekday < 1 || weekday > 7) {
				weekdayOverflow = true;
			}
		} else {
			dow = config._locale._week.dow;
			doy = config._locale._week.doy;

			var curWeek = weekOfYear(createLocal(), dow, doy);

			weekYear = defaults(w.gg, config._a[YEAR], curWeek.year);

			// Default to current week.
			week = defaults(w.w, curWeek.week);

			if (w.d != null) {
				// weekday -- low day numbers are considered next week
				weekday = w.d;
				if (weekday < 0 || weekday > 6) {
					weekdayOverflow = true;
				}
			} else if (w.e != null) {
				// local weekday -- counting starts from begining of week
				weekday = w.e + dow;
				if (w.e < 0 || w.e > 6) {
					weekdayOverflow = true;
				}
			} else {
				// default to begining of week
				weekday = dow;
			}
		}
		if (week < 1 || week > weeksInYear(weekYear, dow, doy)) {
			getParsingFlags(config)._overflowWeeks = true;
		} else if (weekdayOverflow != null) {
			getParsingFlags(config)._overflowWeekday = true;
		} else {
			temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy);
			config._a[YEAR] = temp.year;
			config._dayOfYear = temp.dayOfYear;
		}
	}

// constant that refers to the ISO standard
	hooks.ISO_8601 = function () {};

// date from string and format string
	function configFromStringAndFormat(config) {
		// TODO: Move this to another part of the creation flow to prevent circular deps
		if (config._f === hooks.ISO_8601) {
			configFromISO(config);
			return;
		}

		config._a = [];
		getParsingFlags(config).empty = true;

		// This array is used to make a Date, either with `new Date` or `Date.UTC`
		var string = '' + config._i,
			i, parsedInput, tokens, token, skipped,
			stringLength = string.length,
			totalParsedInputLength = 0;

		tokens = expandFormat(config._f, config._locale).match(formattingTokens) || [];

		for (i = 0; i < tokens.length; i++) {
			token = tokens[i];
			parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0];
			// console.log('token', token, 'parsedInput', parsedInput,
			//         'regex', getParseRegexForToken(token, config));
			if (parsedInput) {
				skipped = string.substr(0, string.indexOf(parsedInput));
				if (skipped.length > 0) {
					getParsingFlags(config).unusedInput.push(skipped);
				}
				string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
				totalParsedInputLength += parsedInput.length;
			}
			// don't parse if it's not a known token
			if (formatTokenFunctions[token]) {
				if (parsedInput) {
					getParsingFlags(config).empty = false;
				}
				else {
					getParsingFlags(config).unusedTokens.push(token);
				}
				addTimeToArrayFromToken(token, parsedInput, config);
			}
			else if (config._strict && !parsedInput) {
				getParsingFlags(config).unusedTokens.push(token);
			}
		}

		// add remaining unparsed input length to the string
		getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength;
		if (string.length > 0) {
			getParsingFlags(config).unusedInput.push(string);
		}

		// clear _12h flag if hour is <= 12
		if (config._a[HOUR] <= 12 &&
			getParsingFlags(config).bigHour === true &&
			config._a[HOUR] > 0) {
			getParsingFlags(config).bigHour = undefined;
		}

		getParsingFlags(config).parsedDateParts = config._a.slice(0);
		getParsingFlags(config).meridiem = config._meridiem;
		// handle meridiem
		config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem);

		configFromArray(config);
		checkOverflow(config);
	}


	function meridiemFixWrap (locale, hour, meridiem) {
		var isPm;

		if (meridiem == null) {
			// nothing to do
			return hour;
		}
		if (locale.meridiemHour != null) {
			return locale.meridiemHour(hour, meridiem);
		} else if (locale.isPM != null) {
			// Fallback
			isPm = locale.isPM(meridiem);
			if (isPm && hour < 12) {
				hour += 12;
			}
			if (!isPm && hour === 12) {
				hour = 0;
			}
			return hour;
		} else {
			// this is not supposed to happen
			return hour;
		}
	}

// date from string and array of format strings
	function configFromStringAndArray(config) {
		var tempConfig,
			bestMoment,

			scoreToBeat,
			i,
			currentScore;

		if (config._f.length === 0) {
			getParsingFlags(config).invalidFormat = true;
			config._d = new Date(NaN);
			return;
		}

		for (i = 0; i < config._f.length; i++) {
			currentScore = 0;
			tempConfig = copyConfig({}, config);
			if (config._useUTC != null) {
				tempConfig._useUTC = config._useUTC;
			}
			tempConfig._f = config._f[i];
			configFromStringAndFormat(tempConfig);

			if (!isValid(tempConfig)) {
				continue;
			}

			// if there is any input that was not parsed add a penalty for that format
			currentScore += getParsingFlags(tempConfig).charsLeftOver;

			//or tokens
			currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10;

			getParsingFlags(tempConfig).score = currentScore;

			if (scoreToBeat == null || currentScore < scoreToBeat) {
				scoreToBeat = currentScore;
				bestMoment = tempConfig;
			}
		}

		extend(config, bestMoment || tempConfig);
	}

	function configFromObject(config) {
		if (config._d) {
			return;
		}

		var i = normalizeObjectUnits(config._i);
		config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) {
			return obj && parseInt(obj, 10);
		});

		configFromArray(config);
	}

	function createFromConfig (config) {
		var res = new Moment(checkOverflow(prepareConfig(config)));
		if (res._nextDay) {
			// Adding is smart enough around DST
			res.add(1, 'd');
			res._nextDay = undefined;
		}

		return res;
	}

	function prepareConfig (config) {
		var input = config._i,
			format = config._f;

		config._locale = config._locale || getLocale(config._l);

		if (input === null || (format === undefined && input === '')) {
			return createInvalid({nullInput: true});
		}

		if (typeof input === 'string') {
			config._i = input = config._locale.preparse(input);
		}

		if (isMoment(input)) {
			return new Moment(checkOverflow(input));
		} else if (isDate(input)) {
			config._d = input;
		} else if (isArray(format)) {
			configFromStringAndArray(config);
		} else if (format) {
			configFromStringAndFormat(config);
		}  else {
			configFromInput(config);
		}

		if (!isValid(config)) {
			config._d = null;
		}

		return config;
	}

	function configFromInput(config) {
		var input = config._i;
		if (input === undefined) {
			config._d = new Date(hooks.now());
		} else if (isDate(input)) {
			config._d = new Date(input.valueOf());
		} else if (typeof input === 'string') {
			configFromString(config);
		} else if (isArray(input)) {
			config._a = map(input.slice(0), function (obj) {
				return parseInt(obj, 10);
			});
			configFromArray(config);
		} else if (typeof(input) === 'object') {
			configFromObject(config);
		} else if (isNumber(input)) {
			// from milliseconds
			config._d = new Date(input);
		} else {
			hooks.createFromInputFallback(config);
		}
	}

	function createLocalOrUTC (input, format, locale, strict, isUTC) {
		var c = {};

		if (locale === true || locale === false) {
			strict = locale;
			locale = undefined;
		}

		if ((isObject(input) && isObjectEmpty(input)) ||
			(isArray(input) && input.length === 0)) {
			input = undefined;
		}
		// object construction must be done this way.
		// https://github.com/moment/moment/issues/1423
		c._isAMomentObject = true;
		c._useUTC = c._isUTC = isUTC;
		c._l = locale;
		c._i = input;
		c._f = format;
		c._strict = strict;

		return createFromConfig(c);
	}

	function createLocal (input, format, locale, strict) {
		return createLocalOrUTC(input, format, locale, strict, false);
	}

	var prototypeMin = deprecate(
		'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/',
		function () {
			var other = createLocal.apply(null, arguments);
			if (this.isValid() && other.isValid()) {
				return other < this ? this : other;
			} else {
				return createInvalid();
			}
		}
	);

	var prototypeMax = deprecate(
		'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/',
		function () {
			var other = createLocal.apply(null, arguments);
			if (this.isValid() && other.isValid()) {
				return other > this ? this : other;
			} else {
				return createInvalid();
			}
		}
	);

// Pick a moment m from moments so that m[fn](other) is true for all
// other. This relies on the function fn to be transitive.
//
// moments should either be an array of moment objects or an array, whose
// first element is an array of moment objects.
	function pickBy(fn, moments) {
		var res, i;
		if (moments.length === 1 && isArray(moments[0])) {
			moments = moments[0];
		}
		if (!moments.length) {
			return createLocal();
		}
		res = moments[0];
		for (i = 1; i < moments.length; ++i) {
			if (!moments[i].isValid() || moments[i][fn](res)) {
				res = moments[i];
			}
		}
		return res;
	}

// TODO: Use [].sort instead?
	function min () {
		var args = [].slice.call(arguments, 0);

		return pickBy('isBefore', args);
	}

	function max () {
		var args = [].slice.call(arguments, 0);

		return pickBy('isAfter', args);
	}

	var now = function () {
		return Date.now ? Date.now() : +(new Date());
	};

	function Duration (duration) {
		var normalizedInput = normalizeObjectUnits(duration),
			years = normalizedInput.year || 0,
			quarters = normalizedInput.quarter || 0,
			months = normalizedInput.month || 0,
			weeks = normalizedInput.week || 0,
			days = normalizedInput.day || 0,
			hours = normalizedInput.hour || 0,
			minutes = normalizedInput.minute || 0,
			seconds = normalizedInput.second || 0,
			milliseconds = normalizedInput.millisecond || 0;

		// representation for dateAddRemove
		this._milliseconds = +milliseconds +
			seconds * 1e3 + // 1000
			minutes * 6e4 + // 1000 * 60
			hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978
		// Because of dateAddRemove treats 24 hours as different from a
		// day when working around DST, we need to store them separately
		this._days = +days +
			weeks * 7;
		// It is impossible translate months into days without knowing
		// which months you are are talking about, so we have to store
		// it separately.
		this._months = +months +
			quarters * 3 +
			years * 12;

		this._data = {};

		this._locale = getLocale();

		this._bubble();
	}

	function isDuration (obj) {
		return obj instanceof Duration;
	}

	function absRound (number) {
		if (number < 0) {
			return Math.round(-1 * number) * -1;
		} else {
			return Math.round(number);
		}
	}

// FORMATTING

	function offset (token, separator) {
		addFormatToken(token, 0, 0, function () {
			var offset = this.utcOffset();
			var sign = '+';
			if (offset < 0) {
				offset = -offset;
				sign = '-';
			}
			return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2);
		});
	}

	offset('Z', ':');
	offset('ZZ', '');

// PARSING

	addRegexToken('Z',  matchShortOffset);
	addRegexToken('ZZ', matchShortOffset);
	addParseToken(['Z', 'ZZ'], function (input, array, config) {
		config._useUTC = true;
		config._tzm = offsetFromString(matchShortOffset, input);
	});

// HELPERS

// timezone chunker
// '+10:00' > ['10',  '00']
// '-1530'  > ['-15', '30']
	var chunkOffset = /([\+\-]|\d\d)/gi;

	function offsetFromString(matcher, string) {
		var matches = (string || '').match(matcher);

		if (matches === null) {
			return null;
		}

		var chunk   = matches[matches.length - 1] || [];
		var parts   = (chunk + '').match(chunkOffset) || ['-', 0, 0];
		var minutes = +(parts[1] * 60) + toInt(parts[2]);

		return minutes === 0 ?
			0 :
			parts[0] === '+' ? minutes : -minutes;
	}

// Return a moment from input, that is local/utc/zone equivalent to model.
	function cloneWithOffset(input, model) {
		var res, diff;
		if (model._isUTC) {
			res = model.clone();
			diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf();
			// Use low-level api, because this fn is low-level api.
			res._d.setTime(res._d.valueOf() + diff);
			hooks.updateOffset(res, false);
			return res;
		} else {
			return createLocal(input).local();
		}
	}

	function getDateOffset (m) {
		// On Firefox.24 Date#getTimezoneOffset returns a floating point.
		// https://github.com/moment/moment/pull/1871
		return -Math.round(m._d.getTimezoneOffset() / 15) * 15;
	}

// HOOKS

// This function will be called whenever a moment is mutated.
// It is intended to keep the offset in sync with the timezone.
	hooks.updateOffset = function () {};

// MOMENTS

// keepLocalTime = true means only change the timezone, without
// affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]-->
// 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset
// +0200, so we adjust the time as needed, to be valid.
//
// Keeping the time actually adds/subtracts (one hour)
// from the actual represented time. That is why we call updateOffset
// a second time. In case it wants us to change the offset again
// _changeInProgress == true case, then we have to adjust, because
// there is no such time in the given timezone.
	function getSetOffset (input, keepLocalTime) {
		var offset = this._offset || 0,
			localAdjust;
		if (!this.isValid()) {
			return input != null ? this : NaN;
		}
		if (input != null) {
			if (typeof input === 'string') {
				input = offsetFromString(matchShortOffset, input);
				if (input === null) {
					return this;
				}
			} else if (Math.abs(input) < 16) {
				input = input * 60;
			}
			if (!this._isUTC && keepLocalTime) {
				localAdjust = getDateOffset(this);
			}
			this._offset = input;
			this._isUTC = true;
			if (localAdjust != null) {
				this.add(localAdjust, 'm');
			}
			if (offset !== input) {
				if (!keepLocalTime || this._changeInProgress) {
					addSubtract(this, createDuration(input - offset, 'm'), 1, false);
				} else if (!this._changeInProgress) {
					this._changeInProgress = true;
					hooks.updateOffset(this, true);
					this._changeInProgress = null;
				}
			}
			return this;
		} else {
			return this._isUTC ? offset : getDateOffset(this);
		}
	}

	function getSetZone (input, keepLocalTime) {
		if (input != null) {
			if (typeof input !== 'string') {
				input = -input;
			}

			this.utcOffset(input, keepLocalTime);

			return this;
		} else {
			return -this.utcOffset();
		}
	}

	function setOffsetToUTC (keepLocalTime) {
		return this.utcOffset(0, keepLocalTime);
	}

	function setOffsetToLocal (keepLocalTime) {
		if (this._isUTC) {
			this.utcOffset(0, keepLocalTime);
			this._isUTC = false;

			if (keepLocalTime) {
				this.subtract(getDateOffset(this), 'm');
			}
		}
		return this;
	}

	function setOffsetToParsedOffset () {
		if (this._tzm != null) {
			this.utcOffset(this._tzm);
		} else if (typeof this._i === 'string') {
			var tZone = offsetFromString(matchOffset, this._i);
			if (tZone != null) {
				this.utcOffset(tZone);
			}
			else {
				this.utcOffset(0, true);
			}
		}
		return this;
	}

	function hasAlignedHourOffset (input) {
		if (!this.isValid()) {
			return false;
		}
		input = input ? createLocal(input).utcOffset() : 0;

		return (this.utcOffset() - input) % 60 === 0;
	}

	function isDaylightSavingTime () {
		return (
			this.utcOffset() > this.clone().month(0).utcOffset() ||
			this.utcOffset() > this.clone().month(5).utcOffset()
		);
	}

	function isDaylightSavingTimeShifted () {
		if (!isUndefined(this._isDSTShifted)) {
			return this._isDSTShifted;
		}

		var c = {};

		copyConfig(c, this);
		c = prepareConfig(c);

		if (c._a) {
			var other = c._isUTC ? createUTC(c._a) : createLocal(c._a);
			this._isDSTShifted = this.isValid() &&
				compareArrays(c._a, other.toArray()) > 0;
		} else {
			this._isDSTShifted = false;
		}

		return this._isDSTShifted;
	}

	function isLocal () {
		return this.isValid() ? !this._isUTC : false;
	}

	function isUtcOffset () {
		return this.isValid() ? this._isUTC : false;
	}

	function isUtc () {
		return this.isValid() ? this._isUTC && this._offset === 0 : false;
	}

// ASP.NET json date format regex
	var aspNetRegex = /^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/;

// from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html
// somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere
// and further modified to allow for strings containing both week and day
	var isoRegex = /^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/;

	function createDuration (input, key) {
		var duration = input,
			// matching against regexp is expensive, do it on demand
			match = null,
			sign,
			ret,
			diffRes;

		if (isDuration(input)) {
			duration = {
				ms : input._milliseconds,
				d  : input._days,
				M  : input._months
			};
		} else if (isNumber(input)) {
			duration = {};
			if (key) {
				duration[key] = input;
			} else {
				duration.milliseconds = input;
			}
		} else if (!!(match = aspNetRegex.exec(input))) {
			sign = (match[1] === '-') ? -1 : 1;
			duration = {
				y  : 0,
				d  : toInt(match[DATE])                         * sign,
				h  : toInt(match[HOUR])                         * sign,
				m  : toInt(match[MINUTE])                       * sign,
				s  : toInt(match[SECOND])                       * sign,
				ms : toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match
			};
		} else if (!!(match = isoRegex.exec(input))) {
			sign = (match[1] === '-') ? -1 : 1;
			duration = {
				y : parseIso(match[2], sign),
				M : parseIso(match[3], sign),
				w : parseIso(match[4], sign),
				d : parseIso(match[5], sign),
				h : parseIso(match[6], sign),
				m : parseIso(match[7], sign),
				s : parseIso(match[8], sign)
			};
		} else if (duration == null) {// checks for null or undefined
			duration = {};
		} else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) {
			diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to));

			duration = {};
			duration.ms = diffRes.milliseconds;
			duration.M = diffRes.months;
		}

		ret = new Duration(duration);

		if (isDuration(input) && hasOwnProp(input, '_locale')) {
			ret._locale = input._locale;
		}

		return ret;
	}

	createDuration.fn = Duration.prototype;

	function parseIso (inp, sign) {
		// We'd normally use ~~inp for this, but unfortunately it also
		// converts floats to ints.
		// inp may be undefined, so careful calling replace on it.
		var res = inp && parseFloat(inp.replace(',', '.'));
		// apply sign while we're at it
		return (isNaN(res) ? 0 : res) * sign;
	}

	function positiveMomentsDifference(base, other) {
		var res = {milliseconds: 0, months: 0};

		res.months = other.month() - base.month() +
			(other.year() - base.year()) * 12;
		if (base.clone().add(res.months, 'M').isAfter(other)) {
			--res.months;
		}

		res.milliseconds = +other - +(base.clone().add(res.months, 'M'));

		return res;
	}

	function momentsDifference(base, other) {
		var res;
		if (!(base.isValid() && other.isValid())) {
			return {milliseconds: 0, months: 0};
		}

		other = cloneWithOffset(other, base);
		if (base.isBefore(other)) {
			res = positiveMomentsDifference(base, other);
		} else {
			res = positiveMomentsDifference(other, base);
			res.milliseconds = -res.milliseconds;
			res.months = -res.months;
		}

		return res;
	}

// TODO: remove 'name' arg after deprecation is removed
	function createAdder(direction, name) {
		return function (val, period) {
			var dur, tmp;
			//invert the arguments, but complain about it
			if (period !== null && !isNaN(+period)) {
				deprecateSimple(name, 'moment().' + name  + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' +
					'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.');
				tmp = val; val = period; period = tmp;
			}

			val = typeof val === 'string' ? +val : val;
			dur = createDuration(val, period);
			addSubtract(this, dur, direction);
			return this;
		};
	}

	function addSubtract (mom, duration, isAdding, updateOffset) {
		var milliseconds = duration._milliseconds,
			days = absRound(duration._days),
			months = absRound(duration._months);

		if (!mom.isValid()) {
			// No op
			return;
		}

		updateOffset = updateOffset == null ? true : updateOffset;

		if (milliseconds) {
			mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding);
		}
		if (days) {
			set$1(mom, 'Date', get(mom, 'Date') + days * isAdding);
		}
		if (months) {
			setMonth(mom, get(mom, 'Month') + months * isAdding);
		}
		if (updateOffset) {
			hooks.updateOffset(mom, days || months);
		}
	}

	var add      = createAdder(1, 'add');
	var subtract = createAdder(-1, 'subtract');

	function getCalendarFormat(myMoment, now) {
		var diff = myMoment.diff(now, 'days', true);
		return diff < -6 ? 'sameElse' :
			diff < -1 ? 'lastWeek' :
				diff < 0 ? 'lastDay' :
					diff < 1 ? 'sameDay' :
						diff < 2 ? 'nextDay' :
							diff < 7 ? 'nextWeek' : 'sameElse';
	}

	function calendar$1 (time, formats) {
		// We want to compare the start of today, vs this.
		// Getting start-of-today depends on whether we're local/utc/offset or not.
		var now = time || createLocal(),
			sod = cloneWithOffset(now, this).startOf('day'),
			format = hooks.calendarFormat(this, sod) || 'sameElse';

		var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]);

		return this.format(output || this.localeData().calendar(format, this, createLocal(now)));
	}

	function clone () {
		return new Moment(this);
	}

	function isAfter (input, units) {
		var localInput = isMoment(input) ? input : createLocal(input);
		if (!(this.isValid() && localInput.isValid())) {
			return false;
		}
		units = normalizeUnits(!isUndefined(units) ? units : 'millisecond');
		if (units === 'millisecond') {
			return this.valueOf() > localInput.valueOf();
		} else {
			return localInput.valueOf() < this.clone().startOf(units).valueOf();
		}
	}

	function isBefore (input, units) {
		var localInput = isMoment(input) ? input : createLocal(input);
		if (!(this.isValid() && localInput.isValid())) {
			return false;
		}
		units = normalizeUnits(!isUndefined(units) ? units : 'millisecond');
		if (units === 'millisecond') {
			return this.valueOf() < localInput.valueOf();
		} else {
			return this.clone().endOf(units).valueOf() < localInput.valueOf();
		}
	}

	function isBetween (from, to, units, inclusivity) {
		inclusivity = inclusivity || '()';
		return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) &&
			(inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units));
	}

	function isSame (input, units) {
		var localInput = isMoment(input) ? input : createLocal(input),
			inputMs;
		if (!(this.isValid() && localInput.isValid())) {
			return false;
		}
		units = normalizeUnits(units || 'millisecond');
		if (units === 'millisecond') {
			return this.valueOf() === localInput.valueOf();
		} else {
			inputMs = localInput.valueOf();
			return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf();
		}
	}

	function isSameOrAfter (input, units) {
		return this.isSame(input, units) || this.isAfter(input,units);
	}

	function isSameOrBefore (input, units) {
		return this.isSame(input, units) || this.isBefore(input,units);
	}

	function diff (input, units, asFloat) {
		var that,
			zoneDelta,
			delta, output;

		if (!this.isValid()) {
			return NaN;
		}

		that = cloneWithOffset(input, this);

		if (!that.isValid()) {
			return NaN;
		}

		zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4;

		units = normalizeUnits(units);

		if (units === 'year' || units === 'month' || units === 'quarter') {
			output = monthDiff(this, that);
			if (units === 'quarter') {
				output = output / 3;
			} else if (units === 'year') {
				output = output / 12;
			}
		} else {
			delta = this - that;
			output = units === 'second' ? delta / 1e3 : // 1000
				units === 'minute' ? delta / 6e4 : // 1000 * 60
					units === 'hour' ? delta / 36e5 : // 1000 * 60 * 60
						units === 'day' ? (delta - zoneDelta) / 864e5 : // 1000 * 60 * 60 * 24, negate dst
							units === 'week' ? (delta - zoneDelta) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst
								delta;
		}
		return asFloat ? output : absFloor(output);
	}

	function monthDiff (a, b) {
		// difference in months
		var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()),
			// b is in (anchor - 1 month, anchor + 1 month)
			anchor = a.clone().add(wholeMonthDiff, 'months'),
			anchor2, adjust;

		if (b - anchor < 0) {
			anchor2 = a.clone().add(wholeMonthDiff - 1, 'months');
			// linear across the month
			adjust = (b - anchor) / (anchor - anchor2);
		} else {
			anchor2 = a.clone().add(wholeMonthDiff + 1, 'months');
			// linear across the month
			adjust = (b - anchor) / (anchor2 - anchor);
		}

		//check for negative zero, return zero if negative zero
		return -(wholeMonthDiff + adjust) || 0;
	}

	hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ';
	hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]';

	function toString () {
		return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ');
	}

	function toISOString () {
		var m = this.clone().utc();
		if (0 < m.year() && m.year() <= 9999) {
			if (isFunction(Date.prototype.toISOString)) {
				// native implementation is ~50x faster, use it when we can
				return this.toDate().toISOString();
			} else {
				return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
			}
		} else {
			return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
		}
	}

	/**
	 * Return a human readable representation of a moment that can
	 * also be evaluated to get a new moment which is the same
	 *
	 * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects
	 */
	function inspect () {
		if (!this.isValid()) {
			return 'moment.invalid(/* ' + this._i + ' */)';
		}
		var func = 'moment';
		var zone = '';
		if (!this.isLocal()) {
			func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone';
			zone = 'Z';
		}
		var prefix = '[' + func + '("]';
		var year = (0 < this.year() && this.year() <= 9999) ? 'YYYY' : 'YYYYYY';
		var datetime = '-MM-DD[T]HH:mm:ss.SSS';
		var suffix = zone + '[")]';

		return this.format(prefix + year + datetime + suffix);
	}

	function format (inputString) {
		if (!inputString) {
			inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat;
		}
		var output = formatMoment(this, inputString);
		return this.localeData().postformat(output);
	}

	function from (time, withoutSuffix) {
		if (this.isValid() &&
			((isMoment(time) && time.isValid()) ||
			createLocal(time).isValid())) {
			return createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix);
		} else {
			return this.localeData().invalidDate();
		}
	}

	function fromNow (withoutSuffix) {
		return this.from(createLocal(), withoutSuffix);
	}

	function to (time, withoutSuffix) {
		if (this.isValid() &&
			((isMoment(time) && time.isValid()) ||
			createLocal(time).isValid())) {
			return createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix);
		} else {
			return this.localeData().invalidDate();
		}
	}

	function toNow (withoutSuffix) {
		return this.to(createLocal(), withoutSuffix);
	}

// If passed a locale key, it will set the locale for this
// instance.  Otherwise, it will return the locale configuration
// variables for this instance.
	function locale (key) {
		var newLocaleData;

		if (key === undefined) {
			return this._locale._abbr;
		} else {
			newLocaleData = getLocale(key);
			if (newLocaleData != null) {
				this._locale = newLocaleData;
			}
			return this;
		}
	}

	var lang = deprecate(
		'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.',
		function (key) {
			if (key === undefined) {
				return this.localeData();
			} else {
				return this.locale(key);
			}
		}
	);

	function localeData () {
		return this._locale;
	}

	function startOf (units) {
		units = normalizeUnits(units);
		// the following switch intentionally omits break keywords
		// to utilize falling through the cases.
		switch (units) {
			case 'year':
				this.month(0);
            /* falls through */
			case 'quarter':
			case 'month':
				this.date(1);
            /* falls through */
			case 'week':
			case 'isoWeek':
			case 'day':
			case 'date':
				this.hours(0);
            /* falls through */
			case 'hour':
				this.minutes(0);
            /* falls through */
			case 'minute':
				this.seconds(0);
            /* falls through */
			case 'second':
				this.milliseconds(0);
		}

		// weeks are a special case
		if (units === 'week') {
			this.weekday(0);
		}
		if (units === 'isoWeek') {
			this.isoWeekday(1);
		}

		// quarters are also special
		if (units === 'quarter') {
			this.month(Math.floor(this.month() / 3) * 3);
		}

		return this;
	}

	function endOf (units) {
		units = normalizeUnits(units);
		if (units === undefined || units === 'millisecond') {
			return this;
		}

		// 'date' is an alias for 'day', so it should be considered as such.
		if (units === 'date') {
			units = 'day';
		}

		return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms');
	}

	function valueOf () {
		return this._d.valueOf() - ((this._offset || 0) * 60000);
	}

	function unix () {
		return Math.floor(this.valueOf() / 1000);
	}

	function toDate () {
		return new Date(this.valueOf());
	}

	function toArray () {
		var m = this;
		return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()];
	}

	function toObject () {
		var m = this;
		return {
			years: m.year(),
			months: m.month(),
			date: m.date(),
			hours: m.hours(),
			minutes: m.minutes(),
			seconds: m.seconds(),
			milliseconds: m.milliseconds()
		};
	}

	function toJSON () {
		// new Date(NaN).toJSON() === null
		return this.isValid() ? this.toISOString() : null;
	}

	function isValid$1 () {
		return isValid(this);
	}

	function parsingFlags () {
		return extend({}, getParsingFlags(this));
	}

	function invalidAt () {
		return getParsingFlags(this).overflow;
	}

	function creationData() {
		return {
			input: this._i,
			format: this._f,
			locale: this._locale,
			isUTC: this._isUTC,
			strict: this._strict
		};
	}

// FORMATTING

	addFormatToken(0, ['gg', 2], 0, function () {
		return this.weekYear() % 100;
	});

	addFormatToken(0, ['GG', 2], 0, function () {
		return this.isoWeekYear() % 100;
	});

	function addWeekYearFormatToken (token, getter) {
		addFormatToken(0, [token, token.length], 0, getter);
	}

	addWeekYearFormatToken('gggg',     'weekYear');
	addWeekYearFormatToken('ggggg',    'weekYear');
	addWeekYearFormatToken('GGGG',  'isoWeekYear');
	addWeekYearFormatToken('GGGGG', 'isoWeekYear');

// ALIASES

	addUnitAlias('weekYear', 'gg');
	addUnitAlias('isoWeekYear', 'GG');

// PRIORITY

	addUnitPriority('weekYear', 1);
	addUnitPriority('isoWeekYear', 1);


// PARSING

	addRegexToken('G',      matchSigned);
	addRegexToken('g',      matchSigned);
	addRegexToken('GG',     match1to2, match2);
	addRegexToken('gg',     match1to2, match2);
	addRegexToken('GGGG',   match1to4, match4);
	addRegexToken('gggg',   match1to4, match4);
	addRegexToken('GGGGG',  match1to6, match6);
	addRegexToken('ggggg',  match1to6, match6);

	addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) {
		week[token.substr(0, 2)] = toInt(input);
	});

	addWeekParseToken(['gg', 'GG'], function (input, week, config, token) {
		week[token] = hooks.parseTwoDigitYear(input);
	});

// MOMENTS

	function getSetWeekYear (input) {
		return getSetWeekYearHelper.call(this,
			input,
			this.week(),
			this.weekday(),
			this.localeData()._week.dow,
			this.localeData()._week.doy);
	}

	function getSetISOWeekYear (input) {
		return getSetWeekYearHelper.call(this,
			input, this.isoWeek(), this.isoWeekday(), 1, 4);
	}

	function getISOWeeksInYear () {
		return weeksInYear(this.year(), 1, 4);
	}

	function getWeeksInYear () {
		var weekInfo = this.localeData()._week;
		return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy);
	}

	function getSetWeekYearHelper(input, week, weekday, dow, doy) {
		var weeksTarget;
		if (input == null) {
			return weekOfYear(this, dow, doy).year;
		} else {
			weeksTarget = weeksInYear(input, dow, doy);
			if (week > weeksTarget) {
				week = weeksTarget;
			}
			return setWeekAll.call(this, input, week, weekday, dow, doy);
		}
	}

	function setWeekAll(weekYear, week, weekday, dow, doy) {
		var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy),
			date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear);

		this.year(date.getUTCFullYear());
		this.month(date.getUTCMonth());
		this.date(date.getUTCDate());
		return this;
	}

// FORMATTING

	addFormatToken('Q', 0, 'Qo', 'quarter');

// ALIASES

	addUnitAlias('quarter', 'Q');

// PRIORITY

	addUnitPriority('quarter', 7);

// PARSING

	addRegexToken('Q', match1);
	addParseToken('Q', function (input, array) {
		array[MONTH] = (toInt(input) - 1) * 3;
	});

// MOMENTS

	function getSetQuarter (input) {
		return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3);
	}

// FORMATTING

	addFormatToken('D', ['DD', 2], 'Do', 'date');

// ALIASES

	addUnitAlias('date', 'D');

// PRIOROITY
	addUnitPriority('date', 9);

// PARSING

	addRegexToken('D',  match1to2);
	addRegexToken('DD', match1to2, match2);
	addRegexToken('Do', function (isStrict, locale) {
		return isStrict ? locale._ordinalParse : locale._ordinalParseLenient;
	});

	addParseToken(['D', 'DD'], DATE);
	addParseToken('Do', function (input, array) {
		array[DATE] = toInt(input.match(match1to2)[0], 10);
	});

// MOMENTS

	var getSetDayOfMonth = makeGetSet('Date', true);

// FORMATTING

	addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear');

// ALIASES

	addUnitAlias('dayOfYear', 'DDD');

// PRIORITY
	addUnitPriority('dayOfYear', 4);

// PARSING

	addRegexToken('DDD',  match1to3);
	addRegexToken('DDDD', match3);
	addParseToken(['DDD', 'DDDD'], function (input, array, config) {
		config._dayOfYear = toInt(input);
	});

// HELPERS

// MOMENTS

	function getSetDayOfYear (input) {
		var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1;
		return input == null ? dayOfYear : this.add((input - dayOfYear), 'd');
	}

// FORMATTING

	addFormatToken('m', ['mm', 2], 0, 'minute');

// ALIASES

	addUnitAlias('minute', 'm');

// PRIORITY

	addUnitPriority('minute', 14);

// PARSING

	addRegexToken('m',  match1to2);
	addRegexToken('mm', match1to2, match2);
	addParseToken(['m', 'mm'], MINUTE);

// MOMENTS

	var getSetMinute = makeGetSet('Minutes', false);

// FORMATTING

	addFormatToken('s', ['ss', 2], 0, 'second');

// ALIASES

	addUnitAlias('second', 's');

// PRIORITY

	addUnitPriority('second', 15);

// PARSING

	addRegexToken('s',  match1to2);
	addRegexToken('ss', match1to2, match2);
	addParseToken(['s', 'ss'], SECOND);

// MOMENTS

	var getSetSecond = makeGetSet('Seconds', false);

// FORMATTING

	addFormatToken('S', 0, 0, function () {
		return ~~(this.millisecond() / 100);
	});

	addFormatToken(0, ['SS', 2], 0, function () {
		return ~~(this.millisecond() / 10);
	});

	addFormatToken(0, ['SSS', 3], 0, 'millisecond');
	addFormatToken(0, ['SSSS', 4], 0, function () {
		return this.millisecond() * 10;
	});
	addFormatToken(0, ['SSSSS', 5], 0, function () {
		return this.millisecond() * 100;
	});
	addFormatToken(0, ['SSSSSS', 6], 0, function () {
		return this.millisecond() * 1000;
	});
	addFormatToken(0, ['SSSSSSS', 7], 0, function () {
		return this.millisecond() * 10000;
	});
	addFormatToken(0, ['SSSSSSSS', 8], 0, function () {
		return this.millisecond() * 100000;
	});
	addFormatToken(0, ['SSSSSSSSS', 9], 0, function () {
		return this.millisecond() * 1000000;
	});


// ALIASES

	addUnitAlias('millisecond', 'ms');

// PRIORITY

	addUnitPriority('millisecond', 16);

// PARSING

	addRegexToken('S',    match1to3, match1);
	addRegexToken('SS',   match1to3, match2);
	addRegexToken('SSS',  match1to3, match3);

	var token;
	for (token = 'SSSS'; token.length <= 9; token += 'S') {
		addRegexToken(token, matchUnsigned);
	}

	function parseMs(input, array) {
		array[MILLISECOND] = toInt(('0.' + input) * 1000);
	}

	for (token = 'S'; token.length <= 9; token += 'S') {
		addParseToken(token, parseMs);
	}
// MOMENTS

	var getSetMillisecond = makeGetSet('Milliseconds', false);

// FORMATTING

	addFormatToken('z',  0, 0, 'zoneAbbr');
	addFormatToken('zz', 0, 0, 'zoneName');

// MOMENTS

	function getZoneAbbr () {
		return this._isUTC ? 'UTC' : '';
	}

	function getZoneName () {
		return this._isUTC ? 'Coordinated Universal Time' : '';
	}

	var proto = Moment.prototype;

	proto.add               = add;
	proto.calendar          = calendar$1;
	proto.clone             = clone;
	proto.diff              = diff;
	proto.endOf             = endOf;
	proto.format            = format;
	proto.from              = from;
	proto.fromNow           = fromNow;
	proto.to                = to;
	proto.toNow             = toNow;
	proto.get               = stringGet;
	proto.invalidAt         = invalidAt;
	proto.isAfter           = isAfter;
	proto.isBefore          = isBefore;
	proto.isBetween         = isBetween;
	proto.isSame            = isSame;
	proto.isSameOrAfter     = isSameOrAfter;
	proto.isSameOrBefore    = isSameOrBefore;
	proto.isValid           = isValid$1;
	proto.lang              = lang;
	proto.locale            = locale;
	proto.localeData        = localeData;
	proto.max               = prototypeMax;
	proto.min               = prototypeMin;
	proto.parsingFlags      = parsingFlags;
	proto.set               = stringSet;
	proto.startOf           = startOf;
	proto.subtract          = subtract;
	proto.toArray           = toArray;
	proto.toObject          = toObject;
	proto.toDate            = toDate;
	proto.toISOString       = toISOString;
	proto.inspect           = inspect;
	proto.toJSON            = toJSON;
	proto.toString          = toString;
	proto.unix              = unix;
	proto.valueOf           = valueOf;
	proto.creationData      = creationData;

// Year
	proto.year       = getSetYear;
	proto.isLeapYear = getIsLeapYear;

// Week Year
	proto.weekYear    = getSetWeekYear;
	proto.isoWeekYear = getSetISOWeekYear;

// Quarter
	proto.quarter = proto.quarters = getSetQuarter;

// Month
	proto.month       = getSetMonth;
	proto.daysInMonth = getDaysInMonth;

// Week
	proto.week           = proto.weeks        = getSetWeek;
	proto.isoWeek        = proto.isoWeeks     = getSetISOWeek;
	proto.weeksInYear    = getWeeksInYear;
	proto.isoWeeksInYear = getISOWeeksInYear;

// Day
	proto.date       = getSetDayOfMonth;
	proto.day        = proto.days             = getSetDayOfWeek;
	proto.weekday    = getSetLocaleDayOfWeek;
	proto.isoWeekday = getSetISODayOfWeek;
	proto.dayOfYear  = getSetDayOfYear;

// Hour
	proto.hour = proto.hours = getSetHour;

// Minute
	proto.minute = proto.minutes = getSetMinute;

// Second
	proto.second = proto.seconds = getSetSecond;

// Millisecond
	proto.millisecond = proto.milliseconds = getSetMillisecond;

// Offset
	proto.utcOffset            = getSetOffset;
	proto.utc                  = setOffsetToUTC;
	proto.local                = setOffsetToLocal;
	proto.parseZone            = setOffsetToParsedOffset;
	proto.hasAlignedHourOffset = hasAlignedHourOffset;
	proto.isDST                = isDaylightSavingTime;
	proto.isLocal              = isLocal;
	proto.isUtcOffset          = isUtcOffset;
	proto.isUtc                = isUtc;
	proto.isUTC                = isUtc;

// Timezone
	proto.zoneAbbr = getZoneAbbr;
	proto.zoneName = getZoneName;

// Deprecations
	proto.dates  = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth);
	proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth);
	proto.years  = deprecate('years accessor is deprecated. Use year instead', getSetYear);
	proto.zone   = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone);
	proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted);

	function createUnix (input) {
		return createLocal(input * 1000);
	}

	function createInZone () {
		return createLocal.apply(null, arguments).parseZone();
	}

	function preParsePostFormat (string) {
		return string;
	}

	var proto$1 = Locale.prototype;

	proto$1.calendar        = calendar;
	proto$1.longDateFormat  = longDateFormat;
	proto$1.invalidDate     = invalidDate;
	proto$1.ordinal         = ordinal;
	proto$1.preparse        = preParsePostFormat;
	proto$1.postformat      = preParsePostFormat;
	proto$1.relativeTime    = relativeTime;
	proto$1.pastFuture      = pastFuture;
	proto$1.set             = set;

// Month
	proto$1.months            =        localeMonths;
	proto$1.monthsShort       =        localeMonthsShort;
	proto$1.monthsParse       =        localeMonthsParse;
	proto$1.monthsRegex       = monthsRegex;
	proto$1.monthsShortRegex  = monthsShortRegex;

// Week
	proto$1.week = localeWeek;
	proto$1.firstDayOfYear = localeFirstDayOfYear;
	proto$1.firstDayOfWeek = localeFirstDayOfWeek;

// Day of Week
	proto$1.weekdays       =        localeWeekdays;
	proto$1.weekdaysMin    =        localeWeekdaysMin;
	proto$1.weekdaysShort  =        localeWeekdaysShort;
	proto$1.weekdaysParse  =        localeWeekdaysParse;

	proto$1.weekdaysRegex       =        weekdaysRegex;
	proto$1.weekdaysShortRegex  =        weekdaysShortRegex;
	proto$1.weekdaysMinRegex    =        weekdaysMinRegex;

// Hours
	proto$1.isPM = localeIsPM;
	proto$1.meridiem = localeMeridiem;

	function get$1 (format, index, field, setter) {
		var locale = getLocale();
		var utc = createUTC().set(setter, index);
		return locale[field](utc, format);
	}

	function listMonthsImpl (format, index, field) {
		if (isNumber(format)) {
			index = format;
			format = undefined;
		}

		format = format || '';

		if (index != null) {
			return get$1(format, index, field, 'month');
		}

		var i;
		var out = [];
		for (i = 0; i < 12; i++) {
			out[i] = get$1(format, i, field, 'month');
		}
		return out;
	}

// ()
// (5)
// (fmt, 5)
// (fmt)
// (true)
// (true, 5)
// (true, fmt, 5)
// (true, fmt)
	function listWeekdaysImpl (localeSorted, format, index, field) {
		if (typeof localeSorted === 'boolean') {
			if (isNumber(format)) {
				index = format;
				format = undefined;
			}

			format = format || '';
		} else {
			format = localeSorted;
			index = format;
			localeSorted = false;

			if (isNumber(format)) {
				index = format;
				format = undefined;
			}

			format = format || '';
		}

		var locale = getLocale(),
			shift = localeSorted ? locale._week.dow : 0;

		if (index != null) {
			return get$1(format, (index + shift) % 7, field, 'day');
		}

		var i;
		var out = [];
		for (i = 0; i < 7; i++) {
			out[i] = get$1(format, (i + shift) % 7, field, 'day');
		}
		return out;
	}

	function listMonths (format, index) {
		return listMonthsImpl(format, index, 'months');
	}

	function listMonthsShort (format, index) {
		return listMonthsImpl(format, index, 'monthsShort');
	}

	function listWeekdays (localeSorted, format, index) {
		return listWeekdaysImpl(localeSorted, format, index, 'weekdays');
	}

	function listWeekdaysShort (localeSorted, format, index) {
		return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort');
	}

	function listWeekdaysMin (localeSorted, format, index) {
		return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin');
	}

	getSetGlobalLocale('en', {
		ordinalParse: /\d{1,2}(th|st|nd|rd)/,
		ordinal : function (number) {
			var b = number % 10,
				output = (toInt(number % 100 / 10) === 1) ? 'th' :
					(b === 1) ? 'st' :
						(b === 2) ? 'nd' :
							(b === 3) ? 'rd' : 'th';
			return number + output;
		}
	});

// Side effect imports
	hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale);
	hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale);

	var mathAbs = Math.abs;

	function abs () {
		var data           = this._data;

		this._milliseconds = mathAbs(this._milliseconds);
		this._days         = mathAbs(this._days);
		this._months       = mathAbs(this._months);

		data.milliseconds  = mathAbs(data.milliseconds);
		data.seconds       = mathAbs(data.seconds);
		data.minutes       = mathAbs(data.minutes);
		data.hours         = mathAbs(data.hours);
		data.months        = mathAbs(data.months);
		data.years         = mathAbs(data.years);

		return this;
	}

	function addSubtract$1 (duration, input, value, direction) {
		var other = createDuration(input, value);

		duration._milliseconds += direction * other._milliseconds;
		duration._days         += direction * other._days;
		duration._months       += direction * other._months;

		return duration._bubble();
	}

// supports only 2.0-style add(1, 's') or add(duration)
	function add$1 (input, value) {
		return addSubtract$1(this, input, value, 1);
	}

// supports only 2.0-style subtract(1, 's') or subtract(duration)
	function subtract$1 (input, value) {
		return addSubtract$1(this, input, value, -1);
	}

	function absCeil (number) {
		if (number < 0) {
			return Math.floor(number);
		} else {
			return Math.ceil(number);
		}
	}

	function bubble () {
		var milliseconds = this._milliseconds;
		var days         = this._days;
		var months       = this._months;
		var data         = this._data;
		var seconds, minutes, hours, years, monthsFromDays;

		// if we have a mix of positive and negative values, bubble down first
		// check: https://github.com/moment/moment/issues/2166
		if (!((milliseconds >= 0 && days >= 0 && months >= 0) ||
			(milliseconds <= 0 && days <= 0 && months <= 0))) {
			milliseconds += absCeil(monthsToDays(months) + days) * 864e5;
			days = 0;
			months = 0;
		}

		// The following code bubbles up values, see the tests for
		// examples of what that means.
		data.milliseconds = milliseconds % 1000;

		seconds           = absFloor(milliseconds / 1000);
		data.seconds      = seconds % 60;

		minutes           = absFloor(seconds / 60);
		data.minutes      = minutes % 60;

		hours             = absFloor(minutes / 60);
		data.hours        = hours % 24;

		days += absFloor(hours / 24);

		// convert days to months
		monthsFromDays = absFloor(daysToMonths(days));
		months += monthsFromDays;
		days -= absCeil(monthsToDays(monthsFromDays));

		// 12 months -> 1 year
		years = absFloor(months / 12);
		months %= 12;

		data.days   = days;
		data.months = months;
		data.years  = years;

		return this;
	}

	function daysToMonths (days) {
		// 400 years have 146097 days (taking into account leap year rules)
		// 400 years have 12 months === 4800
		return days * 4800 / 146097;
	}

	function monthsToDays (months) {
		// the reverse of daysToMonths
		return months * 146097 / 4800;
	}

	function as (units) {
		var days;
		var months;
		var milliseconds = this._milliseconds;

		units = normalizeUnits(units);

		if (units === 'month' || units === 'year') {
			days   = this._days   + milliseconds / 864e5;
			months = this._months + daysToMonths(days);
			return units === 'month' ? months : months / 12;
		} else {
			// handle milliseconds separately because of floating point math errors (issue #1867)
			days = this._days + Math.round(monthsToDays(this._months));
			switch (units) {
				case 'week'   : return days / 7     + milliseconds / 6048e5;
				case 'day'    : return days         + milliseconds / 864e5;
				case 'hour'   : return days * 24    + milliseconds / 36e5;
				case 'minute' : return days * 1440  + milliseconds / 6e4;
				case 'second' : return days * 86400 + milliseconds / 1000;
				// Math.floor prevents floating point math errors here
				case 'millisecond': return Math.floor(days * 864e5) + milliseconds;
				default: throw new Error('Unknown unit ' + units);
			}
		}
	}

// TODO: Use this.as('ms')?
	function valueOf$1 () {
		return (
			this._milliseconds +
			this._days * 864e5 +
			(this._months % 12) * 2592e6 +
			toInt(this._months / 12) * 31536e6
		);
	}

	function makeAs (alias) {
		return function () {
			return this.as(alias);
		};
	}

	var asMilliseconds = makeAs('ms');
	var asSeconds      = makeAs('s');
	var asMinutes      = makeAs('m');
	var asHours        = makeAs('h');
	var asDays         = makeAs('d');
	var asWeeks        = makeAs('w');
	var asMonths       = makeAs('M');
	var asYears        = makeAs('y');

	function get$2 (units) {
		units = normalizeUnits(units);
		return this[units + 's']();
	}

	function makeGetter(name) {
		return function () {
			return this._data[name];
		};
	}

	var milliseconds = makeGetter('milliseconds');
	var seconds      = makeGetter('seconds');
	var minutes      = makeGetter('minutes');
	var hours        = makeGetter('hours');
	var days         = makeGetter('days');
	var months       = makeGetter('months');
	var years        = makeGetter('years');

	function weeks () {
		return absFloor(this.days() / 7);
	}

	var round = Math.round;
	var thresholds = {
		s: 45,  // seconds to minute
		m: 45,  // minutes to hour
		h: 22,  // hours to day
		d: 26,  // days to month
		M: 11   // months to year
	};

// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
	function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) {
		return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
	}

	function relativeTime$1 (posNegDuration, withoutSuffix, locale) {
		var duration = createDuration(posNegDuration).abs();
		var seconds  = round(duration.as('s'));
		var minutes  = round(duration.as('m'));
		var hours    = round(duration.as('h'));
		var days     = round(duration.as('d'));
		var months   = round(duration.as('M'));
		var years    = round(duration.as('y'));

		var a = seconds < thresholds.s && ['s', seconds]  ||
			minutes <= 1           && ['m']           ||
			minutes < thresholds.m && ['mm', minutes] ||
			hours   <= 1           && ['h']           ||
			hours   < thresholds.h && ['hh', hours]   ||
			days    <= 1           && ['d']           ||
			days    < thresholds.d && ['dd', days]    ||
			months  <= 1           && ['M']           ||
			months  < thresholds.M && ['MM', months]  ||
			years   <= 1           && ['y']           || ['yy', years];

		a[2] = withoutSuffix;
		a[3] = +posNegDuration > 0;
		a[4] = locale;
		return substituteTimeAgo.apply(null, a);
	}

// This function allows you to set the rounding function for relative time strings
	function getSetRelativeTimeRounding (roundingFunction) {
		if (roundingFunction === undefined) {
			return round;
		}
		if (typeof(roundingFunction) === 'function') {
			round = roundingFunction;
			return true;
		}
		return false;
	}

// This function allows you to set a threshold for relative time strings
	function getSetRelativeTimeThreshold (threshold, limit) {
		if (thresholds[threshold] === undefined) {
			return false;
		}
		if (limit === undefined) {
			return thresholds[threshold];
		}
		thresholds[threshold] = limit;
		return true;
	}

	function humanize (withSuffix) {
		var locale = this.localeData();
		var output = relativeTime$1(this, !withSuffix, locale);

		if (withSuffix) {
			output = locale.pastFuture(+this, output);
		}

		return locale.postformat(output);
	}

	var abs$1 = Math.abs;

	function toISOString$1() {
		// for ISO strings we do not use the normal bubbling rules:
		//  * milliseconds bubble up until they become hours
		//  * days do not bubble at all
		//  * months bubble up until they become years
		// This is because there is no context-free conversion between hours and days
		// (think of clock changes)
		// and also not between days and months (28-31 days per month)
		var seconds = abs$1(this._milliseconds) / 1000;
		var days         = abs$1(this._days);
		var months       = abs$1(this._months);
		var minutes, hours, years;

		// 3600 seconds -> 60 minutes -> 1 hour
		minutes           = absFloor(seconds / 60);
		hours             = absFloor(minutes / 60);
		seconds %= 60;
		minutes %= 60;

		// 12 months -> 1 year
		years  = absFloor(months / 12);
		months %= 12;


		// inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
		var Y = years;
		var M = months;
		var D = days;
		var h = hours;
		var m = minutes;
		var s = seconds;
		var total = this.asSeconds();

		if (!total) {
			// this is the same as C#'s (Noda) and python (isodate)...
			// but not other JS (goog.date)
			return 'P0D';
		}

		return (total < 0 ? '-' : '') +
			'P' +
			(Y ? Y + 'Y' : '') +
			(M ? M + 'M' : '') +
			(D ? D + 'D' : '') +
			((h || m || s) ? 'T' : '') +
			(h ? h + 'H' : '') +
			(m ? m + 'M' : '') +
			(s ? s + 'S' : '');
	}

	var proto$2 = Duration.prototype;

	proto$2.abs            = abs;
	proto$2.add            = add$1;
	proto$2.subtract       = subtract$1;
	proto$2.as             = as;
	proto$2.asMilliseconds = asMilliseconds;
	proto$2.asSeconds      = asSeconds;
	proto$2.asMinutes      = asMinutes;
	proto$2.asHours        = asHours;
	proto$2.asDays         = asDays;
	proto$2.asWeeks        = asWeeks;
	proto$2.asMonths       = asMonths;
	proto$2.asYears        = asYears;
	proto$2.valueOf        = valueOf$1;
	proto$2._bubble        = bubble;
	proto$2.get            = get$2;
	proto$2.milliseconds   = milliseconds;
	proto$2.seconds        = seconds;
	proto$2.minutes        = minutes;
	proto$2.hours          = hours;
	proto$2.days           = days;
	proto$2.weeks          = weeks;
	proto$2.months         = months;
	proto$2.years          = years;
	proto$2.humanize       = humanize;
	proto$2.toISOString    = toISOString$1;
	proto$2.toString       = toISOString$1;
	proto$2.toJSON         = toISOString$1;
	proto$2.locale         = locale;
	proto$2.localeData     = localeData;

// Deprecations
	proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1);
	proto$2.lang = lang;

// Side effect imports

// FORMATTING

	addFormatToken('X', 0, 0, 'unix');
	addFormatToken('x', 0, 0, 'valueOf');

// PARSING

	addRegexToken('x', matchSigned);
	addRegexToken('X', matchTimestamp);
	addParseToken('X', function (input, array, config) {
		config._d = new Date(parseFloat(input, 10) * 1000);
	});
	addParseToken('x', function (input, array, config) {
		config._d = new Date(toInt(input));
	});

// Side effect imports

//! moment.js
//! version : 2.17.1
//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
//! license : MIT
//! momentjs.com

	hooks.version = '2.17.1';

	setHookCallback(createLocal);

	hooks.fn                    = proto;
	hooks.min                   = min;
	hooks.max                   = max;
	hooks.now                   = now;
	hooks.utc                   = createUTC;
	hooks.unix                  = createUnix;
	hooks.months                = listMonths;
	hooks.isDate                = isDate;
	hooks.locale                = getSetGlobalLocale;
	hooks.invalid               = createInvalid;
	hooks.duration              = createDuration;
	hooks.isMoment              = isMoment;
	hooks.weekdays              = listWeekdays;
	hooks.parseZone             = createInZone;
	hooks.localeData            = getLocale;
	hooks.isDuration            = isDuration;
	hooks.monthsShort           = listMonthsShort;
	hooks.weekdaysMin           = listWeekdaysMin;
	hooks.defineLocale          = defineLocale;
	hooks.updateLocale          = updateLocale;
	hooks.locales               = listLocales;
	hooks.weekdaysShort         = listWeekdaysShort;
	hooks.normalizeUnits        = normalizeUnits;
	hooks.relativeTimeRounding = getSetRelativeTimeRounding;
	hooks.relativeTimeThreshold = getSetRelativeTimeThreshold;
	hooks.calendarFormat        = getCalendarFormat;
	hooks.prototype             = proto;

//! moment.js locale configuration
//! locale : Afrikaans [af]
//! author : Werner Mollentze : https://github.com/wernerm

	hooks.defineLocale('af', {
		months : 'Januarie_Februarie_Maart_April_Mei_Junie_Julie_Augustus_September_Oktober_November_Desember'.split('_'),
		monthsShort : 'Jan_Feb_Mrt_Apr_Mei_Jun_Jul_Aug_Sep_Okt_Nov_Des'.split('_'),
		weekdays : 'Sondag_Maandag_Dinsdag_Woensdag_Donderdag_Vrydag_Saterdag'.split('_'),
		weekdaysShort : 'Son_Maa_Din_Woe_Don_Vry_Sat'.split('_'),
		weekdaysMin : 'So_Ma_Di_Wo_Do_Vr_Sa'.split('_'),
		meridiemParse: /vm|nm/i,
		isPM : function (input) {
			return /^nm$/i.test(input);
		},
		meridiem : function (hours, minutes, isLower) {
			if (hours < 12) {
				return isLower ? 'vm' : 'VM';
			} else {
				return isLower ? 'nm' : 'NM';
			}
		},
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd, D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay : '[Vandag om] LT',
			nextDay : '[Môre om] LT',
			nextWeek : 'dddd [om] LT',
			lastDay : '[Gister om] LT',
			lastWeek : '[Laas] dddd [om] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'oor %s',
			past : '%s gelede',
			s : '\'n paar sekondes',
			m : '\'n minuut',
			mm : '%d minute',
			h : '\'n uur',
			hh : '%d ure',
			d : '\'n dag',
			dd : '%d dae',
			M : '\'n maand',
			MM : '%d maande',
			y : '\'n jaar',
			yy : '%d jaar'
		},
		ordinalParse: /\d{1,2}(ste|de)/,
		ordinal : function (number) {
			return number + ((number === 1 || number === 8 || number >= 20) ? 'ste' : 'de'); // Thanks to Joris Röling : https://github.com/jjupiter
		},
		week : {
			dow : 1, // Maandag is die eerste dag van die week.
			doy : 4  // Die week wat die 4de Januarie bevat is die eerste week van die jaar.
		}
	});

//! moment.js locale configuration
//! locale : Arabic (Algeria) [ar-dz]
//! author : Noureddine LOUAHEDJ : https://github.com/noureddineme

	hooks.defineLocale('ar-dz', {
		months : 'جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'),
		monthsShort : 'جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'),
		weekdays : 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'),
		weekdaysShort : 'احد_اثنين_ثلاثاء_اربعاء_خميس_جمعة_سبت'.split('_'),
		weekdaysMin : 'أح_إث_ثلا_أر_خم_جم_سب'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay: '[اليوم على الساعة] LT',
			nextDay: '[غدا على الساعة] LT',
			nextWeek: 'dddd [على الساعة] LT',
			lastDay: '[أمس على الساعة] LT',
			lastWeek: 'dddd [على الساعة] LT',
			sameElse: 'L'
		},
		relativeTime : {
			future : 'في %s',
			past : 'منذ %s',
			s : 'ثوان',
			m : 'دقيقة',
			mm : '%d دقائق',
			h : 'ساعة',
			hh : '%d ساعات',
			d : 'يوم',
			dd : '%d أيام',
			M : 'شهر',
			MM : '%d أشهر',
			y : 'سنة',
			yy : '%d سنوات'
		},
		week : {
			dow : 0, // Sunday is the first day of the week.
			doy : 4  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Arabic (Lybia) [ar-ly]
//! author : Ali Hmer: https://github.com/kikoanis

	var symbolMap = {
		'1': '1',
		'2': '2',
		'3': '3',
		'4': '4',
		'5': '5',
		'6': '6',
		'7': '7',
		'8': '8',
		'9': '9',
		'0': '0'
	};
	var pluralForm = function (n) {
		return n === 0 ? 0 : n === 1 ? 1 : n === 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 ? 4 : 5;
	};
	var plurals = {
		s : ['أقل من ثانية', 'ثانية واحدة', ['ثانيتان', 'ثانيتين'], '%d ثوان', '%d ثانية', '%d ثانية'],
		m : ['أقل من دقيقة', 'دقيقة واحدة', ['دقيقتان', 'دقيقتين'], '%d دقائق', '%d دقيقة', '%d دقيقة'],
		h : ['أقل من ساعة', 'ساعة واحدة', ['ساعتان', 'ساعتين'], '%d ساعات', '%d ساعة', '%d ساعة'],
		d : ['أقل من يوم', 'يوم واحد', ['يومان', 'يومين'], '%d أيام', '%d يومًا', '%d يوم'],
		M : ['أقل من شهر', 'شهر واحد', ['شهران', 'شهرين'], '%d أشهر', '%d شهرا', '%d شهر'],
		y : ['أقل من عام', 'عام واحد', ['عامان', 'عامين'], '%d أعوام', '%d عامًا', '%d عام']
	};
	var pluralize = function (u) {
		return function (number, withoutSuffix, string, isFuture) {
			var f = pluralForm(number),
				str = plurals[u][pluralForm(number)];
			if (f === 2) {
				str = str[withoutSuffix ? 0 : 1];
			}
			return str.replace(/%d/i, number);
		};
	};
	var months$1 = [
		'يناير',
		'فبراير',
		'مارس',
		'أبريل',
		'مايو',
		'يونيو',
		'يوليو',
		'أغسطس',
		'سبتمبر',
		'أكتوبر',
		'نوفمبر',
		'ديسمبر'
	];

	hooks.defineLocale('ar-ly', {
		months : months$1,
		monthsShort : months$1,
		weekdays : 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'),
		weekdaysShort : 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'),
		weekdaysMin : 'ح_ن_ث_ر_خ_ج_س'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'D/\u200FM/\u200FYYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd D MMMM YYYY HH:mm'
		},
		meridiemParse: /ص|م/,
		isPM : function (input) {
			return 'م' === input;
		},
		meridiem : function (hour, minute, isLower) {
			if (hour < 12) {
				return 'ص';
			} else {
				return 'م';
			}
		},
		calendar : {
			sameDay: '[اليوم عند الساعة] LT',
			nextDay: '[غدًا عند الساعة] LT',
			nextWeek: 'dddd [عند الساعة] LT',
			lastDay: '[أمس عند الساعة] LT',
			lastWeek: 'dddd [عند الساعة] LT',
			sameElse: 'L'
		},
		relativeTime : {
			future : 'بعد %s',
			past : 'منذ %s',
			s : pluralize('s'),
			m : pluralize('m'),
			mm : pluralize('m'),
			h : pluralize('h'),
			hh : pluralize('h'),
			d : pluralize('d'),
			dd : pluralize('d'),
			M : pluralize('M'),
			MM : pluralize('M'),
			y : pluralize('y'),
			yy : pluralize('y')
		},
		preparse: function (string) {
			return string.replace(/\u200f/g, '').replace(/،/g, ',');
		},
		postformat: function (string) {
			return string.replace(/\d/g, function (match) {
				return symbolMap[match];
			}).replace(/,/g, '،');
		},
		week : {
			dow : 6, // Saturday is the first day of the week.
			doy : 12  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Arabic (Morocco) [ar-ma]
//! author : ElFadili Yassine : https://github.com/ElFadiliY
//! author : Abdel Said : https://github.com/abdelsaid

	hooks.defineLocale('ar-ma', {
		months : 'يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر'.split('_'),
		monthsShort : 'يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر'.split('_'),
		weekdays : 'الأحد_الإتنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'),
		weekdaysShort : 'احد_اتنين_ثلاثاء_اربعاء_خميس_جمعة_سبت'.split('_'),
		weekdaysMin : 'ح_ن_ث_ر_خ_ج_س'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay: '[اليوم على الساعة] LT',
			nextDay: '[غدا على الساعة] LT',
			nextWeek: 'dddd [على الساعة] LT',
			lastDay: '[أمس على الساعة] LT',
			lastWeek: 'dddd [على الساعة] LT',
			sameElse: 'L'
		},
		relativeTime : {
			future : 'في %s',
			past : 'منذ %s',
			s : 'ثوان',
			m : 'دقيقة',
			mm : '%d دقائق',
			h : 'ساعة',
			hh : '%d ساعات',
			d : 'يوم',
			dd : '%d أيام',
			M : 'شهر',
			MM : '%d أشهر',
			y : 'سنة',
			yy : '%d سنوات'
		},
		week : {
			dow : 6, // Saturday is the first day of the week.
			doy : 12  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Arabic (Saudi Arabia) [ar-sa]
//! author : Suhail Alkowaileet : https://github.com/xsoh

	var symbolMap$1 = {
		'1': '١',
		'2': '٢',
		'3': '٣',
		'4': '٤',
		'5': '٥',
		'6': '٦',
		'7': '٧',
		'8': '٨',
		'9': '٩',
		'0': '٠'
	};
	var numberMap = {
		'١': '1',
		'٢': '2',
		'٣': '3',
		'٤': '4',
		'٥': '5',
		'٦': '6',
		'٧': '7',
		'٨': '8',
		'٩': '9',
		'٠': '0'
	};

	hooks.defineLocale('ar-sa', {
		months : 'يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'),
		monthsShort : 'يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'),
		weekdays : 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'),
		weekdaysShort : 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'),
		weekdaysMin : 'ح_ن_ث_ر_خ_ج_س'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd D MMMM YYYY HH:mm'
		},
		meridiemParse: /ص|م/,
		isPM : function (input) {
			return 'م' === input;
		},
		meridiem : function (hour, minute, isLower) {
			if (hour < 12) {
				return 'ص';
			} else {
				return 'م';
			}
		},
		calendar : {
			sameDay: '[اليوم على الساعة] LT',
			nextDay: '[غدا على الساعة] LT',
			nextWeek: 'dddd [على الساعة] LT',
			lastDay: '[أمس على الساعة] LT',
			lastWeek: 'dddd [على الساعة] LT',
			sameElse: 'L'
		},
		relativeTime : {
			future : 'في %s',
			past : 'منذ %s',
			s : 'ثوان',
			m : 'دقيقة',
			mm : '%d دقائق',
			h : 'ساعة',
			hh : '%d ساعات',
			d : 'يوم',
			dd : '%d أيام',
			M : 'شهر',
			MM : '%d أشهر',
			y : 'سنة',
			yy : '%d سنوات'
		},
		preparse: function (string) {
			return string.replace(/[١٢٣٤٥٦٧٨٩٠]/g, function (match) {
				return numberMap[match];
			}).replace(/،/g, ',');
		},
		postformat: function (string) {
			return string.replace(/\d/g, function (match) {
				return symbolMap$1[match];
			}).replace(/,/g, '،');
		},
		week : {
			dow : 0, // Sunday is the first day of the week.
			doy : 6  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale  :  Arabic (Tunisia) [ar-tn]
//! author : Nader Toukabri : https://github.com/naderio

	hooks.defineLocale('ar-tn', {
		months: 'جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'),
		monthsShort: 'جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'),
		weekdays: 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'),
		weekdaysShort: 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'),
		weekdaysMin: 'ح_ن_ث_ر_خ_ج_س'.split('_'),
		weekdaysParseExact : true,
		longDateFormat: {
			LT: 'HH:mm',
			LTS: 'HH:mm:ss',
			L: 'DD/MM/YYYY',
			LL: 'D MMMM YYYY',
			LLL: 'D MMMM YYYY HH:mm',
			LLLL: 'dddd D MMMM YYYY HH:mm'
		},
		calendar: {
			sameDay: '[اليوم على الساعة] LT',
			nextDay: '[غدا على الساعة] LT',
			nextWeek: 'dddd [على الساعة] LT',
			lastDay: '[أمس على الساعة] LT',
			lastWeek: 'dddd [على الساعة] LT',
			sameElse: 'L'
		},
		relativeTime: {
			future: 'في %s',
			past: 'منذ %s',
			s: 'ثوان',
			m: 'دقيقة',
			mm: '%d دقائق',
			h: 'ساعة',
			hh: '%d ساعات',
			d: 'يوم',
			dd: '%d أيام',
			M: 'شهر',
			MM: '%d أشهر',
			y: 'سنة',
			yy: '%d سنوات'
		},
		week: {
			dow: 1, // Monday is the first day of the week.
			doy: 4 // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Arabic [ar]
//! author : Abdel Said: https://github.com/abdelsaid
//! author : Ahmed Elkhatib
//! author : forabi https://github.com/forabi

	var symbolMap$2 = {
		'1': '١',
		'2': '٢',
		'3': '٣',
		'4': '٤',
		'5': '٥',
		'6': '٦',
		'7': '٧',
		'8': '٨',
		'9': '٩',
		'0': '٠'
	};
	var numberMap$1 = {
		'١': '1',
		'٢': '2',
		'٣': '3',
		'٤': '4',
		'٥': '5',
		'٦': '6',
		'٧': '7',
		'٨': '8',
		'٩': '9',
		'٠': '0'
	};
	var pluralForm$1 = function (n) {
		return n === 0 ? 0 : n === 1 ? 1 : n === 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 ? 4 : 5;
	};
	var plurals$1 = {
		s : ['أقل من ثانية', 'ثانية واحدة', ['ثانيتان', 'ثانيتين'], '%d ثوان', '%d ثانية', '%d ثانية'],
		m : ['أقل من دقيقة', 'دقيقة واحدة', ['دقيقتان', 'دقيقتين'], '%d دقائق', '%d دقيقة', '%d دقيقة'],
		h : ['أقل من ساعة', 'ساعة واحدة', ['ساعتان', 'ساعتين'], '%d ساعات', '%d ساعة', '%d ساعة'],
		d : ['أقل من يوم', 'يوم واحد', ['يومان', 'يومين'], '%d أيام', '%d يومًا', '%d يوم'],
		M : ['أقل من شهر', 'شهر واحد', ['شهران', 'شهرين'], '%d أشهر', '%d شهرا', '%d شهر'],
		y : ['أقل من عام', 'عام واحد', ['عامان', 'عامين'], '%d أعوام', '%d عامًا', '%d عام']
	};
	var pluralize$1 = function (u) {
		return function (number, withoutSuffix, string, isFuture) {
			var f = pluralForm$1(number),
				str = plurals$1[u][pluralForm$1(number)];
			if (f === 2) {
				str = str[withoutSuffix ? 0 : 1];
			}
			return str.replace(/%d/i, number);
		};
	};
	var months$2 = [
		'كانون الثاني يناير',
		'شباط فبراير',
		'آذار مارس',
		'نيسان أبريل',
		'أيار مايو',
		'حزيران يونيو',
		'تموز يوليو',
		'آب أغسطس',
		'أيلول سبتمبر',
		'تشرين الأول أكتوبر',
		'تشرين الثاني نوفمبر',
		'كانون الأول ديسمبر'
	];

	hooks.defineLocale('ar', {
		months : months$2,
		monthsShort : months$2,
		weekdays : 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'),
		weekdaysShort : 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'),
		weekdaysMin : 'ح_ن_ث_ر_خ_ج_س'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'D/\u200FM/\u200FYYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd D MMMM YYYY HH:mm'
		},
		meridiemParse: /ص|م/,
		isPM : function (input) {
			return 'م' === input;
		},
		meridiem : function (hour, minute, isLower) {
			if (hour < 12) {
				return 'ص';
			} else {
				return 'م';
			}
		},
		calendar : {
			sameDay: '[اليوم عند الساعة] LT',
			nextDay: '[غدًا عند الساعة] LT',
			nextWeek: 'dddd [عند الساعة] LT',
			lastDay: '[أمس عند الساعة] LT',
			lastWeek: 'dddd [عند الساعة] LT',
			sameElse: 'L'
		},
		relativeTime : {
			future : 'بعد %s',
			past : 'منذ %s',
			s : pluralize$1('s'),
			m : pluralize$1('m'),
			mm : pluralize$1('m'),
			h : pluralize$1('h'),
			hh : pluralize$1('h'),
			d : pluralize$1('d'),
			dd : pluralize$1('d'),
			M : pluralize$1('M'),
			MM : pluralize$1('M'),
			y : pluralize$1('y'),
			yy : pluralize$1('y')
		},
		preparse: function (string) {
			return string.replace(/\u200f/g, '').replace(/[١٢٣٤٥٦٧٨٩٠]/g, function (match) {
				return numberMap$1[match];
			}).replace(/،/g, ',');
		},
		postformat: function (string) {
			return string.replace(/\d/g, function (match) {
				return symbolMap$2[match];
			}).replace(/,/g, '،');
		},
		week : {
			dow : 6, // Saturday is the first day of the week.
			doy : 12  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Azerbaijani [az]
//! author : topchiyev : https://github.com/topchiyev

	var suffixes = {
		1: '-inci',
		5: '-inci',
		8: '-inci',
		70: '-inci',
		80: '-inci',
		2: '-nci',
		7: '-nci',
		20: '-nci',
		50: '-nci',
		3: '-üncü',
		4: '-üncü',
		100: '-üncü',
		6: '-ncı',
		9: '-uncu',
		10: '-uncu',
		30: '-uncu',
		60: '-ıncı',
		90: '-ıncı'
	};

	hooks.defineLocale('az', {
		months : 'yanvar_fevral_mart_aprel_may_iyun_iyul_avqust_sentyabr_oktyabr_noyabr_dekabr'.split('_'),
		monthsShort : 'yan_fev_mar_apr_may_iyn_iyl_avq_sen_okt_noy_dek'.split('_'),
		weekdays : 'Bazar_Bazar ertəsi_Çərşənbə axşamı_Çərşənbə_Cümə axşamı_Cümə_Şənbə'.split('_'),
		weekdaysShort : 'Baz_BzE_ÇAx_Çər_CAx_Cüm_Şən'.split('_'),
		weekdaysMin : 'Bz_BE_ÇA_Çə_CA_Cü_Şə'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd, D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay : '[bugün saat] LT',
			nextDay : '[sabah saat] LT',
			nextWeek : '[gələn həftə] dddd [saat] LT',
			lastDay : '[dünən] LT',
			lastWeek : '[keçən həftə] dddd [saat] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : '%s sonra',
			past : '%s əvvəl',
			s : 'birneçə saniyyə',
			m : 'bir dəqiqə',
			mm : '%d dəqiqə',
			h : 'bir saat',
			hh : '%d saat',
			d : 'bir gün',
			dd : '%d gün',
			M : 'bir ay',
			MM : '%d ay',
			y : 'bir il',
			yy : '%d il'
		},
		meridiemParse: /gecə|səhər|gündüz|axşam/,
		isPM : function (input) {
			return /^(gündüz|axşam)$/.test(input);
		},
		meridiem : function (hour, minute, isLower) {
			if (hour < 4) {
				return 'gecə';
			} else if (hour < 12) {
				return 'səhər';
			} else if (hour < 17) {
				return 'gündüz';
			} else {
				return 'axşam';
			}
		},
		ordinalParse: /\d{1,2}-(ıncı|inci|nci|üncü|ncı|uncu)/,
		ordinal : function (number) {
			if (number === 0) {  // special case for zero
				return number + '-ıncı';
			}
			var a = number % 10,
				b = number % 100 - a,
				c = number >= 100 ? 100 : null;
			return number + (suffixes[a] || suffixes[b] || suffixes[c]);
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Belarusian [be]
//! author : Dmitry Demidov : https://github.com/demidov91
//! author: Praleska: http://praleska.pro/
//! Author : Menelion Elensúle : https://github.com/Oire

	function plural(word, num) {
		var forms = word.split('_');
		return num % 10 === 1 && num % 100 !== 11 ? forms[0] : (num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20) ? forms[1] : forms[2]);
	}
	function relativeTimeWithPlural(number, withoutSuffix, key) {
		var format = {
			'mm': withoutSuffix ? 'хвіліна_хвіліны_хвілін' : 'хвіліну_хвіліны_хвілін',
			'hh': withoutSuffix ? 'гадзіна_гадзіны_гадзін' : 'гадзіну_гадзіны_гадзін',
			'dd': 'дзень_дні_дзён',
			'MM': 'месяц_месяцы_месяцаў',
			'yy': 'год_гады_гадоў'
		};
		if (key === 'm') {
			return withoutSuffix ? 'хвіліна' : 'хвіліну';
		}
		else if (key === 'h') {
			return withoutSuffix ? 'гадзіна' : 'гадзіну';
		}
		else {
			return number + ' ' + plural(format[key], +number);
		}
	}

	hooks.defineLocale('be', {
		months : {
			format: 'студзеня_лютага_сакавіка_красавіка_траўня_чэрвеня_ліпеня_жніўня_верасня_кастрычніка_лістапада_снежня'.split('_'),
			standalone: 'студзень_люты_сакавік_красавік_травень_чэрвень_ліпень_жнівень_верасень_кастрычнік_лістапад_снежань'.split('_')
		},
		monthsShort : 'студ_лют_сак_крас_трав_чэрв_ліп_жнів_вер_каст_ліст_снеж'.split('_'),
		weekdays : {
			format: 'нядзелю_панядзелак_аўторак_сераду_чацвер_пятніцу_суботу'.split('_'),
			standalone: 'нядзеля_панядзелак_аўторак_серада_чацвер_пятніца_субота'.split('_'),
			isFormat: /\[ ?[Вв] ?(?:мінулую|наступную)? ?\] ?dddd/
		},
		weekdaysShort : 'нд_пн_ат_ср_чц_пт_сб'.split('_'),
		weekdaysMin : 'нд_пн_ат_ср_чц_пт_сб'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'D MMMM YYYY г.',
			LLL : 'D MMMM YYYY г., HH:mm',
			LLLL : 'dddd, D MMMM YYYY г., HH:mm'
		},
		calendar : {
			sameDay: '[Сёння ў] LT',
			nextDay: '[Заўтра ў] LT',
			lastDay: '[Учора ў] LT',
			nextWeek: function () {
				return '[У] dddd [ў] LT';
			},
			lastWeek: function () {
				switch (this.day()) {
					case 0:
					case 3:
					case 5:
					case 6:
						return '[У мінулую] dddd [ў] LT';
					case 1:
					case 2:
					case 4:
						return '[У мінулы] dddd [ў] LT';
				}
			},
			sameElse: 'L'
		},
		relativeTime : {
			future : 'праз %s',
			past : '%s таму',
			s : 'некалькі секунд',
			m : relativeTimeWithPlural,
			mm : relativeTimeWithPlural,
			h : relativeTimeWithPlural,
			hh : relativeTimeWithPlural,
			d : 'дзень',
			dd : relativeTimeWithPlural,
			M : 'месяц',
			MM : relativeTimeWithPlural,
			y : 'год',
			yy : relativeTimeWithPlural
		},
		meridiemParse: /ночы|раніцы|дня|вечара/,
		isPM : function (input) {
			return /^(дня|вечара)$/.test(input);
		},
		meridiem : function (hour, minute, isLower) {
			if (hour < 4) {
				return 'ночы';
			} else if (hour < 12) {
				return 'раніцы';
			} else if (hour < 17) {
				return 'дня';
			} else {
				return 'вечара';
			}
		},
		ordinalParse: /\d{1,2}-(і|ы|га)/,
		ordinal: function (number, period) {
			switch (period) {
				case 'M':
				case 'd':
				case 'DDD':
				case 'w':
				case 'W':
					return (number % 10 === 2 || number % 10 === 3) && (number % 100 !== 12 && number % 100 !== 13) ? number + '-і' : number + '-ы';
				case 'D':
					return number + '-га';
				default:
					return number;
			}
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Bulgarian [bg]
//! author : Krasen Borisov : https://github.com/kraz

	hooks.defineLocale('bg', {
		months : 'януари_февруари_март_април_май_юни_юли_август_септември_октомври_ноември_декември'.split('_'),
		monthsShort : 'янр_фев_мар_апр_май_юни_юли_авг_сеп_окт_ное_дек'.split('_'),
		weekdays : 'неделя_понеделник_вторник_сряда_четвъртък_петък_събота'.split('_'),
		weekdaysShort : 'нед_пон_вто_сря_чет_пет_съб'.split('_'),
		weekdaysMin : 'нд_пн_вт_ср_чт_пт_сб'.split('_'),
		longDateFormat : {
			LT : 'H:mm',
			LTS : 'H:mm:ss',
			L : 'D.MM.YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY H:mm',
			LLLL : 'dddd, D MMMM YYYY H:mm'
		},
		calendar : {
			sameDay : '[Днес в] LT',
			nextDay : '[Утре в] LT',
			nextWeek : 'dddd [в] LT',
			lastDay : '[Вчера в] LT',
			lastWeek : function () {
				switch (this.day()) {
					case 0:
					case 3:
					case 6:
						return '[В изминалата] dddd [в] LT';
					case 1:
					case 2:
					case 4:
					case 5:
						return '[В изминалия] dddd [в] LT';
				}
			},
			sameElse : 'L'
		},
		relativeTime : {
			future : 'след %s',
			past : 'преди %s',
			s : 'няколко секунди',
			m : 'минута',
			mm : '%d минути',
			h : 'час',
			hh : '%d часа',
			d : 'ден',
			dd : '%d дни',
			M : 'месец',
			MM : '%d месеца',
			y : 'година',
			yy : '%d години'
		},
		ordinalParse: /\d{1,2}-(ев|ен|ти|ви|ри|ми)/,
		ordinal : function (number) {
			var lastDigit = number % 10,
				last2Digits = number % 100;
			if (number === 0) {
				return number + '-ев';
			} else if (last2Digits === 0) {
				return number + '-ен';
			} else if (last2Digits > 10 && last2Digits < 20) {
				return number + '-ти';
			} else if (lastDigit === 1) {
				return number + '-ви';
			} else if (lastDigit === 2) {
				return number + '-ри';
			} else if (lastDigit === 7 || lastDigit === 8) {
				return number + '-ми';
			} else {
				return number + '-ти';
			}
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Bengali [bn]
//! author : Kaushik Gandhi : https://github.com/kaushikgandhi

	var symbolMap$3 = {
		'1': '১',
		'2': '২',
		'3': '৩',
		'4': '৪',
		'5': '৫',
		'6': '৬',
		'7': '৭',
		'8': '৮',
		'9': '৯',
		'0': '০'
	};
	var numberMap$2 = {
		'১': '1',
		'২': '2',
		'৩': '3',
		'৪': '4',
		'৫': '5',
		'৬': '6',
		'৭': '7',
		'৮': '8',
		'৯': '9',
		'০': '0'
	};

	hooks.defineLocale('bn', {
		months : 'জানুয়ারী_ফেব্রুয়ারি_মার্চ_এপ্রিল_মে_জুন_জুলাই_আগস্ট_সেপ্টেম্বর_অক্টোবর_নভেম্বর_ডিসেম্বর'.split('_'),
		monthsShort : 'জানু_ফেব_মার্চ_এপ্র_মে_জুন_জুল_আগ_সেপ্ট_অক্টো_নভে_ডিসে'.split('_'),
		weekdays : 'রবিবার_সোমবার_মঙ্গলবার_বুধবার_বৃহস্পতিবার_শুক্রবার_শনিবার'.split('_'),
		weekdaysShort : 'রবি_সোম_মঙ্গল_বুধ_বৃহস্পতি_শুক্র_শনি'.split('_'),
		weekdaysMin : 'রবি_সোম_মঙ্গ_বুধ_বৃহঃ_শুক্র_শনি'.split('_'),
		longDateFormat : {
			LT : 'A h:mm সময়',
			LTS : 'A h:mm:ss সময়',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY, A h:mm সময়',
			LLLL : 'dddd, D MMMM YYYY, A h:mm সময়'
		},
		calendar : {
			sameDay : '[আজ] LT',
			nextDay : '[আগামীকাল] LT',
			nextWeek : 'dddd, LT',
			lastDay : '[গতকাল] LT',
			lastWeek : '[গত] dddd, LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : '%s পরে',
			past : '%s আগে',
			s : 'কয়েক সেকেন্ড',
			m : 'এক মিনিট',
			mm : '%d মিনিট',
			h : 'এক ঘন্টা',
			hh : '%d ঘন্টা',
			d : 'এক দিন',
			dd : '%d দিন',
			M : 'এক মাস',
			MM : '%d মাস',
			y : 'এক বছর',
			yy : '%d বছর'
		},
		preparse: function (string) {
			return string.replace(/[১২৩৪৫৬৭৮৯০]/g, function (match) {
				return numberMap$2[match];
			});
		},
		postformat: function (string) {
			return string.replace(/\d/g, function (match) {
				return symbolMap$3[match];
			});
		},
		meridiemParse: /রাত|সকাল|দুপুর|বিকাল|রাত/,
		meridiemHour : function (hour, meridiem) {
			if (hour === 12) {
				hour = 0;
			}
			if ((meridiem === 'রাত' && hour >= 4) ||
				(meridiem === 'দুপুর' && hour < 5) ||
				meridiem === 'বিকাল') {
				return hour + 12;
			} else {
				return hour;
			}
		},
		meridiem : function (hour, minute, isLower) {
			if (hour < 4) {
				return 'রাত';
			} else if (hour < 10) {
				return 'সকাল';
			} else if (hour < 17) {
				return 'দুপুর';
			} else if (hour < 20) {
				return 'বিকাল';
			} else {
				return 'রাত';
			}
		},
		week : {
			dow : 0, // Sunday is the first day of the week.
			doy : 6  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Tibetan [bo]
//! author : Thupten N. Chakrishar : https://github.com/vajradog

	var symbolMap$4 = {
		'1': '༡',
		'2': '༢',
		'3': '༣',
		'4': '༤',
		'5': '༥',
		'6': '༦',
		'7': '༧',
		'8': '༨',
		'9': '༩',
		'0': '༠'
	};
	var numberMap$3 = {
		'༡': '1',
		'༢': '2',
		'༣': '3',
		'༤': '4',
		'༥': '5',
		'༦': '6',
		'༧': '7',
		'༨': '8',
		'༩': '9',
		'༠': '0'
	};

	hooks.defineLocale('bo', {
		months : 'ཟླ་བ་དང་པོ_ཟླ་བ་གཉིས་པ_ཟླ་བ་གསུམ་པ_ཟླ་བ་བཞི་པ_ཟླ་བ་ལྔ་པ_ཟླ་བ་དྲུག་པ_ཟླ་བ་བདུན་པ_ཟླ་བ་བརྒྱད་པ_ཟླ་བ་དགུ་པ_ཟླ་བ་བཅུ་པ_ཟླ་བ་བཅུ་གཅིག་པ_ཟླ་བ་བཅུ་གཉིས་པ'.split('_'),
		monthsShort : 'ཟླ་བ་དང་པོ_ཟླ་བ་གཉིས་པ_ཟླ་བ་གསུམ་པ_ཟླ་བ་བཞི་པ_ཟླ་བ་ལྔ་པ_ཟླ་བ་དྲུག་པ_ཟླ་བ་བདུན་པ_ཟླ་བ་བརྒྱད་པ_ཟླ་བ་དགུ་པ_ཟླ་བ་བཅུ་པ_ཟླ་བ་བཅུ་གཅིག་པ_ཟླ་བ་བཅུ་གཉིས་པ'.split('_'),
		weekdays : 'གཟའ་ཉི་མ་_གཟའ་ཟླ་བ་_གཟའ་མིག་དམར་_གཟའ་ལྷག་པ་_གཟའ་ཕུར་བུ_གཟའ་པ་སངས་_གཟའ་སྤེན་པ་'.split('_'),
		weekdaysShort : 'ཉི་མ་_ཟླ་བ་_མིག་དམར་_ལྷག་པ་_ཕུར་བུ_པ་སངས་_སྤེན་པ་'.split('_'),
		weekdaysMin : 'ཉི་མ་_ཟླ་བ་_མིག་དམར་_ལྷག་པ་_ཕུར་བུ_པ་སངས་_སྤེན་པ་'.split('_'),
		longDateFormat : {
			LT : 'A h:mm',
			LTS : 'A h:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY, A h:mm',
			LLLL : 'dddd, D MMMM YYYY, A h:mm'
		},
		calendar : {
			sameDay : '[དི་རིང] LT',
			nextDay : '[སང་ཉིན] LT',
			nextWeek : '[བདུན་ཕྲག་རྗེས་མ], LT',
			lastDay : '[ཁ་སང] LT',
			lastWeek : '[བདུན་ཕྲག་མཐའ་མ] dddd, LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : '%s ལ་',
			past : '%s སྔན་ལ',
			s : 'ལམ་སང',
			m : 'སྐར་མ་གཅིག',
			mm : '%d སྐར་མ',
			h : 'ཆུ་ཚོད་གཅིག',
			hh : '%d ཆུ་ཚོད',
			d : 'ཉིན་གཅིག',
			dd : '%d ཉིན་',
			M : 'ཟླ་བ་གཅིག',
			MM : '%d ཟླ་བ',
			y : 'ལོ་གཅིག',
			yy : '%d ལོ'
		},
		preparse: function (string) {
			return string.replace(/[༡༢༣༤༥༦༧༨༩༠]/g, function (match) {
				return numberMap$3[match];
			});
		},
		postformat: function (string) {
			return string.replace(/\d/g, function (match) {
				return symbolMap$4[match];
			});
		},
		meridiemParse: /མཚན་མོ|ཞོགས་ཀས|ཉིན་གུང|དགོང་དག|མཚན་མོ/,
		meridiemHour : function (hour, meridiem) {
			if (hour === 12) {
				hour = 0;
			}
			if ((meridiem === 'མཚན་མོ' && hour >= 4) ||
				(meridiem === 'ཉིན་གུང' && hour < 5) ||
				meridiem === 'དགོང་དག') {
				return hour + 12;
			} else {
				return hour;
			}
		},
		meridiem : function (hour, minute, isLower) {
			if (hour < 4) {
				return 'མཚན་མོ';
			} else if (hour < 10) {
				return 'ཞོགས་ཀས';
			} else if (hour < 17) {
				return 'ཉིན་གུང';
			} else if (hour < 20) {
				return 'དགོང་དག';
			} else {
				return 'མཚན་མོ';
			}
		},
		week : {
			dow : 0, // Sunday is the first day of the week.
			doy : 6  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Breton [br]
//! author : Jean-Baptiste Le Duigou : https://github.com/jbleduigou

	function relativeTimeWithMutation(number, withoutSuffix, key) {
		var format = {
			'mm': 'munutenn',
			'MM': 'miz',
			'dd': 'devezh'
		};
		return number + ' ' + mutation(format[key], number);
	}
	function specialMutationForYears(number) {
		switch (lastNumber(number)) {
			case 1:
			case 3:
			case 4:
			case 5:
			case 9:
				return number + ' bloaz';
			default:
				return number + ' vloaz';
		}
	}
	function lastNumber(number) {
		if (number > 9) {
			return lastNumber(number % 10);
		}
		return number;
	}
	function mutation(text, number) {
		if (number === 2) {
			return softMutation(text);
		}
		return text;
	}
	function softMutation(text) {
		var mutationTable = {
			'm': 'v',
			'b': 'v',
			'd': 'z'
		};
		if (mutationTable[text.charAt(0)] === undefined) {
			return text;
		}
		return mutationTable[text.charAt(0)] + text.substring(1);
	}

	hooks.defineLocale('br', {
		months : 'Genver_C\'hwevrer_Meurzh_Ebrel_Mae_Mezheven_Gouere_Eost_Gwengolo_Here_Du_Kerzu'.split('_'),
		monthsShort : 'Gen_C\'hwe_Meu_Ebr_Mae_Eve_Gou_Eos_Gwe_Her_Du_Ker'.split('_'),
		weekdays : 'Sul_Lun_Meurzh_Merc\'her_Yaou_Gwener_Sadorn'.split('_'),
		weekdaysShort : 'Sul_Lun_Meu_Mer_Yao_Gwe_Sad'.split('_'),
		weekdaysMin : 'Su_Lu_Me_Mer_Ya_Gw_Sa'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'h[e]mm A',
			LTS : 'h[e]mm:ss A',
			L : 'DD/MM/YYYY',
			LL : 'D [a viz] MMMM YYYY',
			LLL : 'D [a viz] MMMM YYYY h[e]mm A',
			LLLL : 'dddd, D [a viz] MMMM YYYY h[e]mm A'
		},
		calendar : {
			sameDay : '[Hiziv da] LT',
			nextDay : '[Warc\'hoazh da] LT',
			nextWeek : 'dddd [da] LT',
			lastDay : '[Dec\'h da] LT',
			lastWeek : 'dddd [paset da] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'a-benn %s',
			past : '%s \'zo',
			s : 'un nebeud segondennoù',
			m : 'ur vunutenn',
			mm : relativeTimeWithMutation,
			h : 'un eur',
			hh : '%d eur',
			d : 'un devezh',
			dd : relativeTimeWithMutation,
			M : 'ur miz',
			MM : relativeTimeWithMutation,
			y : 'ur bloaz',
			yy : specialMutationForYears
		},
		ordinalParse: /\d{1,2}(añ|vet)/,
		ordinal : function (number) {
			var output = (number === 1) ? 'añ' : 'vet';
			return number + output;
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Bosnian [bs]
//! author : Nedim Cholich : https://github.com/frontyard
//! based on (hr) translation by Bojan Marković

	function translate(number, withoutSuffix, key) {
		var result = number + ' ';
		switch (key) {
			case 'm':
				return withoutSuffix ? 'jedna minuta' : 'jedne minute';
			case 'mm':
				if (number === 1) {
					result += 'minuta';
				} else if (number === 2 || number === 3 || number === 4) {
					result += 'minute';
				} else {
					result += 'minuta';
				}
				return result;
			case 'h':
				return withoutSuffix ? 'jedan sat' : 'jednog sata';
			case 'hh':
				if (number === 1) {
					result += 'sat';
				} else if (number === 2 || number === 3 || number === 4) {
					result += 'sata';
				} else {
					result += 'sati';
				}
				return result;
			case 'dd':
				if (number === 1) {
					result += 'dan';
				} else {
					result += 'dana';
				}
				return result;
			case 'MM':
				if (number === 1) {
					result += 'mjesec';
				} else if (number === 2 || number === 3 || number === 4) {
					result += 'mjeseca';
				} else {
					result += 'mjeseci';
				}
				return result;
			case 'yy':
				if (number === 1) {
					result += 'godina';
				} else if (number === 2 || number === 3 || number === 4) {
					result += 'godine';
				} else {
					result += 'godina';
				}
				return result;
		}
	}

	hooks.defineLocale('bs', {
		months : 'januar_februar_mart_april_maj_juni_juli_august_septembar_oktobar_novembar_decembar'.split('_'),
		monthsShort : 'jan._feb._mar._apr._maj._jun._jul._aug._sep._okt._nov._dec.'.split('_'),
		monthsParseExact: true,
		weekdays : 'nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota'.split('_'),
		weekdaysShort : 'ned._pon._uto._sri._čet._pet._sub.'.split('_'),
		weekdaysMin : 'ne_po_ut_sr_če_pe_su'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'H:mm',
			LTS : 'H:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'D. MMMM YYYY',
			LLL : 'D. MMMM YYYY H:mm',
			LLLL : 'dddd, D. MMMM YYYY H:mm'
		},
		calendar : {
			sameDay  : '[danas u] LT',
			nextDay  : '[sutra u] LT',
			nextWeek : function () {
				switch (this.day()) {
					case 0:
						return '[u] [nedjelju] [u] LT';
					case 3:
						return '[u] [srijedu] [u] LT';
					case 6:
						return '[u] [subotu] [u] LT';
					case 1:
					case 2:
					case 4:
					case 5:
						return '[u] dddd [u] LT';
				}
			},
			lastDay  : '[jučer u] LT',
			lastWeek : function () {
				switch (this.day()) {
					case 0:
					case 3:
						return '[prošlu] dddd [u] LT';
					case 6:
						return '[prošle] [subote] [u] LT';
					case 1:
					case 2:
					case 4:
					case 5:
						return '[prošli] dddd [u] LT';
				}
			},
			sameElse : 'L'
		},
		relativeTime : {
			future : 'za %s',
			past   : 'prije %s',
			s      : 'par sekundi',
			m      : translate,
			mm     : translate,
			h      : translate,
			hh     : translate,
			d      : 'dan',
			dd     : translate,
			M      : 'mjesec',
			MM     : translate,
			y      : 'godinu',
			yy     : translate
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Catalan [ca]
//! author : Juan G. Hurtado : https://github.com/juanghurtado

	hooks.defineLocale('ca', {
		months : 'gener_febrer_març_abril_maig_juny_juliol_agost_setembre_octubre_novembre_desembre'.split('_'),
		monthsShort : 'gen._febr._mar._abr._mai._jun._jul._ag._set._oct._nov._des.'.split('_'),
		monthsParseExact : true,
		weekdays : 'diumenge_dilluns_dimarts_dimecres_dijous_divendres_dissabte'.split('_'),
		weekdaysShort : 'dg._dl._dt._dc._dj._dv._ds.'.split('_'),
		weekdaysMin : 'Dg_Dl_Dt_Dc_Dj_Dv_Ds'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'H:mm',
			LTS : 'H:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY H:mm',
			LLLL : 'dddd D MMMM YYYY H:mm'
		},
		calendar : {
			sameDay : function () {
				return '[avui a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT';
			},
			nextDay : function () {
				return '[demà a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT';
			},
			nextWeek : function () {
				return 'dddd [a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT';
			},
			lastDay : function () {
				return '[ahir a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT';
			},
			lastWeek : function () {
				return '[el] dddd [passat a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT';
			},
			sameElse : 'L'
		},
		relativeTime : {
			future : 'd\'aquí %s',
			past : 'fa %s',
			s : 'uns segons',
			m : 'un minut',
			mm : '%d minuts',
			h : 'una hora',
			hh : '%d hores',
			d : 'un dia',
			dd : '%d dies',
			M : 'un mes',
			MM : '%d mesos',
			y : 'un any',
			yy : '%d anys'
		},
		ordinalParse: /\d{1,2}(r|n|t|è|a)/,
		ordinal : function (number, period) {
			var output = (number === 1) ? 'r' :
				(number === 2) ? 'n' :
					(number === 3) ? 'r' :
						(number === 4) ? 't' : 'è';
			if (period === 'w' || period === 'W') {
				output = 'a';
			}
			return number + output;
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Czech [cs]
//! author : petrbela : https://github.com/petrbela

	var months$3 = 'leden_únor_březen_duben_květen_červen_červenec_srpen_září_říjen_listopad_prosinec'.split('_');
	var monthsShort = 'led_úno_bře_dub_kvě_čvn_čvc_srp_zář_říj_lis_pro'.split('_');
	function plural$1(n) {
		return (n > 1) && (n < 5) && (~~(n / 10) !== 1);
	}
	function translate$1(number, withoutSuffix, key, isFuture) {
		var result = number + ' ';
		switch (key) {
			case 's':  // a few seconds / in a few seconds / a few seconds ago
				return (withoutSuffix || isFuture) ? 'pár sekund' : 'pár sekundami';
			case 'm':  // a minute / in a minute / a minute ago
				return withoutSuffix ? 'minuta' : (isFuture ? 'minutu' : 'minutou');
			case 'mm': // 9 minutes / in 9 minutes / 9 minutes ago
				if (withoutSuffix || isFuture) {
					return result + (plural$1(number) ? 'minuty' : 'minut');
				} else {
					return result + 'minutami';
				}
				break;
			case 'h':  // an hour / in an hour / an hour ago
				return withoutSuffix ? 'hodina' : (isFuture ? 'hodinu' : 'hodinou');
			case 'hh': // 9 hours / in 9 hours / 9 hours ago
				if (withoutSuffix || isFuture) {
					return result + (plural$1(number) ? 'hodiny' : 'hodin');
				} else {
					return result + 'hodinami';
				}
				break;
			case 'd':  // a day / in a day / a day ago
				return (withoutSuffix || isFuture) ? 'den' : 'dnem';
			case 'dd': // 9 days / in 9 days / 9 days ago
				if (withoutSuffix || isFuture) {
					return result + (plural$1(number) ? 'dny' : 'dní');
				} else {
					return result + 'dny';
				}
				break;
			case 'M':  // a month / in a month / a month ago
				return (withoutSuffix || isFuture) ? 'měsíc' : 'měsícem';
			case 'MM': // 9 months / in 9 months / 9 months ago
				if (withoutSuffix || isFuture) {
					return result + (plural$1(number) ? 'měsíce' : 'měsíců');
				} else {
					return result + 'měsíci';
				}
				break;
			case 'y':  // a year / in a year / a year ago
				return (withoutSuffix || isFuture) ? 'rok' : 'rokem';
			case 'yy': // 9 years / in 9 years / 9 years ago
				if (withoutSuffix || isFuture) {
					return result + (plural$1(number) ? 'roky' : 'let');
				} else {
					return result + 'lety';
				}
				break;
		}
	}

	hooks.defineLocale('cs', {
		months : months$3,
		monthsShort : monthsShort,
		monthsParse : (function (months, monthsShort) {
			var i, _monthsParse = [];
			for (i = 0; i < 12; i++) {
				// use custom parser to solve problem with July (červenec)
				_monthsParse[i] = new RegExp('^' + months[i] + '$|^' + monthsShort[i] + '$', 'i');
			}
			return _monthsParse;
		}(months$3, monthsShort)),
		shortMonthsParse : (function (monthsShort) {
			var i, _shortMonthsParse = [];
			for (i = 0; i < 12; i++) {
				_shortMonthsParse[i] = new RegExp('^' + monthsShort[i] + '$', 'i');
			}
			return _shortMonthsParse;
		}(monthsShort)),
		longMonthsParse : (function (months) {
			var i, _longMonthsParse = [];
			for (i = 0; i < 12; i++) {
				_longMonthsParse[i] = new RegExp('^' + months[i] + '$', 'i');
			}
			return _longMonthsParse;
		}(months$3)),
		weekdays : 'neděle_pondělí_úterý_středa_čtvrtek_pátek_sobota'.split('_'),
		weekdaysShort : 'ne_po_út_st_čt_pá_so'.split('_'),
		weekdaysMin : 'ne_po_út_st_čt_pá_so'.split('_'),
		longDateFormat : {
			LT: 'H:mm',
			LTS : 'H:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'D. MMMM YYYY',
			LLL : 'D. MMMM YYYY H:mm',
			LLLL : 'dddd D. MMMM YYYY H:mm',
			l : 'D. M. YYYY'
		},
		calendar : {
			sameDay: '[dnes v] LT',
			nextDay: '[zítra v] LT',
			nextWeek: function () {
				switch (this.day()) {
					case 0:
						return '[v neděli v] LT';
					case 1:
					case 2:
						return '[v] dddd [v] LT';
					case 3:
						return '[ve středu v] LT';
					case 4:
						return '[ve čtvrtek v] LT';
					case 5:
						return '[v pátek v] LT';
					case 6:
						return '[v sobotu v] LT';
				}
			},
			lastDay: '[včera v] LT',
			lastWeek: function () {
				switch (this.day()) {
					case 0:
						return '[minulou neděli v] LT';
					case 1:
					case 2:
						return '[minulé] dddd [v] LT';
					case 3:
						return '[minulou středu v] LT';
					case 4:
					case 5:
						return '[minulý] dddd [v] LT';
					case 6:
						return '[minulou sobotu v] LT';
				}
			},
			sameElse: 'L'
		},
		relativeTime : {
			future : 'za %s',
			past : 'před %s',
			s : translate$1,
			m : translate$1,
			mm : translate$1,
			h : translate$1,
			hh : translate$1,
			d : translate$1,
			dd : translate$1,
			M : translate$1,
			MM : translate$1,
			y : translate$1,
			yy : translate$1
		},
		ordinalParse : /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Chuvash [cv]
//! author : Anatoly Mironov : https://github.com/mirontoli

	hooks.defineLocale('cv', {
		months : 'кӑрлач_нарӑс_пуш_ака_май_ҫӗртме_утӑ_ҫурла_авӑн_юпа_чӳк_раштав'.split('_'),
		monthsShort : 'кӑр_нар_пуш_ака_май_ҫӗр_утӑ_ҫур_авн_юпа_чӳк_раш'.split('_'),
		weekdays : 'вырсарникун_тунтикун_ытларикун_юнкун_кӗҫнерникун_эрнекун_шӑматкун'.split('_'),
		weekdaysShort : 'выр_тун_ытл_юн_кӗҫ_эрн_шӑм'.split('_'),
		weekdaysMin : 'вр_тн_ыт_юн_кҫ_эр_шм'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD-MM-YYYY',
			LL : 'YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ]',
			LLL : 'YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm',
			LLLL : 'dddd, YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm'
		},
		calendar : {
			sameDay: '[Паян] LT [сехетре]',
			nextDay: '[Ыран] LT [сехетре]',
			lastDay: '[Ӗнер] LT [сехетре]',
			nextWeek: '[Ҫитес] dddd LT [сехетре]',
			lastWeek: '[Иртнӗ] dddd LT [сехетре]',
			sameElse: 'L'
		},
		relativeTime : {
			future : function (output) {
				var affix = /сехет$/i.exec(output) ? 'рен' : /ҫул$/i.exec(output) ? 'тан' : 'ран';
				return output + affix;
			},
			past : '%s каялла',
			s : 'пӗр-ик ҫеккунт',
			m : 'пӗр минут',
			mm : '%d минут',
			h : 'пӗр сехет',
			hh : '%d сехет',
			d : 'пӗр кун',
			dd : '%d кун',
			M : 'пӗр уйӑх',
			MM : '%d уйӑх',
			y : 'пӗр ҫул',
			yy : '%d ҫул'
		},
		ordinalParse: /\d{1,2}-мӗш/,
		ordinal : '%d-мӗш',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Welsh [cy]
//! author : Robert Allen : https://github.com/robgallen
//! author : https://github.com/ryangreaves

	hooks.defineLocale('cy', {
		months: 'Ionawr_Chwefror_Mawrth_Ebrill_Mai_Mehefin_Gorffennaf_Awst_Medi_Hydref_Tachwedd_Rhagfyr'.split('_'),
		monthsShort: 'Ion_Chwe_Maw_Ebr_Mai_Meh_Gor_Aws_Med_Hyd_Tach_Rhag'.split('_'),
		weekdays: 'Dydd Sul_Dydd Llun_Dydd Mawrth_Dydd Mercher_Dydd Iau_Dydd Gwener_Dydd Sadwrn'.split('_'),
		weekdaysShort: 'Sul_Llun_Maw_Mer_Iau_Gwe_Sad'.split('_'),
		weekdaysMin: 'Su_Ll_Ma_Me_Ia_Gw_Sa'.split('_'),
		weekdaysParseExact : true,
		// time formats are the same as en-gb
		longDateFormat: {
			LT: 'HH:mm',
			LTS : 'HH:mm:ss',
			L: 'DD/MM/YYYY',
			LL: 'D MMMM YYYY',
			LLL: 'D MMMM YYYY HH:mm',
			LLLL: 'dddd, D MMMM YYYY HH:mm'
		},
		calendar: {
			sameDay: '[Heddiw am] LT',
			nextDay: '[Yfory am] LT',
			nextWeek: 'dddd [am] LT',
			lastDay: '[Ddoe am] LT',
			lastWeek: 'dddd [diwethaf am] LT',
			sameElse: 'L'
		},
		relativeTime: {
			future: 'mewn %s',
			past: '%s yn ôl',
			s: 'ychydig eiliadau',
			m: 'munud',
			mm: '%d munud',
			h: 'awr',
			hh: '%d awr',
			d: 'diwrnod',
			dd: '%d diwrnod',
			M: 'mis',
			MM: '%d mis',
			y: 'blwyddyn',
			yy: '%d flynedd'
		},
		ordinalParse: /\d{1,2}(fed|ain|af|il|ydd|ed|eg)/,
		// traditional ordinal numbers above 31 are not commonly used in colloquial Welsh
		ordinal: function (number) {
			var b = number,
				output = '',
				lookup = [
					'', 'af', 'il', 'ydd', 'ydd', 'ed', 'ed', 'ed', 'fed', 'fed', 'fed', // 1af to 10fed
					'eg', 'fed', 'eg', 'eg', 'fed', 'eg', 'eg', 'fed', 'eg', 'fed' // 11eg to 20fed
				];
			if (b > 20) {
				if (b === 40 || b === 50 || b === 60 || b === 80 || b === 100) {
					output = 'fed'; // not 30ain, 70ain or 90ain
				} else {
					output = 'ain';
				}
			} else if (b > 0) {
				output = lookup[b];
			}
			return number + output;
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Danish [da]
//! author : Ulrik Nielsen : https://github.com/mrbase

	hooks.defineLocale('da', {
		months : 'januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december'.split('_'),
		monthsShort : 'jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec'.split('_'),
		weekdays : 'søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag'.split('_'),
		weekdaysShort : 'søn_man_tir_ons_tor_fre_lør'.split('_'),
		weekdaysMin : 'sø_ma_ti_on_to_fr_lø'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D. MMMM YYYY',
			LLL : 'D. MMMM YYYY HH:mm',
			LLLL : 'dddd [d.] D. MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay : '[I dag kl.] LT',
			nextDay : '[I morgen kl.] LT',
			nextWeek : 'dddd [kl.] LT',
			lastDay : '[I går kl.] LT',
			lastWeek : '[sidste] dddd [kl] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'om %s',
			past : '%s siden',
			s : 'få sekunder',
			m : 'et minut',
			mm : '%d minutter',
			h : 'en time',
			hh : '%d timer',
			d : 'en dag',
			dd : '%d dage',
			M : 'en måned',
			MM : '%d måneder',
			y : 'et år',
			yy : '%d år'
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : German (Austria) [de-at]
//! author : lluchs : https://github.com/lluchs
//! author: Menelion Elensúle: https://github.com/Oire
//! author : Martin Groller : https://github.com/MadMG
//! author : Mikolaj Dadela : https://github.com/mik01aj

	function processRelativeTime(number, withoutSuffix, key, isFuture) {
		var format = {
			'm': ['eine Minute', 'einer Minute'],
			'h': ['eine Stunde', 'einer Stunde'],
			'd': ['ein Tag', 'einem Tag'],
			'dd': [number + ' Tage', number + ' Tagen'],
			'M': ['ein Monat', 'einem Monat'],
			'MM': [number + ' Monate', number + ' Monaten'],
			'y': ['ein Jahr', 'einem Jahr'],
			'yy': [number + ' Jahre', number + ' Jahren']
		};
		return withoutSuffix ? format[key][0] : format[key][1];
	}

	hooks.defineLocale('de-at', {
		months : 'Jänner_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember'.split('_'),
		monthsShort : 'Jän._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.'.split('_'),
		monthsParseExact : true,
		weekdays : 'Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag'.split('_'),
		weekdaysShort : 'So._Mo._Di._Mi._Do._Fr._Sa.'.split('_'),
		weekdaysMin : 'So_Mo_Di_Mi_Do_Fr_Sa'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT: 'HH:mm',
			LTS: 'HH:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'D. MMMM YYYY',
			LLL : 'D. MMMM YYYY HH:mm',
			LLLL : 'dddd, D. MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay: '[heute um] LT [Uhr]',
			sameElse: 'L',
			nextDay: '[morgen um] LT [Uhr]',
			nextWeek: 'dddd [um] LT [Uhr]',
			lastDay: '[gestern um] LT [Uhr]',
			lastWeek: '[letzten] dddd [um] LT [Uhr]'
		},
		relativeTime : {
			future : 'in %s',
			past : 'vor %s',
			s : 'ein paar Sekunden',
			m : processRelativeTime,
			mm : '%d Minuten',
			h : processRelativeTime,
			hh : '%d Stunden',
			d : processRelativeTime,
			dd : processRelativeTime,
			M : processRelativeTime,
			MM : processRelativeTime,
			y : processRelativeTime,
			yy : processRelativeTime
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : German [de]
//! author : lluchs : https://github.com/lluchs
//! author: Menelion Elensúle: https://github.com/Oire
//! author : Mikolaj Dadela : https://github.com/mik01aj

	function processRelativeTime$1(number, withoutSuffix, key, isFuture) {
		var format = {
			'm': ['eine Minute', 'einer Minute'],
			'h': ['eine Stunde', 'einer Stunde'],
			'd': ['ein Tag', 'einem Tag'],
			'dd': [number + ' Tage', number + ' Tagen'],
			'M': ['ein Monat', 'einem Monat'],
			'MM': [number + ' Monate', number + ' Monaten'],
			'y': ['ein Jahr', 'einem Jahr'],
			'yy': [number + ' Jahre', number + ' Jahren']
		};
		return withoutSuffix ? format[key][0] : format[key][1];
	}

	hooks.defineLocale('de', {
		months : 'Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember'.split('_'),
		monthsShort : 'Jan._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.'.split('_'),
		monthsParseExact : true,
		weekdays : 'Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag'.split('_'),
		weekdaysShort : 'So._Mo._Di._Mi._Do._Fr._Sa.'.split('_'),
		weekdaysMin : 'So_Mo_Di_Mi_Do_Fr_Sa'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT: 'HH:mm',
			LTS: 'HH:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'D. MMMM YYYY',
			LLL : 'D. MMMM YYYY HH:mm',
			LLLL : 'dddd, D. MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay: '[heute um] LT [Uhr]',
			sameElse: 'L',
			nextDay: '[morgen um] LT [Uhr]',
			nextWeek: 'dddd [um] LT [Uhr]',
			lastDay: '[gestern um] LT [Uhr]',
			lastWeek: '[letzten] dddd [um] LT [Uhr]'
		},
		relativeTime : {
			future : 'in %s',
			past : 'vor %s',
			s : 'ein paar Sekunden',
			m : processRelativeTime$1,
			mm : '%d Minuten',
			h : processRelativeTime$1,
			hh : '%d Stunden',
			d : processRelativeTime$1,
			dd : processRelativeTime$1,
			M : processRelativeTime$1,
			MM : processRelativeTime$1,
			y : processRelativeTime$1,
			yy : processRelativeTime$1
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Maldivian [dv]
//! author : Jawish Hameed : https://github.com/jawish

	var months$4 = [
		'ޖެނުއަރީ',
		'ފެބްރުއަރީ',
		'މާރިޗު',
		'އޭޕްރީލު',
		'މޭ',
		'ޖޫން',
		'ޖުލައި',
		'އޯގަސްޓު',
		'ސެޕްޓެމްބަރު',
		'އޮކްޓޯބަރު',
		'ނޮވެމްބަރު',
		'ޑިސެމްބަރު'
	];
	var weekdays = [
		'އާދިއްތަ',
		'ހޯމަ',
		'އަންގާރަ',
		'ބުދަ',
		'ބުރާސްފަތި',
		'ހުކުރު',
		'ހޮނިހިރު'
	];

	hooks.defineLocale('dv', {
		months : months$4,
		monthsShort : months$4,
		weekdays : weekdays,
		weekdaysShort : weekdays,
		weekdaysMin : 'އާދި_ހޯމަ_އަން_ބުދަ_ބުރާ_ހުކު_ހޮނި'.split('_'),
		longDateFormat : {

			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'D/M/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd D MMMM YYYY HH:mm'
		},
		meridiemParse: /މކ|މފ/,
		isPM : function (input) {
			return 'މފ' === input;
		},
		meridiem : function (hour, minute, isLower) {
			if (hour < 12) {
				return 'މކ';
			} else {
				return 'މފ';
			}
		},
		calendar : {
			sameDay : '[މިއަދު] LT',
			nextDay : '[މާދަމާ] LT',
			nextWeek : 'dddd LT',
			lastDay : '[އިއްޔެ] LT',
			lastWeek : '[ފާއިތުވި] dddd LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'ތެރޭގައި %s',
			past : 'ކުރިން %s',
			s : 'ސިކުންތުކޮޅެއް',
			m : 'މިނިޓެއް',
			mm : 'މިނިޓު %d',
			h : 'ގަޑިއިރެއް',
			hh : 'ގަޑިއިރު %d',
			d : 'ދުވަހެއް',
			dd : 'ދުވަސް %d',
			M : 'މަހެއް',
			MM : 'މަސް %d',
			y : 'އަހަރެއް',
			yy : 'އަހަރު %d'
		},
		preparse: function (string) {
			return string.replace(/،/g, ',');
		},
		postformat: function (string) {
			return string.replace(/,/g, '،');
		},
		week : {
			dow : 7,  // Sunday is the first day of the week.
			doy : 12  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Greek [el]
//! author : Aggelos Karalias : https://github.com/mehiel

	hooks.defineLocale('el', {
		monthsNominativeEl : 'Ιανουάριος_Φεβρουάριος_Μάρτιος_Απρίλιος_Μάιος_Ιούνιος_Ιούλιος_Αύγουστος_Σεπτέμβριος_Οκτώβριος_Νοέμβριος_Δεκέμβριος'.split('_'),
		monthsGenitiveEl : 'Ιανουαρίου_Φεβρουαρίου_Μαρτίου_Απριλίου_Μαΐου_Ιουνίου_Ιουλίου_Αυγούστου_Σεπτεμβρίου_Οκτωβρίου_Νοεμβρίου_Δεκεμβρίου'.split('_'),
		months : function (momentToFormat, format) {
			if (/D/.test(format.substring(0, format.indexOf('MMMM')))) { // if there is a day number before 'MMMM'
				return this._monthsGenitiveEl[momentToFormat.month()];
			} else {
				return this._monthsNominativeEl[momentToFormat.month()];
			}
		},
		monthsShort : 'Ιαν_Φεβ_Μαρ_Απρ_Μαϊ_Ιουν_Ιουλ_Αυγ_Σεπ_Οκτ_Νοε_Δεκ'.split('_'),
		weekdays : 'Κυριακή_Δευτέρα_Τρίτη_Τετάρτη_Πέμπτη_Παρασκευή_Σάββατο'.split('_'),
		weekdaysShort : 'Κυρ_Δευ_Τρι_Τετ_Πεμ_Παρ_Σαβ'.split('_'),
		weekdaysMin : 'Κυ_Δε_Τρ_Τε_Πε_Πα_Σα'.split('_'),
		meridiem : function (hours, minutes, isLower) {
			if (hours > 11) {
				return isLower ? 'μμ' : 'ΜΜ';
			} else {
				return isLower ? 'πμ' : 'ΠΜ';
			}
		},
		isPM : function (input) {
			return ((input + '').toLowerCase()[0] === 'μ');
		},
		meridiemParse : /[ΠΜ]\.?Μ?\.?/i,
		longDateFormat : {
			LT : 'h:mm A',
			LTS : 'h:mm:ss A',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY h:mm A',
			LLLL : 'dddd, D MMMM YYYY h:mm A'
		},
		calendarEl : {
			sameDay : '[Σήμερα {}] LT',
			nextDay : '[Αύριο {}] LT',
			nextWeek : 'dddd [{}] LT',
			lastDay : '[Χθες {}] LT',
			lastWeek : function () {
				switch (this.day()) {
					case 6:
						return '[το προηγούμενο] dddd [{}] LT';
					default:
						return '[την προηγούμενη] dddd [{}] LT';
				}
			},
			sameElse : 'L'
		},
		calendar : function (key, mom) {
			var output = this._calendarEl[key],
				hours = mom && mom.hours();
			if (isFunction(output)) {
				output = output.apply(mom);
			}
			return output.replace('{}', (hours % 12 === 1 ? 'στη' : 'στις'));
		},
		relativeTime : {
			future : 'σε %s',
			past : '%s πριν',
			s : 'λίγα δευτερόλεπτα',
			m : 'ένα λεπτό',
			mm : '%d λεπτά',
			h : 'μία ώρα',
			hh : '%d ώρες',
			d : 'μία μέρα',
			dd : '%d μέρες',
			M : 'ένας μήνας',
			MM : '%d μήνες',
			y : 'ένας χρόνος',
			yy : '%d χρόνια'
		},
		ordinalParse: /\d{1,2}η/,
		ordinal: '%dη',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : English (Australia) [en-au]
//! author : Jared Morse : https://github.com/jarcoal

	hooks.defineLocale('en-au', {
		months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'),
		monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'),
		weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'),
		weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),
		weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'),
		longDateFormat : {
			LT : 'h:mm A',
			LTS : 'h:mm:ss A',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY h:mm A',
			LLLL : 'dddd, D MMMM YYYY h:mm A'
		},
		calendar : {
			sameDay : '[Today at] LT',
			nextDay : '[Tomorrow at] LT',
			nextWeek : 'dddd [at] LT',
			lastDay : '[Yesterday at] LT',
			lastWeek : '[Last] dddd [at] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'in %s',
			past : '%s ago',
			s : 'a few seconds',
			m : 'a minute',
			mm : '%d minutes',
			h : 'an hour',
			hh : '%d hours',
			d : 'a day',
			dd : '%d days',
			M : 'a month',
			MM : '%d months',
			y : 'a year',
			yy : '%d years'
		},
		ordinalParse: /\d{1,2}(st|nd|rd|th)/,
		ordinal : function (number) {
			var b = number % 10,
				output = (~~(number % 100 / 10) === 1) ? 'th' :
					(b === 1) ? 'st' :
						(b === 2) ? 'nd' :
							(b === 3) ? 'rd' : 'th';
			return number + output;
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : English (Canada) [en-ca]
//! author : Jonathan Abourbih : https://github.com/jonbca

	hooks.defineLocale('en-ca', {
		months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'),
		monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'),
		weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'),
		weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),
		weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'),
		longDateFormat : {
			LT : 'h:mm A',
			LTS : 'h:mm:ss A',
			L : 'YYYY-MM-DD',
			LL : 'MMMM D, YYYY',
			LLL : 'MMMM D, YYYY h:mm A',
			LLLL : 'dddd, MMMM D, YYYY h:mm A'
		},
		calendar : {
			sameDay : '[Today at] LT',
			nextDay : '[Tomorrow at] LT',
			nextWeek : 'dddd [at] LT',
			lastDay : '[Yesterday at] LT',
			lastWeek : '[Last] dddd [at] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'in %s',
			past : '%s ago',
			s : 'a few seconds',
			m : 'a minute',
			mm : '%d minutes',
			h : 'an hour',
			hh : '%d hours',
			d : 'a day',
			dd : '%d days',
			M : 'a month',
			MM : '%d months',
			y : 'a year',
			yy : '%d years'
		},
		ordinalParse: /\d{1,2}(st|nd|rd|th)/,
		ordinal : function (number) {
			var b = number % 10,
				output = (~~(number % 100 / 10) === 1) ? 'th' :
					(b === 1) ? 'st' :
						(b === 2) ? 'nd' :
							(b === 3) ? 'rd' : 'th';
			return number + output;
		}
	});

//! moment.js locale configuration
//! locale : English (United Kingdom) [en-gb]
//! author : Chris Gedrim : https://github.com/chrisgedrim

	hooks.defineLocale('en-gb', {
		months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'),
		monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'),
		weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'),
		weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),
		weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd, D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay : '[Today at] LT',
			nextDay : '[Tomorrow at] LT',
			nextWeek : 'dddd [at] LT',
			lastDay : '[Yesterday at] LT',
			lastWeek : '[Last] dddd [at] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'in %s',
			past : '%s ago',
			s : 'a few seconds',
			m : 'a minute',
			mm : '%d minutes',
			h : 'an hour',
			hh : '%d hours',
			d : 'a day',
			dd : '%d days',
			M : 'a month',
			MM : '%d months',
			y : 'a year',
			yy : '%d years'
		},
		ordinalParse: /\d{1,2}(st|nd|rd|th)/,
		ordinal : function (number) {
			var b = number % 10,
				output = (~~(number % 100 / 10) === 1) ? 'th' :
					(b === 1) ? 'st' :
						(b === 2) ? 'nd' :
							(b === 3) ? 'rd' : 'th';
			return number + output;
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : English (Ireland) [en-ie]
//! author : Chris Cartlidge : https://github.com/chriscartlidge

	hooks.defineLocale('en-ie', {
		months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'),
		monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'),
		weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'),
		weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),
		weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD-MM-YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay : '[Today at] LT',
			nextDay : '[Tomorrow at] LT',
			nextWeek : 'dddd [at] LT',
			lastDay : '[Yesterday at] LT',
			lastWeek : '[Last] dddd [at] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'in %s',
			past : '%s ago',
			s : 'a few seconds',
			m : 'a minute',
			mm : '%d minutes',
			h : 'an hour',
			hh : '%d hours',
			d : 'a day',
			dd : '%d days',
			M : 'a month',
			MM : '%d months',
			y : 'a year',
			yy : '%d years'
		},
		ordinalParse: /\d{1,2}(st|nd|rd|th)/,
		ordinal : function (number) {
			var b = number % 10,
				output = (~~(number % 100 / 10) === 1) ? 'th' :
					(b === 1) ? 'st' :
						(b === 2) ? 'nd' :
							(b === 3) ? 'rd' : 'th';
			return number + output;
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : English (New Zealand) [en-nz]
//! author : Luke McGregor : https://github.com/lukemcgregor

	hooks.defineLocale('en-nz', {
		months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'),
		monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'),
		weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'),
		weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),
		weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'),
		longDateFormat : {
			LT : 'h:mm A',
			LTS : 'h:mm:ss A',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY h:mm A',
			LLLL : 'dddd, D MMMM YYYY h:mm A'
		},
		calendar : {
			sameDay : '[Today at] LT',
			nextDay : '[Tomorrow at] LT',
			nextWeek : 'dddd [at] LT',
			lastDay : '[Yesterday at] LT',
			lastWeek : '[Last] dddd [at] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'in %s',
			past : '%s ago',
			s : 'a few seconds',
			m : 'a minute',
			mm : '%d minutes',
			h : 'an hour',
			hh : '%d hours',
			d : 'a day',
			dd : '%d days',
			M : 'a month',
			MM : '%d months',
			y : 'a year',
			yy : '%d years'
		},
		ordinalParse: /\d{1,2}(st|nd|rd|th)/,
		ordinal : function (number) {
			var b = number % 10,
				output = (~~(number % 100 / 10) === 1) ? 'th' :
					(b === 1) ? 'st' :
						(b === 2) ? 'nd' :
							(b === 3) ? 'rd' : 'th';
			return number + output;
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Esperanto [eo]
//! author : Colin Dean : https://github.com/colindean
//! komento: Mi estas malcerta se mi korekte traktis akuzativojn en tiu traduko.
//!          Se ne, bonvolu korekti kaj avizi min por ke mi povas lerni!

	hooks.defineLocale('eo', {
		months : 'januaro_februaro_marto_aprilo_majo_junio_julio_aŭgusto_septembro_oktobro_novembro_decembro'.split('_'),
		monthsShort : 'jan_feb_mar_apr_maj_jun_jul_aŭg_sep_okt_nov_dec'.split('_'),
		weekdays : 'Dimanĉo_Lundo_Mardo_Merkredo_Ĵaŭdo_Vendredo_Sabato'.split('_'),
		weekdaysShort : 'Dim_Lun_Mard_Merk_Ĵaŭ_Ven_Sab'.split('_'),
		weekdaysMin : 'Di_Lu_Ma_Me_Ĵa_Ve_Sa'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'YYYY-MM-DD',
			LL : 'D[-an de] MMMM, YYYY',
			LLL : 'D[-an de] MMMM, YYYY HH:mm',
			LLLL : 'dddd, [la] D[-an de] MMMM, YYYY HH:mm'
		},
		meridiemParse: /[ap]\.t\.m/i,
		isPM: function (input) {
			return input.charAt(0).toLowerCase() === 'p';
		},
		meridiem : function (hours, minutes, isLower) {
			if (hours > 11) {
				return isLower ? 'p.t.m.' : 'P.T.M.';
			} else {
				return isLower ? 'a.t.m.' : 'A.T.M.';
			}
		},
		calendar : {
			sameDay : '[Hodiaŭ je] LT',
			nextDay : '[Morgaŭ je] LT',
			nextWeek : 'dddd [je] LT',
			lastDay : '[Hieraŭ je] LT',
			lastWeek : '[pasinta] dddd [je] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'je %s',
			past : 'antaŭ %s',
			s : 'sekundoj',
			m : 'minuto',
			mm : '%d minutoj',
			h : 'horo',
			hh : '%d horoj',
			d : 'tago',//ne 'diurno', ĉar estas uzita por proksimumo
			dd : '%d tagoj',
			M : 'monato',
			MM : '%d monatoj',
			y : 'jaro',
			yy : '%d jaroj'
		},
		ordinalParse: /\d{1,2}a/,
		ordinal : '%da',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Spanish (Dominican Republic) [es-do]

	var monthsShortDot = 'ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.'.split('_');
	var monthsShort$1 = 'ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic'.split('_');

	hooks.defineLocale('es-do', {
		months : 'enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre'.split('_'),
		monthsShort : function (m, format) {
			if (/-MMM-/.test(format)) {
				return monthsShort$1[m.month()];
			} else {
				return monthsShortDot[m.month()];
			}
		},
		monthsParseExact : true,
		weekdays : 'domingo_lunes_martes_miércoles_jueves_viernes_sábado'.split('_'),
		weekdaysShort : 'dom._lun._mar._mié._jue._vie._sáb.'.split('_'),
		weekdaysMin : 'do_lu_ma_mi_ju_vi_sá'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'h:mm A',
			LTS : 'h:mm:ss A',
			L : 'DD/MM/YYYY',
			LL : 'D [de] MMMM [de] YYYY',
			LLL : 'D [de] MMMM [de] YYYY h:mm A',
			LLLL : 'dddd, D [de] MMMM [de] YYYY h:mm A'
		},
		calendar : {
			sameDay : function () {
				return '[hoy a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
			},
			nextDay : function () {
				return '[mañana a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
			},
			nextWeek : function () {
				return 'dddd [a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
			},
			lastDay : function () {
				return '[ayer a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
			},
			lastWeek : function () {
				return '[el] dddd [pasado a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
			},
			sameElse : 'L'
		},
		relativeTime : {
			future : 'en %s',
			past : 'hace %s',
			s : 'unos segundos',
			m : 'un minuto',
			mm : '%d minutos',
			h : 'una hora',
			hh : '%d horas',
			d : 'un día',
			dd : '%d días',
			M : 'un mes',
			MM : '%d meses',
			y : 'un año',
			yy : '%d años'
		},
		ordinalParse : /\d{1,2}º/,
		ordinal : '%dº',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Spanish [es]
//! author : Julio Napurí : https://github.com/julionc

	var monthsShortDot$1 = 'ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.'.split('_');
	var monthsShort$2 = 'ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic'.split('_');

	hooks.defineLocale('es', {
		months : 'enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre'.split('_'),
		monthsShort : function (m, format) {
			if (/-MMM-/.test(format)) {
				return monthsShort$2[m.month()];
			} else {
				return monthsShortDot$1[m.month()];
			}
		},
		monthsParseExact : true,
		weekdays : 'domingo_lunes_martes_miércoles_jueves_viernes_sábado'.split('_'),
		weekdaysShort : 'dom._lun._mar._mié._jue._vie._sáb.'.split('_'),
		weekdaysMin : 'do_lu_ma_mi_ju_vi_sá'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'H:mm',
			LTS : 'H:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D [de] MMMM [de] YYYY',
			LLL : 'D [de] MMMM [de] YYYY H:mm',
			LLLL : 'dddd, D [de] MMMM [de] YYYY H:mm'
		},
		calendar : {
			sameDay : function () {
				return '[hoy a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
			},
			nextDay : function () {
				return '[mañana a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
			},
			nextWeek : function () {
				return 'dddd [a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
			},
			lastDay : function () {
				return '[ayer a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
			},
			lastWeek : function () {
				return '[el] dddd [pasado a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
			},
			sameElse : 'L'
		},
		relativeTime : {
			future : 'en %s',
			past : 'hace %s',
			s : 'unos segundos',
			m : 'un minuto',
			mm : '%d minutos',
			h : 'una hora',
			hh : '%d horas',
			d : 'un día',
			dd : '%d días',
			M : 'un mes',
			MM : '%d meses',
			y : 'un año',
			yy : '%d años'
		},
		ordinalParse : /\d{1,2}º/,
		ordinal : '%dº',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Estonian [et]
//! author : Henry Kehlmann : https://github.com/madhenry
//! improvements : Illimar Tambek : https://github.com/ragulka

	function processRelativeTime$2(number, withoutSuffix, key, isFuture) {
		var format = {
			's' : ['mõne sekundi', 'mõni sekund', 'paar sekundit'],
			'm' : ['ühe minuti', 'üks minut'],
			'mm': [number + ' minuti', number + ' minutit'],
			'h' : ['ühe tunni', 'tund aega', 'üks tund'],
			'hh': [number + ' tunni', number + ' tundi'],
			'd' : ['ühe päeva', 'üks päev'],
			'M' : ['kuu aja', 'kuu aega', 'üks kuu'],
			'MM': [number + ' kuu', number + ' kuud'],
			'y' : ['ühe aasta', 'aasta', 'üks aasta'],
			'yy': [number + ' aasta', number + ' aastat']
		};
		if (withoutSuffix) {
			return format[key][2] ? format[key][2] : format[key][1];
		}
		return isFuture ? format[key][0] : format[key][1];
	}

	hooks.defineLocale('et', {
		months        : 'jaanuar_veebruar_märts_aprill_mai_juuni_juuli_august_september_oktoober_november_detsember'.split('_'),
		monthsShort   : 'jaan_veebr_märts_apr_mai_juuni_juuli_aug_sept_okt_nov_dets'.split('_'),
		weekdays      : 'pühapäev_esmaspäev_teisipäev_kolmapäev_neljapäev_reede_laupäev'.split('_'),
		weekdaysShort : 'P_E_T_K_N_R_L'.split('_'),
		weekdaysMin   : 'P_E_T_K_N_R_L'.split('_'),
		longDateFormat : {
			LT   : 'H:mm',
			LTS : 'H:mm:ss',
			L    : 'DD.MM.YYYY',
			LL   : 'D. MMMM YYYY',
			LLL  : 'D. MMMM YYYY H:mm',
			LLLL : 'dddd, D. MMMM YYYY H:mm'
		},
		calendar : {
			sameDay  : '[Täna,] LT',
			nextDay  : '[Homme,] LT',
			nextWeek : '[Järgmine] dddd LT',
			lastDay  : '[Eile,] LT',
			lastWeek : '[Eelmine] dddd LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : '%s pärast',
			past   : '%s tagasi',
			s      : processRelativeTime$2,
			m      : processRelativeTime$2,
			mm     : processRelativeTime$2,
			h      : processRelativeTime$2,
			hh     : processRelativeTime$2,
			d      : processRelativeTime$2,
			dd     : '%d päeva',
			M      : processRelativeTime$2,
			MM     : processRelativeTime$2,
			y      : processRelativeTime$2,
			yy     : processRelativeTime$2
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Basque [eu]
//! author : Eneko Illarramendi : https://github.com/eillarra

	hooks.defineLocale('eu', {
		months : 'urtarrila_otsaila_martxoa_apirila_maiatza_ekaina_uztaila_abuztua_iraila_urria_azaroa_abendua'.split('_'),
		monthsShort : 'urt._ots._mar._api._mai._eka._uzt._abu._ira._urr._aza._abe.'.split('_'),
		monthsParseExact : true,
		weekdays : 'igandea_astelehena_asteartea_asteazkena_osteguna_ostirala_larunbata'.split('_'),
		weekdaysShort : 'ig._al._ar._az._og._ol._lr.'.split('_'),
		weekdaysMin : 'ig_al_ar_az_og_ol_lr'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'YYYY-MM-DD',
			LL : 'YYYY[ko] MMMM[ren] D[a]',
			LLL : 'YYYY[ko] MMMM[ren] D[a] HH:mm',
			LLLL : 'dddd, YYYY[ko] MMMM[ren] D[a] HH:mm',
			l : 'YYYY-M-D',
			ll : 'YYYY[ko] MMM D[a]',
			lll : 'YYYY[ko] MMM D[a] HH:mm',
			llll : 'ddd, YYYY[ko] MMM D[a] HH:mm'
		},
		calendar : {
			sameDay : '[gaur] LT[etan]',
			nextDay : '[bihar] LT[etan]',
			nextWeek : 'dddd LT[etan]',
			lastDay : '[atzo] LT[etan]',
			lastWeek : '[aurreko] dddd LT[etan]',
			sameElse : 'L'
		},
		relativeTime : {
			future : '%s barru',
			past : 'duela %s',
			s : 'segundo batzuk',
			m : 'minutu bat',
			mm : '%d minutu',
			h : 'ordu bat',
			hh : '%d ordu',
			d : 'egun bat',
			dd : '%d egun',
			M : 'hilabete bat',
			MM : '%d hilabete',
			y : 'urte bat',
			yy : '%d urte'
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Persian [fa]
//! author : Ebrahim Byagowi : https://github.com/ebraminio

	var symbolMap$5 = {
		'1': '۱',
		'2': '۲',
		'3': '۳',
		'4': '۴',
		'5': '۵',
		'6': '۶',
		'7': '۷',
		'8': '۸',
		'9': '۹',
		'0': '۰'
	};
	var numberMap$4 = {
		'۱': '1',
		'۲': '2',
		'۳': '3',
		'۴': '4',
		'۵': '5',
		'۶': '6',
		'۷': '7',
		'۸': '8',
		'۹': '9',
		'۰': '0'
	};

	hooks.defineLocale('fa', {
		months : 'ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر'.split('_'),
		monthsShort : 'ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر'.split('_'),
		weekdays : 'یک\u200cشنبه_دوشنبه_سه\u200cشنبه_چهارشنبه_پنج\u200cشنبه_جمعه_شنبه'.split('_'),
		weekdaysShort : 'یک\u200cشنبه_دوشنبه_سه\u200cشنبه_چهارشنبه_پنج\u200cشنبه_جمعه_شنبه'.split('_'),
		weekdaysMin : 'ی_د_س_چ_پ_ج_ش'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd, D MMMM YYYY HH:mm'
		},
		meridiemParse: /قبل از ظهر|بعد از ظهر/,
		isPM: function (input) {
			return /بعد از ظهر/.test(input);
		},
		meridiem : function (hour, minute, isLower) {
			if (hour < 12) {
				return 'قبل از ظهر';
			} else {
				return 'بعد از ظهر';
			}
		},
		calendar : {
			sameDay : '[امروز ساعت] LT',
			nextDay : '[فردا ساعت] LT',
			nextWeek : 'dddd [ساعت] LT',
			lastDay : '[دیروز ساعت] LT',
			lastWeek : 'dddd [پیش] [ساعت] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'در %s',
			past : '%s پیش',
			s : 'چندین ثانیه',
			m : 'یک دقیقه',
			mm : '%d دقیقه',
			h : 'یک ساعت',
			hh : '%d ساعت',
			d : 'یک روز',
			dd : '%d روز',
			M : 'یک ماه',
			MM : '%d ماه',
			y : 'یک سال',
			yy : '%d سال'
		},
		preparse: function (string) {
			return string.replace(/[۰-۹]/g, function (match) {
				return numberMap$4[match];
			}).replace(/،/g, ',');
		},
		postformat: function (string) {
			return string.replace(/\d/g, function (match) {
				return symbolMap$5[match];
			}).replace(/,/g, '،');
		},
		ordinalParse: /\d{1,2}م/,
		ordinal : '%dم',
		week : {
			dow : 6, // Saturday is the first day of the week.
			doy : 12 // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Finnish [fi]
//! author : Tarmo Aidantausta : https://github.com/bleadof

	var numbersPast = 'nolla yksi kaksi kolme neljä viisi kuusi seitsemän kahdeksan yhdeksän'.split(' ');
	var numbersFuture = [
		'nolla', 'yhden', 'kahden', 'kolmen', 'neljän', 'viiden', 'kuuden',
		numbersPast[7], numbersPast[8], numbersPast[9]
	];
	function translate$2(number, withoutSuffix, key, isFuture) {
		var result = '';
		switch (key) {
			case 's':
				return isFuture ? 'muutaman sekunnin' : 'muutama sekunti';
			case 'm':
				return isFuture ? 'minuutin' : 'minuutti';
			case 'mm':
				result = isFuture ? 'minuutin' : 'minuuttia';
				break;
			case 'h':
				return isFuture ? 'tunnin' : 'tunti';
			case 'hh':
				result = isFuture ? 'tunnin' : 'tuntia';
				break;
			case 'd':
				return isFuture ? 'päivän' : 'päivä';
			case 'dd':
				result = isFuture ? 'päivän' : 'päivää';
				break;
			case 'M':
				return isFuture ? 'kuukauden' : 'kuukausi';
			case 'MM':
				result = isFuture ? 'kuukauden' : 'kuukautta';
				break;
			case 'y':
				return isFuture ? 'vuoden' : 'vuosi';
			case 'yy':
				result = isFuture ? 'vuoden' : 'vuotta';
				break;
		}
		result = verbalNumber(number, isFuture) + ' ' + result;
		return result;
	}
	function verbalNumber(number, isFuture) {
		return number < 10 ? (isFuture ? numbersFuture[number] : numbersPast[number]) : number;
	}

	hooks.defineLocale('fi', {
		months : 'tammikuu_helmikuu_maaliskuu_huhtikuu_toukokuu_kesäkuu_heinäkuu_elokuu_syyskuu_lokakuu_marraskuu_joulukuu'.split('_'),
		monthsShort : 'tammi_helmi_maalis_huhti_touko_kesä_heinä_elo_syys_loka_marras_joulu'.split('_'),
		weekdays : 'sunnuntai_maanantai_tiistai_keskiviikko_torstai_perjantai_lauantai'.split('_'),
		weekdaysShort : 'su_ma_ti_ke_to_pe_la'.split('_'),
		weekdaysMin : 'su_ma_ti_ke_to_pe_la'.split('_'),
		longDateFormat : {
			LT : 'HH.mm',
			LTS : 'HH.mm.ss',
			L : 'DD.MM.YYYY',
			LL : 'Do MMMM[ta] YYYY',
			LLL : 'Do MMMM[ta] YYYY, [klo] HH.mm',
			LLLL : 'dddd, Do MMMM[ta] YYYY, [klo] HH.mm',
			l : 'D.M.YYYY',
			ll : 'Do MMM YYYY',
			lll : 'Do MMM YYYY, [klo] HH.mm',
			llll : 'ddd, Do MMM YYYY, [klo] HH.mm'
		},
		calendar : {
			sameDay : '[tänään] [klo] LT',
			nextDay : '[huomenna] [klo] LT',
			nextWeek : 'dddd [klo] LT',
			lastDay : '[eilen] [klo] LT',
			lastWeek : '[viime] dddd[na] [klo] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : '%s päästä',
			past : '%s sitten',
			s : translate$2,
			m : translate$2,
			mm : translate$2,
			h : translate$2,
			hh : translate$2,
			d : translate$2,
			dd : translate$2,
			M : translate$2,
			MM : translate$2,
			y : translate$2,
			yy : translate$2
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Faroese [fo]
//! author : Ragnar Johannesen : https://github.com/ragnar123

	hooks.defineLocale('fo', {
		months : 'januar_februar_mars_apríl_mai_juni_juli_august_september_oktober_november_desember'.split('_'),
		monthsShort : 'jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des'.split('_'),
		weekdays : 'sunnudagur_mánadagur_týsdagur_mikudagur_hósdagur_fríggjadagur_leygardagur'.split('_'),
		weekdaysShort : 'sun_mán_týs_mik_hós_frí_ley'.split('_'),
		weekdaysMin : 'su_má_tý_mi_hó_fr_le'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd D. MMMM, YYYY HH:mm'
		},
		calendar : {
			sameDay : '[Í dag kl.] LT',
			nextDay : '[Í morgin kl.] LT',
			nextWeek : 'dddd [kl.] LT',
			lastDay : '[Í gjár kl.] LT',
			lastWeek : '[síðstu] dddd [kl] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'um %s',
			past : '%s síðani',
			s : 'fá sekund',
			m : 'ein minutt',
			mm : '%d minuttir',
			h : 'ein tími',
			hh : '%d tímar',
			d : 'ein dagur',
			dd : '%d dagar',
			M : 'ein mánaði',
			MM : '%d mánaðir',
			y : 'eitt ár',
			yy : '%d ár'
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : French (Canada) [fr-ca]
//! author : Jonathan Abourbih : https://github.com/jonbca

	hooks.defineLocale('fr-ca', {
		months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'),
		monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'),
		monthsParseExact : true,
		weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'),
		weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'),
		weekdaysMin : 'Di_Lu_Ma_Me_Je_Ve_Sa'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'YYYY-MM-DD',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay: '[Aujourd\'hui à] LT',
			nextDay: '[Demain à] LT',
			nextWeek: 'dddd [à] LT',
			lastDay: '[Hier à] LT',
			lastWeek: 'dddd [dernier à] LT',
			sameElse: 'L'
		},
		relativeTime : {
			future : 'dans %s',
			past : 'il y a %s',
			s : 'quelques secondes',
			m : 'une minute',
			mm : '%d minutes',
			h : 'une heure',
			hh : '%d heures',
			d : 'un jour',
			dd : '%d jours',
			M : 'un mois',
			MM : '%d mois',
			y : 'un an',
			yy : '%d ans'
		},
		ordinalParse: /\d{1,2}(er|e)/,
		ordinal : function (number) {
			return number + (number === 1 ? 'er' : 'e');
		}
	});

//! moment.js locale configuration
//! locale : French (Switzerland) [fr-ch]
//! author : Gaspard Bucher : https://github.com/gaspard

	hooks.defineLocale('fr-ch', {
		months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'),
		monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'),
		monthsParseExact : true,
		weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'),
		weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'),
		weekdaysMin : 'Di_Lu_Ma_Me_Je_Ve_Sa'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay: '[Aujourd\'hui à] LT',
			nextDay: '[Demain à] LT',
			nextWeek: 'dddd [à] LT',
			lastDay: '[Hier à] LT',
			lastWeek: 'dddd [dernier à] LT',
			sameElse: 'L'
		},
		relativeTime : {
			future : 'dans %s',
			past : 'il y a %s',
			s : 'quelques secondes',
			m : 'une minute',
			mm : '%d minutes',
			h : 'une heure',
			hh : '%d heures',
			d : 'un jour',
			dd : '%d jours',
			M : 'un mois',
			MM : '%d mois',
			y : 'un an',
			yy : '%d ans'
		},
		ordinalParse: /\d{1,2}(er|e)/,
		ordinal : function (number) {
			return number + (number === 1 ? 'er' : 'e');
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : French [fr]
//! author : John Fischer : https://github.com/jfroffice

	hooks.defineLocale('fr', {
		months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'),
		monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'),
		monthsParseExact : true,
		weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'),
		weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'),
		weekdaysMin : 'Di_Lu_Ma_Me_Je_Ve_Sa'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay: '[Aujourd\'hui à] LT',
			nextDay: '[Demain à] LT',
			nextWeek: 'dddd [à] LT',
			lastDay: '[Hier à] LT',
			lastWeek: 'dddd [dernier à] LT',
			sameElse: 'L'
		},
		relativeTime : {
			future : 'dans %s',
			past : 'il y a %s',
			s : 'quelques secondes',
			m : 'une minute',
			mm : '%d minutes',
			h : 'une heure',
			hh : '%d heures',
			d : 'un jour',
			dd : '%d jours',
			M : 'un mois',
			MM : '%d mois',
			y : 'un an',
			yy : '%d ans'
		},
		ordinalParse: /\d{1,2}(er|)/,
		ordinal : function (number) {
			return number + (number === 1 ? 'er' : '');
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Frisian [fy]
//! author : Robin van der Vliet : https://github.com/robin0van0der0v

	var monthsShortWithDots = 'jan._feb._mrt._apr._mai_jun._jul._aug._sep._okt._nov._des.'.split('_');
	var monthsShortWithoutDots = 'jan_feb_mrt_apr_mai_jun_jul_aug_sep_okt_nov_des'.split('_');

	hooks.defineLocale('fy', {
		months : 'jannewaris_febrewaris_maart_april_maaie_juny_july_augustus_septimber_oktober_novimber_desimber'.split('_'),
		monthsShort : function (m, format) {
			if (/-MMM-/.test(format)) {
				return monthsShortWithoutDots[m.month()];
			} else {
				return monthsShortWithDots[m.month()];
			}
		},
		monthsParseExact : true,
		weekdays : 'snein_moandei_tiisdei_woansdei_tongersdei_freed_sneon'.split('_'),
		weekdaysShort : 'si._mo._ti._wo._to._fr._so.'.split('_'),
		weekdaysMin : 'Si_Mo_Ti_Wo_To_Fr_So'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD-MM-YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay: '[hjoed om] LT',
			nextDay: '[moarn om] LT',
			nextWeek: 'dddd [om] LT',
			lastDay: '[juster om] LT',
			lastWeek: '[ôfrûne] dddd [om] LT',
			sameElse: 'L'
		},
		relativeTime : {
			future : 'oer %s',
			past : '%s lyn',
			s : 'in pear sekonden',
			m : 'ien minút',
			mm : '%d minuten',
			h : 'ien oere',
			hh : '%d oeren',
			d : 'ien dei',
			dd : '%d dagen',
			M : 'ien moanne',
			MM : '%d moannen',
			y : 'ien jier',
			yy : '%d jierren'
		},
		ordinalParse: /\d{1,2}(ste|de)/,
		ordinal : function (number) {
			return number + ((number === 1 || number === 8 || number >= 20) ? 'ste' : 'de');
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Scottish Gaelic [gd]
//! author : Jon Ashdown : https://github.com/jonashdown

	var months$5 = [
		'Am Faoilleach', 'An Gearran', 'Am Màrt', 'An Giblean', 'An Cèitean', 'An t-Ògmhios', 'An t-Iuchar', 'An Lùnastal', 'An t-Sultain', 'An Dàmhair', 'An t-Samhain', 'An Dùbhlachd'
	];

	var monthsShort$3 = ['Faoi', 'Gear', 'Màrt', 'Gibl', 'Cèit', 'Ògmh', 'Iuch', 'Lùn', 'Sult', 'Dàmh', 'Samh', 'Dùbh'];

	var weekdays$1 = ['Didòmhnaich', 'Diluain', 'Dimàirt', 'Diciadain', 'Diardaoin', 'Dihaoine', 'Disathairne'];

	var weekdaysShort = ['Did', 'Dil', 'Dim', 'Dic', 'Dia', 'Dih', 'Dis'];

	var weekdaysMin = ['Dò', 'Lu', 'Mà', 'Ci', 'Ar', 'Ha', 'Sa'];

	hooks.defineLocale('gd', {
		months : months$5,
		monthsShort : monthsShort$3,
		monthsParseExact : true,
		weekdays : weekdays$1,
		weekdaysShort : weekdaysShort,
		weekdaysMin : weekdaysMin,
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd, D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay : '[An-diugh aig] LT',
			nextDay : '[A-màireach aig] LT',
			nextWeek : 'dddd [aig] LT',
			lastDay : '[An-dè aig] LT',
			lastWeek : 'dddd [seo chaidh] [aig] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'ann an %s',
			past : 'bho chionn %s',
			s : 'beagan diogan',
			m : 'mionaid',
			mm : '%d mionaidean',
			h : 'uair',
			hh : '%d uairean',
			d : 'latha',
			dd : '%d latha',
			M : 'mìos',
			MM : '%d mìosan',
			y : 'bliadhna',
			yy : '%d bliadhna'
		},
		ordinalParse : /\d{1,2}(d|na|mh)/,
		ordinal : function (number) {
			var output = number === 1 ? 'd' : number % 10 === 2 ? 'na' : 'mh';
			return number + output;
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Galician [gl]
//! author : Juan G. Hurtado : https://github.com/juanghurtado

	hooks.defineLocale('gl', {
		months : 'xaneiro_febreiro_marzo_abril_maio_xuño_xullo_agosto_setembro_outubro_novembro_decembro'.split('_'),
		monthsShort : 'xan._feb._mar._abr._mai._xuñ._xul._ago._set._out._nov._dec.'.split('_'),
		monthsParseExact: true,
		weekdays : 'domingo_luns_martes_mércores_xoves_venres_sábado'.split('_'),
		weekdaysShort : 'dom._lun._mar._mér._xov._ven._sáb.'.split('_'),
		weekdaysMin : 'do_lu_ma_mé_xo_ve_sá'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'H:mm',
			LTS : 'H:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D [de] MMMM [de] YYYY',
			LLL : 'D [de] MMMM [de] YYYY H:mm',
			LLLL : 'dddd, D [de] MMMM [de] YYYY H:mm'
		},
		calendar : {
			sameDay : function () {
				return '[hoxe ' + ((this.hours() !== 1) ? 'ás' : 'á') + '] LT';
			},
			nextDay : function () {
				return '[mañá ' + ((this.hours() !== 1) ? 'ás' : 'á') + '] LT';
			},
			nextWeek : function () {
				return 'dddd [' + ((this.hours() !== 1) ? 'ás' : 'a') + '] LT';
			},
			lastDay : function () {
				return '[onte ' + ((this.hours() !== 1) ? 'á' : 'a') + '] LT';
			},
			lastWeek : function () {
				return '[o] dddd [pasado ' + ((this.hours() !== 1) ? 'ás' : 'a') + '] LT';
			},
			sameElse : 'L'
		},
		relativeTime : {
			future : function (str) {
				if (str.indexOf('un') === 0) {
					return 'n' + str;
				}
				return 'en ' + str;
			},
			past : 'hai %s',
			s : 'uns segundos',
			m : 'un minuto',
			mm : '%d minutos',
			h : 'unha hora',
			hh : '%d horas',
			d : 'un día',
			dd : '%d días',
			M : 'un mes',
			MM : '%d meses',
			y : 'un ano',
			yy : '%d anos'
		},
		ordinalParse : /\d{1,2}º/,
		ordinal : '%dº',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Hebrew [he]
//! author : Tomer Cohen : https://github.com/tomer
//! author : Moshe Simantov : https://github.com/DevelopmentIL
//! author : Tal Ater : https://github.com/TalAter

	hooks.defineLocale('he', {
		months : 'ינואר_פברואר_מרץ_אפריל_מאי_יוני_יולי_אוגוסט_ספטמבר_אוקטובר_נובמבר_דצמבר'.split('_'),
		monthsShort : 'ינו׳_פבר׳_מרץ_אפר׳_מאי_יוני_יולי_אוג׳_ספט׳_אוק׳_נוב׳_דצמ׳'.split('_'),
		weekdays : 'ראשון_שני_שלישי_רביעי_חמישי_שישי_שבת'.split('_'),
		weekdaysShort : 'א׳_ב׳_ג׳_ד׳_ה׳_ו׳_ש׳'.split('_'),
		weekdaysMin : 'א_ב_ג_ד_ה_ו_ש'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D [ב]MMMM YYYY',
			LLL : 'D [ב]MMMM YYYY HH:mm',
			LLLL : 'dddd, D [ב]MMMM YYYY HH:mm',
			l : 'D/M/YYYY',
			ll : 'D MMM YYYY',
			lll : 'D MMM YYYY HH:mm',
			llll : 'ddd, D MMM YYYY HH:mm'
		},
		calendar : {
			sameDay : '[היום ב־]LT',
			nextDay : '[מחר ב־]LT',
			nextWeek : 'dddd [בשעה] LT',
			lastDay : '[אתמול ב־]LT',
			lastWeek : '[ביום] dddd [האחרון בשעה] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'בעוד %s',
			past : 'לפני %s',
			s : 'מספר שניות',
			m : 'דקה',
			mm : '%d דקות',
			h : 'שעה',
			hh : function (number) {
				if (number === 2) {
					return 'שעתיים';
				}
				return number + ' שעות';
			},
			d : 'יום',
			dd : function (number) {
				if (number === 2) {
					return 'יומיים';
				}
				return number + ' ימים';
			},
			M : 'חודש',
			MM : function (number) {
				if (number === 2) {
					return 'חודשיים';
				}
				return number + ' חודשים';
			},
			y : 'שנה',
			yy : function (number) {
				if (number === 2) {
					return 'שנתיים';
				} else if (number % 10 === 0 && number !== 10) {
					return number + ' שנה';
				}
				return number + ' שנים';
			}
		},
		meridiemParse: /אחה"צ|לפנה"צ|אחרי הצהריים|לפני הצהריים|לפנות בוקר|בבוקר|בערב/i,
		isPM : function (input) {
			return /^(אחה"צ|אחרי הצהריים|בערב)$/.test(input);
		},
		meridiem : function (hour, minute, isLower) {
			if (hour < 5) {
				return 'לפנות בוקר';
			} else if (hour < 10) {
				return 'בבוקר';
			} else if (hour < 12) {
				return isLower ? 'לפנה"צ' : 'לפני הצהריים';
			} else if (hour < 18) {
				return isLower ? 'אחה"צ' : 'אחרי הצהריים';
			} else {
				return 'בערב';
			}
		}
	});

//! moment.js locale configuration
//! locale : Hindi [hi]
//! author : Mayank Singhal : https://github.com/mayanksinghal

	var symbolMap$6 = {
		'1': '१',
		'2': '२',
		'3': '३',
		'4': '४',
		'5': '५',
		'6': '६',
		'7': '७',
		'8': '८',
		'9': '९',
		'0': '०'
	};
	var numberMap$5 = {
		'१': '1',
		'२': '2',
		'३': '3',
		'४': '4',
		'५': '5',
		'६': '6',
		'७': '7',
		'८': '8',
		'९': '9',
		'०': '0'
	};

	hooks.defineLocale('hi', {
		months : 'जनवरी_फ़रवरी_मार्च_अप्रैल_मई_जून_जुलाई_अगस्त_सितम्बर_अक्टूबर_नवम्बर_दिसम्बर'.split('_'),
		monthsShort : 'जन._फ़र._मार्च_अप्रै._मई_जून_जुल._अग._सित._अक्टू._नव._दिस.'.split('_'),
		monthsParseExact: true,
		weekdays : 'रविवार_सोमवार_मंगलवार_बुधवार_गुरूवार_शुक्रवार_शनिवार'.split('_'),
		weekdaysShort : 'रवि_सोम_मंगल_बुध_गुरू_शुक्र_शनि'.split('_'),
		weekdaysMin : 'र_सो_मं_बु_गु_शु_श'.split('_'),
		longDateFormat : {
			LT : 'A h:mm बजे',
			LTS : 'A h:mm:ss बजे',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY, A h:mm बजे',
			LLLL : 'dddd, D MMMM YYYY, A h:mm बजे'
		},
		calendar : {
			sameDay : '[आज] LT',
			nextDay : '[कल] LT',
			nextWeek : 'dddd, LT',
			lastDay : '[कल] LT',
			lastWeek : '[पिछले] dddd, LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : '%s में',
			past : '%s पहले',
			s : 'कुछ ही क्षण',
			m : 'एक मिनट',
			mm : '%d मिनट',
			h : 'एक घंटा',
			hh : '%d घंटे',
			d : 'एक दिन',
			dd : '%d दिन',
			M : 'एक महीने',
			MM : '%d महीने',
			y : 'एक वर्ष',
			yy : '%d वर्ष'
		},
		preparse: function (string) {
			return string.replace(/[१२३४५६७८९०]/g, function (match) {
				return numberMap$5[match];
			});
		},
		postformat: function (string) {
			return string.replace(/\d/g, function (match) {
				return symbolMap$6[match];
			});
		},
		// Hindi notation for meridiems are quite fuzzy in practice. While there exists
		// a rigid notion of a 'Pahar' it is not used as rigidly in modern Hindi.
		meridiemParse: /रात|सुबह|दोपहर|शाम/,
		meridiemHour : function (hour, meridiem) {
			if (hour === 12) {
				hour = 0;
			}
			if (meridiem === 'रात') {
				return hour < 4 ? hour : hour + 12;
			} else if (meridiem === 'सुबह') {
				return hour;
			} else if (meridiem === 'दोपहर') {
				return hour >= 10 ? hour : hour + 12;
			} else if (meridiem === 'शाम') {
				return hour + 12;
			}
		},
		meridiem : function (hour, minute, isLower) {
			if (hour < 4) {
				return 'रात';
			} else if (hour < 10) {
				return 'सुबह';
			} else if (hour < 17) {
				return 'दोपहर';
			} else if (hour < 20) {
				return 'शाम';
			} else {
				return 'रात';
			}
		},
		week : {
			dow : 0, // Sunday is the first day of the week.
			doy : 6  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Croatian [hr]
//! author : Bojan Marković : https://github.com/bmarkovic

	function translate$3(number, withoutSuffix, key) {
		var result = number + ' ';
		switch (key) {
			case 'm':
				return withoutSuffix ? 'jedna minuta' : 'jedne minute';
			case 'mm':
				if (number === 1) {
					result += 'minuta';
				} else if (number === 2 || number === 3 || number === 4) {
					result += 'minute';
				} else {
					result += 'minuta';
				}
				return result;
			case 'h':
				return withoutSuffix ? 'jedan sat' : 'jednog sata';
			case 'hh':
				if (number === 1) {
					result += 'sat';
				} else if (number === 2 || number === 3 || number === 4) {
					result += 'sata';
				} else {
					result += 'sati';
				}
				return result;
			case 'dd':
				if (number === 1) {
					result += 'dan';
				} else {
					result += 'dana';
				}
				return result;
			case 'MM':
				if (number === 1) {
					result += 'mjesec';
				} else if (number === 2 || number === 3 || number === 4) {
					result += 'mjeseca';
				} else {
					result += 'mjeseci';
				}
				return result;
			case 'yy':
				if (number === 1) {
					result += 'godina';
				} else if (number === 2 || number === 3 || number === 4) {
					result += 'godine';
				} else {
					result += 'godina';
				}
				return result;
		}
	}

	hooks.defineLocale('hr', {
		months : {
			format: 'siječnja_veljače_ožujka_travnja_svibnja_lipnja_srpnja_kolovoza_rujna_listopada_studenoga_prosinca'.split('_'),
			standalone: 'siječanj_veljača_ožujak_travanj_svibanj_lipanj_srpanj_kolovoz_rujan_listopad_studeni_prosinac'.split('_')
		},
		monthsShort : 'sij._velj._ožu._tra._svi._lip._srp._kol._ruj._lis._stu._pro.'.split('_'),
		monthsParseExact: true,
		weekdays : 'nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota'.split('_'),
		weekdaysShort : 'ned._pon._uto._sri._čet._pet._sub.'.split('_'),
		weekdaysMin : 'ne_po_ut_sr_če_pe_su'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'H:mm',
			LTS : 'H:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'D. MMMM YYYY',
			LLL : 'D. MMMM YYYY H:mm',
			LLLL : 'dddd, D. MMMM YYYY H:mm'
		},
		calendar : {
			sameDay  : '[danas u] LT',
			nextDay  : '[sutra u] LT',
			nextWeek : function () {
				switch (this.day()) {
					case 0:
						return '[u] [nedjelju] [u] LT';
					case 3:
						return '[u] [srijedu] [u] LT';
					case 6:
						return '[u] [subotu] [u] LT';
					case 1:
					case 2:
					case 4:
					case 5:
						return '[u] dddd [u] LT';
				}
			},
			lastDay  : '[jučer u] LT',
			lastWeek : function () {
				switch (this.day()) {
					case 0:
					case 3:
						return '[prošlu] dddd [u] LT';
					case 6:
						return '[prošle] [subote] [u] LT';
					case 1:
					case 2:
					case 4:
					case 5:
						return '[prošli] dddd [u] LT';
				}
			},
			sameElse : 'L'
		},
		relativeTime : {
			future : 'za %s',
			past   : 'prije %s',
			s      : 'par sekundi',
			m      : translate$3,
			mm     : translate$3,
			h      : translate$3,
			hh     : translate$3,
			d      : 'dan',
			dd     : translate$3,
			M      : 'mjesec',
			MM     : translate$3,
			y      : 'godinu',
			yy     : translate$3
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Hungarian [hu]
//! author : Adam Brunner : https://github.com/adambrunner

	var weekEndings = 'vasárnap hétfőn kedden szerdán csütörtökön pénteken szombaton'.split(' ');
	function translate$4(number, withoutSuffix, key, isFuture) {
		var num = number,
			suffix;
		switch (key) {
			case 's':
				return (isFuture || withoutSuffix) ? 'néhány másodperc' : 'néhány másodperce';
			case 'm':
				return 'egy' + (isFuture || withoutSuffix ? ' perc' : ' perce');
			case 'mm':
				return num + (isFuture || withoutSuffix ? ' perc' : ' perce');
			case 'h':
				return 'egy' + (isFuture || withoutSuffix ? ' óra' : ' órája');
			case 'hh':
				return num + (isFuture || withoutSuffix ? ' óra' : ' órája');
			case 'd':
				return 'egy' + (isFuture || withoutSuffix ? ' nap' : ' napja');
			case 'dd':
				return num + (isFuture || withoutSuffix ? ' nap' : ' napja');
			case 'M':
				return 'egy' + (isFuture || withoutSuffix ? ' hónap' : ' hónapja');
			case 'MM':
				return num + (isFuture || withoutSuffix ? ' hónap' : ' hónapja');
			case 'y':
				return 'egy' + (isFuture || withoutSuffix ? ' év' : ' éve');
			case 'yy':
				return num + (isFuture || withoutSuffix ? ' év' : ' éve');
		}
		return '';
	}
	function week(isFuture) {
		return (isFuture ? '' : '[múlt] ') + '[' + weekEndings[this.day()] + '] LT[-kor]';
	}

	hooks.defineLocale('hu', {
		months : 'január_február_március_április_május_június_július_augusztus_szeptember_október_november_december'.split('_'),
		monthsShort : 'jan_feb_márc_ápr_máj_jún_júl_aug_szept_okt_nov_dec'.split('_'),
		weekdays : 'vasárnap_hétfő_kedd_szerda_csütörtök_péntek_szombat'.split('_'),
		weekdaysShort : 'vas_hét_kedd_sze_csüt_pén_szo'.split('_'),
		weekdaysMin : 'v_h_k_sze_cs_p_szo'.split('_'),
		longDateFormat : {
			LT : 'H:mm',
			LTS : 'H:mm:ss',
			L : 'YYYY.MM.DD.',
			LL : 'YYYY. MMMM D.',
			LLL : 'YYYY. MMMM D. H:mm',
			LLLL : 'YYYY. MMMM D., dddd H:mm'
		},
		meridiemParse: /de|du/i,
		isPM: function (input) {
			return input.charAt(1).toLowerCase() === 'u';
		},
		meridiem : function (hours, minutes, isLower) {
			if (hours < 12) {
				return isLower === true ? 'de' : 'DE';
			} else {
				return isLower === true ? 'du' : 'DU';
			}
		},
		calendar : {
			sameDay : '[ma] LT[-kor]',
			nextDay : '[holnap] LT[-kor]',
			nextWeek : function () {
				return week.call(this, true);
			},
			lastDay : '[tegnap] LT[-kor]',
			lastWeek : function () {
				return week.call(this, false);
			},
			sameElse : 'L'
		},
		relativeTime : {
			future : '%s múlva',
			past : '%s',
			s : translate$4,
			m : translate$4,
			mm : translate$4,
			h : translate$4,
			hh : translate$4,
			d : translate$4,
			dd : translate$4,
			M : translate$4,
			MM : translate$4,
			y : translate$4,
			yy : translate$4
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Armenian [hy-am]
//! author : Armendarabyan : https://github.com/armendarabyan

	hooks.defineLocale('hy-am', {
		months : {
			format: 'հունվարի_փետրվարի_մարտի_ապրիլի_մայիսի_հունիսի_հուլիսի_օգոստոսի_սեպտեմբերի_հոկտեմբերի_նոյեմբերի_դեկտեմբերի'.split('_'),
			standalone: 'հունվար_փետրվար_մարտ_ապրիլ_մայիս_հունիս_հուլիս_օգոստոս_սեպտեմբեր_հոկտեմբեր_նոյեմբեր_դեկտեմբեր'.split('_')
		},
		monthsShort : 'հնվ_փտր_մրտ_ապր_մյս_հնս_հլս_օգս_սպտ_հկտ_նմբ_դկտ'.split('_'),
		weekdays : 'կիրակի_երկուշաբթի_երեքշաբթի_չորեքշաբթի_հինգշաբթի_ուրբաթ_շաբաթ'.split('_'),
		weekdaysShort : 'կրկ_երկ_երք_չրք_հնգ_ուրբ_շբթ'.split('_'),
		weekdaysMin : 'կրկ_երկ_երք_չրք_հնգ_ուրբ_շբթ'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'D MMMM YYYY թ.',
			LLL : 'D MMMM YYYY թ., HH:mm',
			LLLL : 'dddd, D MMMM YYYY թ., HH:mm'
		},
		calendar : {
			sameDay: '[այսօր] LT',
			nextDay: '[վաղը] LT',
			lastDay: '[երեկ] LT',
			nextWeek: function () {
				return 'dddd [օրը ժամը] LT';
			},
			lastWeek: function () {
				return '[անցած] dddd [օրը ժամը] LT';
			},
			sameElse: 'L'
		},
		relativeTime : {
			future : '%s հետո',
			past : '%s առաջ',
			s : 'մի քանի վայրկյան',
			m : 'րոպե',
			mm : '%d րոպե',
			h : 'ժամ',
			hh : '%d ժամ',
			d : 'օր',
			dd : '%d օր',
			M : 'ամիս',
			MM : '%d ամիս',
			y : 'տարի',
			yy : '%d տարի'
		},
		meridiemParse: /գիշերվա|առավոտվա|ցերեկվա|երեկոյան/,
		isPM: function (input) {
			return /^(ցերեկվա|երեկոյան)$/.test(input);
		},
		meridiem : function (hour) {
			if (hour < 4) {
				return 'գիշերվա';
			} else if (hour < 12) {
				return 'առավոտվա';
			} else if (hour < 17) {
				return 'ցերեկվա';
			} else {
				return 'երեկոյան';
			}
		},
		ordinalParse: /\d{1,2}|\d{1,2}-(ին|րդ)/,
		ordinal: function (number, period) {
			switch (period) {
				case 'DDD':
				case 'w':
				case 'W':
				case 'DDDo':
					if (number === 1) {
						return number + '-ին';
					}
					return number + '-րդ';
				default:
					return number;
			}
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Indonesian [id]
//! author : Mohammad Satrio Utomo : https://github.com/tyok
//! reference: http://id.wikisource.org/wiki/Pedoman_Umum_Ejaan_Bahasa_Indonesia_yang_Disempurnakan

	hooks.defineLocale('id', {
		months : 'Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_November_Desember'.split('_'),
		monthsShort : 'Jan_Feb_Mar_Apr_Mei_Jun_Jul_Ags_Sep_Okt_Nov_Des'.split('_'),
		weekdays : 'Minggu_Senin_Selasa_Rabu_Kamis_Jumat_Sabtu'.split('_'),
		weekdaysShort : 'Min_Sen_Sel_Rab_Kam_Jum_Sab'.split('_'),
		weekdaysMin : 'Mg_Sn_Sl_Rb_Km_Jm_Sb'.split('_'),
		longDateFormat : {
			LT : 'HH.mm',
			LTS : 'HH.mm.ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY [pukul] HH.mm',
			LLLL : 'dddd, D MMMM YYYY [pukul] HH.mm'
		},
		meridiemParse: /pagi|siang|sore|malam/,
		meridiemHour : function (hour, meridiem) {
			if (hour === 12) {
				hour = 0;
			}
			if (meridiem === 'pagi') {
				return hour;
			} else if (meridiem === 'siang') {
				return hour >= 11 ? hour : hour + 12;
			} else if (meridiem === 'sore' || meridiem === 'malam') {
				return hour + 12;
			}
		},
		meridiem : function (hours, minutes, isLower) {
			if (hours < 11) {
				return 'pagi';
			} else if (hours < 15) {
				return 'siang';
			} else if (hours < 19) {
				return 'sore';
			} else {
				return 'malam';
			}
		},
		calendar : {
			sameDay : '[Hari ini pukul] LT',
			nextDay : '[Besok pukul] LT',
			nextWeek : 'dddd [pukul] LT',
			lastDay : '[Kemarin pukul] LT',
			lastWeek : 'dddd [lalu pukul] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'dalam %s',
			past : '%s yang lalu',
			s : 'beberapa detik',
			m : 'semenit',
			mm : '%d menit',
			h : 'sejam',
			hh : '%d jam',
			d : 'sehari',
			dd : '%d hari',
			M : 'sebulan',
			MM : '%d bulan',
			y : 'setahun',
			yy : '%d tahun'
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Icelandic [is]
//! author : Hinrik Örn Sigurðsson : https://github.com/hinrik

	function plural$2(n) {
		if (n % 100 === 11) {
			return true;
		} else if (n % 10 === 1) {
			return false;
		}
		return true;
	}
	function translate$5(number, withoutSuffix, key, isFuture) {
		var result = number + ' ';
		switch (key) {
			case 's':
				return withoutSuffix || isFuture ? 'nokkrar sekúndur' : 'nokkrum sekúndum';
			case 'm':
				return withoutSuffix ? 'mínúta' : 'mínútu';
			case 'mm':
				if (plural$2(number)) {
					return result + (withoutSuffix || isFuture ? 'mínútur' : 'mínútum');
				} else if (withoutSuffix) {
					return result + 'mínúta';
				}
				return result + 'mínútu';
			case 'hh':
				if (plural$2(number)) {
					return result + (withoutSuffix || isFuture ? 'klukkustundir' : 'klukkustundum');
				}
				return result + 'klukkustund';
			case 'd':
				if (withoutSuffix) {
					return 'dagur';
				}
				return isFuture ? 'dag' : 'degi';
			case 'dd':
				if (plural$2(number)) {
					if (withoutSuffix) {
						return result + 'dagar';
					}
					return result + (isFuture ? 'daga' : 'dögum');
				} else if (withoutSuffix) {
					return result + 'dagur';
				}
				return result + (isFuture ? 'dag' : 'degi');
			case 'M':
				if (withoutSuffix) {
					return 'mánuður';
				}
				return isFuture ? 'mánuð' : 'mánuði';
			case 'MM':
				if (plural$2(number)) {
					if (withoutSuffix) {
						return result + 'mánuðir';
					}
					return result + (isFuture ? 'mánuði' : 'mánuðum');
				} else if (withoutSuffix) {
					return result + 'mánuður';
				}
				return result + (isFuture ? 'mánuð' : 'mánuði');
			case 'y':
				return withoutSuffix || isFuture ? 'ár' : 'ári';
			case 'yy':
				if (plural$2(number)) {
					return result + (withoutSuffix || isFuture ? 'ár' : 'árum');
				}
				return result + (withoutSuffix || isFuture ? 'ár' : 'ári');
		}
	}

	hooks.defineLocale('is', {
		months : 'janúar_febrúar_mars_apríl_maí_júní_júlí_ágúst_september_október_nóvember_desember'.split('_'),
		monthsShort : 'jan_feb_mar_apr_maí_jún_júl_ágú_sep_okt_nóv_des'.split('_'),
		weekdays : 'sunnudagur_mánudagur_þriðjudagur_miðvikudagur_fimmtudagur_föstudagur_laugardagur'.split('_'),
		weekdaysShort : 'sun_mán_þri_mið_fim_fös_lau'.split('_'),
		weekdaysMin : 'Su_Má_Þr_Mi_Fi_Fö_La'.split('_'),
		longDateFormat : {
			LT : 'H:mm',
			LTS : 'H:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'D. MMMM YYYY',
			LLL : 'D. MMMM YYYY [kl.] H:mm',
			LLLL : 'dddd, D. MMMM YYYY [kl.] H:mm'
		},
		calendar : {
			sameDay : '[í dag kl.] LT',
			nextDay : '[á morgun kl.] LT',
			nextWeek : 'dddd [kl.] LT',
			lastDay : '[í gær kl.] LT',
			lastWeek : '[síðasta] dddd [kl.] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'eftir %s',
			past : 'fyrir %s síðan',
			s : translate$5,
			m : translate$5,
			mm : translate$5,
			h : 'klukkustund',
			hh : translate$5,
			d : translate$5,
			dd : translate$5,
			M : translate$5,
			MM : translate$5,
			y : translate$5,
			yy : translate$5
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Italian [it]
//! author : Lorenzo : https://github.com/aliem
//! author: Mattia Larentis: https://github.com/nostalgiaz

	hooks.defineLocale('it', {
		months : 'gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre'.split('_'),
		monthsShort : 'gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic'.split('_'),
		weekdays : 'Domenica_Lunedì_Martedì_Mercoledì_Giovedì_Venerdì_Sabato'.split('_'),
		weekdaysShort : 'Dom_Lun_Mar_Mer_Gio_Ven_Sab'.split('_'),
		weekdaysMin : 'Do_Lu_Ma_Me_Gi_Ve_Sa'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd, D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay: '[Oggi alle] LT',
			nextDay: '[Domani alle] LT',
			nextWeek: 'dddd [alle] LT',
			lastDay: '[Ieri alle] LT',
			lastWeek: function () {
				switch (this.day()) {
					case 0:
						return '[la scorsa] dddd [alle] LT';
					default:
						return '[lo scorso] dddd [alle] LT';
				}
			},
			sameElse: 'L'
		},
		relativeTime : {
			future : function (s) {
				return ((/^[0-9].+$/).test(s) ? 'tra' : 'in') + ' ' + s;
			},
			past : '%s fa',
			s : 'alcuni secondi',
			m : 'un minuto',
			mm : '%d minuti',
			h : 'un\'ora',
			hh : '%d ore',
			d : 'un giorno',
			dd : '%d giorni',
			M : 'un mese',
			MM : '%d mesi',
			y : 'un anno',
			yy : '%d anni'
		},
		ordinalParse : /\d{1,2}º/,
		ordinal: '%dº',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Japanese [ja]
//! author : LI Long : https://github.com/baryon

	hooks.defineLocale('ja', {
		months : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'),
		monthsShort : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'),
		weekdays : '日曜日_月曜日_火曜日_水曜日_木曜日_金曜日_土曜日'.split('_'),
		weekdaysShort : '日_月_火_水_木_金_土'.split('_'),
		weekdaysMin : '日_月_火_水_木_金_土'.split('_'),
		longDateFormat : {
			LT : 'Ah時m分',
			LTS : 'Ah時m分s秒',
			L : 'YYYY/MM/DD',
			LL : 'YYYY年M月D日',
			LLL : 'YYYY年M月D日Ah時m分',
			LLLL : 'YYYY年M月D日Ah時m分 dddd'
		},
		meridiemParse: /午前|午後/i,
		isPM : function (input) {
			return input === '午後';
		},
		meridiem : function (hour, minute, isLower) {
			if (hour < 12) {
				return '午前';
			} else {
				return '午後';
			}
		},
		calendar : {
			sameDay : '[今日] LT',
			nextDay : '[明日] LT',
			nextWeek : '[来週]dddd LT',
			lastDay : '[昨日] LT',
			lastWeek : '[前週]dddd LT',
			sameElse : 'L'
		},
		ordinalParse : /\d{1,2}日/,
		ordinal : function (number, period) {
			switch (period) {
				case 'd':
				case 'D':
				case 'DDD':
					return number + '日';
				default:
					return number;
			}
		},
		relativeTime : {
			future : '%s後',
			past : '%s前',
			s : '数秒',
			m : '1分',
			mm : '%d分',
			h : '1時間',
			hh : '%d時間',
			d : '1日',
			dd : '%d日',
			M : '1ヶ月',
			MM : '%dヶ月',
			y : '1年',
			yy : '%d年'
		}
	});

//! moment.js locale configuration
//! locale : Javanese [jv]
//! author : Rony Lantip : https://github.com/lantip
//! reference: http://jv.wikipedia.org/wiki/Basa_Jawa

	hooks.defineLocale('jv', {
		months : 'Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_Nopember_Desember'.split('_'),
		monthsShort : 'Jan_Feb_Mar_Apr_Mei_Jun_Jul_Ags_Sep_Okt_Nop_Des'.split('_'),
		weekdays : 'Minggu_Senen_Seloso_Rebu_Kemis_Jemuwah_Septu'.split('_'),
		weekdaysShort : 'Min_Sen_Sel_Reb_Kem_Jem_Sep'.split('_'),
		weekdaysMin : 'Mg_Sn_Sl_Rb_Km_Jm_Sp'.split('_'),
		longDateFormat : {
			LT : 'HH.mm',
			LTS : 'HH.mm.ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY [pukul] HH.mm',
			LLLL : 'dddd, D MMMM YYYY [pukul] HH.mm'
		},
		meridiemParse: /enjing|siyang|sonten|ndalu/,
		meridiemHour : function (hour, meridiem) {
			if (hour === 12) {
				hour = 0;
			}
			if (meridiem === 'enjing') {
				return hour;
			} else if (meridiem === 'siyang') {
				return hour >= 11 ? hour : hour + 12;
			} else if (meridiem === 'sonten' || meridiem === 'ndalu') {
				return hour + 12;
			}
		},
		meridiem : function (hours, minutes, isLower) {
			if (hours < 11) {
				return 'enjing';
			} else if (hours < 15) {
				return 'siyang';
			} else if (hours < 19) {
				return 'sonten';
			} else {
				return 'ndalu';
			}
		},
		calendar : {
			sameDay : '[Dinten puniko pukul] LT',
			nextDay : '[Mbenjang pukul] LT',
			nextWeek : 'dddd [pukul] LT',
			lastDay : '[Kala wingi pukul] LT',
			lastWeek : 'dddd [kepengker pukul] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'wonten ing %s',
			past : '%s ingkang kepengker',
			s : 'sawetawis detik',
			m : 'setunggal menit',
			mm : '%d menit',
			h : 'setunggal jam',
			hh : '%d jam',
			d : 'sedinten',
			dd : '%d dinten',
			M : 'sewulan',
			MM : '%d wulan',
			y : 'setaun',
			yy : '%d taun'
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Georgian [ka]
//! author : Irakli Janiashvili : https://github.com/irakli-janiashvili

	hooks.defineLocale('ka', {
		months : {
			standalone: 'იანვარი_თებერვალი_მარტი_აპრილი_მაისი_ივნისი_ივლისი_აგვისტო_სექტემბერი_ოქტომბერი_ნოემბერი_დეკემბერი'.split('_'),
			format: 'იანვარს_თებერვალს_მარტს_აპრილის_მაისს_ივნისს_ივლისს_აგვისტს_სექტემბერს_ოქტომბერს_ნოემბერს_დეკემბერს'.split('_')
		},
		monthsShort : 'იან_თებ_მარ_აპრ_მაი_ივნ_ივლ_აგვ_სექ_ოქტ_ნოე_დეკ'.split('_'),
		weekdays : {
			standalone: 'კვირა_ორშაბათი_სამშაბათი_ოთხშაბათი_ხუთშაბათი_პარასკევი_შაბათი'.split('_'),
			format: 'კვირას_ორშაბათს_სამშაბათს_ოთხშაბათს_ხუთშაბათს_პარასკევს_შაბათს'.split('_'),
			isFormat: /(წინა|შემდეგ)/
		},
		weekdaysShort : 'კვი_ორშ_სამ_ოთხ_ხუთ_პარ_შაბ'.split('_'),
		weekdaysMin : 'კვ_ორ_სა_ოთ_ხუ_პა_შა'.split('_'),
		longDateFormat : {
			LT : 'h:mm A',
			LTS : 'h:mm:ss A',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY h:mm A',
			LLLL : 'dddd, D MMMM YYYY h:mm A'
		},
		calendar : {
			sameDay : '[დღეს] LT[-ზე]',
			nextDay : '[ხვალ] LT[-ზე]',
			lastDay : '[გუშინ] LT[-ზე]',
			nextWeek : '[შემდეგ] dddd LT[-ზე]',
			lastWeek : '[წინა] dddd LT-ზე',
			sameElse : 'L'
		},
		relativeTime : {
			future : function (s) {
				return (/(წამი|წუთი|საათი|წელი)/).test(s) ?
					s.replace(/ი$/, 'ში') :
					s + 'ში';
			},
			past : function (s) {
				if ((/(წამი|წუთი|საათი|დღე|თვე)/).test(s)) {
					return s.replace(/(ი|ე)$/, 'ის წინ');
				}
				if ((/წელი/).test(s)) {
					return s.replace(/წელი$/, 'წლის წინ');
				}
			},
			s : 'რამდენიმე წამი',
			m : 'წუთი',
			mm : '%d წუთი',
			h : 'საათი',
			hh : '%d საათი',
			d : 'დღე',
			dd : '%d დღე',
			M : 'თვე',
			MM : '%d თვე',
			y : 'წელი',
			yy : '%d წელი'
		},
		ordinalParse: /0|1-ლი|მე-\d{1,2}|\d{1,2}-ე/,
		ordinal : function (number) {
			if (number === 0) {
				return number;
			}
			if (number === 1) {
				return number + '-ლი';
			}
			if ((number < 20) || (number <= 100 && (number % 20 === 0)) || (number % 100 === 0)) {
				return 'მე-' + number;
			}
			return number + '-ე';
		},
		week : {
			dow : 1,
			doy : 7
		}
	});

//! moment.js locale configuration
//! locale : Kazakh [kk]
//! authors : Nurlan Rakhimzhanov : https://github.com/nurlan

	var suffixes$1 = {
		0: '-ші',
		1: '-ші',
		2: '-ші',
		3: '-ші',
		4: '-ші',
		5: '-ші',
		6: '-шы',
		7: '-ші',
		8: '-ші',
		9: '-шы',
		10: '-шы',
		20: '-шы',
		30: '-шы',
		40: '-шы',
		50: '-ші',
		60: '-шы',
		70: '-ші',
		80: '-ші',
		90: '-шы',
		100: '-ші'
	};

	hooks.defineLocale('kk', {
		months : 'қаңтар_ақпан_наурыз_сәуір_мамыр_маусым_шілде_тамыз_қыркүйек_қазан_қараша_желтоқсан'.split('_'),
		monthsShort : 'қаң_ақп_нау_сәу_мам_мау_шіл_там_қыр_қаз_қар_жел'.split('_'),
		weekdays : 'жексенбі_дүйсенбі_сейсенбі_сәрсенбі_бейсенбі_жұма_сенбі'.split('_'),
		weekdaysShort : 'жек_дүй_сей_сәр_бей_жұм_сен'.split('_'),
		weekdaysMin : 'жк_дй_сй_ср_бй_жм_сн'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd, D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay : '[Бүгін сағат] LT',
			nextDay : '[Ертең сағат] LT',
			nextWeek : 'dddd [сағат] LT',
			lastDay : '[Кеше сағат] LT',
			lastWeek : '[Өткен аптаның] dddd [сағат] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : '%s ішінде',
			past : '%s бұрын',
			s : 'бірнеше секунд',
			m : 'бір минут',
			mm : '%d минут',
			h : 'бір сағат',
			hh : '%d сағат',
			d : 'бір күн',
			dd : '%d күн',
			M : 'бір ай',
			MM : '%d ай',
			y : 'бір жыл',
			yy : '%d жыл'
		},
		ordinalParse: /\d{1,2}-(ші|шы)/,
		ordinal : function (number) {
			var a = number % 10,
				b = number >= 100 ? 100 : null;
			return number + (suffixes$1[number] || suffixes$1[a] || suffixes$1[b]);
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Cambodian [km]
//! author : Kruy Vanna : https://github.com/kruyvanna

	hooks.defineLocale('km', {
		months: 'មករា_កុម្ភៈ_មីនា_មេសា_ឧសភា_មិថុនា_កក្កដា_សីហា_កញ្ញា_តុលា_វិច្ឆិកា_ធ្នូ'.split('_'),
		monthsShort: 'មករា_កុម្ភៈ_មីនា_មេសា_ឧសភា_មិថុនា_កក្កដា_សីហា_កញ្ញា_តុលា_វិច្ឆិកា_ធ្នូ'.split('_'),
		weekdays: 'អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍'.split('_'),
		weekdaysShort: 'អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍'.split('_'),
		weekdaysMin: 'អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍'.split('_'),
		longDateFormat: {
			LT: 'HH:mm',
			LTS : 'HH:mm:ss',
			L: 'DD/MM/YYYY',
			LL: 'D MMMM YYYY',
			LLL: 'D MMMM YYYY HH:mm',
			LLLL: 'dddd, D MMMM YYYY HH:mm'
		},
		calendar: {
			sameDay: '[ថ្ងៃនេះ ម៉ោង] LT',
			nextDay: '[ស្អែក ម៉ោង] LT',
			nextWeek: 'dddd [ម៉ោង] LT',
			lastDay: '[ម្សិលមិញ ម៉ោង] LT',
			lastWeek: 'dddd [សប្តាហ៍មុន] [ម៉ោង] LT',
			sameElse: 'L'
		},
		relativeTime: {
			future: '%sទៀត',
			past: '%sមុន',
			s: 'ប៉ុន្មានវិនាទី',
			m: 'មួយនាទី',
			mm: '%d នាទី',
			h: 'មួយម៉ោង',
			hh: '%d ម៉ោង',
			d: 'មួយថ្ងៃ',
			dd: '%d ថ្ងៃ',
			M: 'មួយខែ',
			MM: '%d ខែ',
			y: 'មួយឆ្នាំ',
			yy: '%d ឆ្នាំ'
		},
		week: {
			dow: 1, // Monday is the first day of the week.
			doy: 4 // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Korean [ko]
//! author : Kyungwook, Park : https://github.com/kyungw00k
//! author : Jeeeyul Lee <jeeeyul@gmail.com>

	hooks.defineLocale('ko', {
		months : '1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월'.split('_'),
		monthsShort : '1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월'.split('_'),
		weekdays : '일요일_월요일_화요일_수요일_목요일_금요일_토요일'.split('_'),
		weekdaysShort : '일_월_화_수_목_금_토'.split('_'),
		weekdaysMin : '일_월_화_수_목_금_토'.split('_'),
		longDateFormat : {
			LT : 'A h시 m분',
			LTS : 'A h시 m분 s초',
			L : 'YYYY.MM.DD',
			LL : 'YYYY년 MMMM D일',
			LLL : 'YYYY년 MMMM D일 A h시 m분',
			LLLL : 'YYYY년 MMMM D일 dddd A h시 m분'
		},
		calendar : {
			sameDay : '오늘 LT',
			nextDay : '내일 LT',
			nextWeek : 'dddd LT',
			lastDay : '어제 LT',
			lastWeek : '지난주 dddd LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : '%s 후',
			past : '%s 전',
			s : '몇 초',
			ss : '%d초',
			m : '일분',
			mm : '%d분',
			h : '한 시간',
			hh : '%d시간',
			d : '하루',
			dd : '%d일',
			M : '한 달',
			MM : '%d달',
			y : '일 년',
			yy : '%d년'
		},
		ordinalParse : /\d{1,2}일/,
		ordinal : '%d일',
		meridiemParse : /오전|오후/,
		isPM : function (token) {
			return token === '오후';
		},
		meridiem : function (hour, minute, isUpper) {
			return hour < 12 ? '오전' : '오후';
		}
	});

//! moment.js locale configuration
//! locale : Kyrgyz [ky]
//! author : Chyngyz Arystan uulu : https://github.com/chyngyz


	var suffixes$2 = {
		0: '-чү',
		1: '-чи',
		2: '-чи',
		3: '-чү',
		4: '-чү',
		5: '-чи',
		6: '-чы',
		7: '-чи',
		8: '-чи',
		9: '-чу',
		10: '-чу',
		20: '-чы',
		30: '-чу',
		40: '-чы',
		50: '-чү',
		60: '-чы',
		70: '-чи',
		80: '-чи',
		90: '-чу',
		100: '-чү'
	};

	hooks.defineLocale('ky', {
		months : 'январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь'.split('_'),
		monthsShort : 'янв_фев_март_апр_май_июнь_июль_авг_сен_окт_ноя_дек'.split('_'),
		weekdays : 'Жекшемби_Дүйшөмбү_Шейшемби_Шаршемби_Бейшемби_Жума_Ишемби'.split('_'),
		weekdaysShort : 'Жек_Дүй_Шей_Шар_Бей_Жум_Ише'.split('_'),
		weekdaysMin : 'Жк_Дй_Шй_Шр_Бй_Жм_Иш'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd, D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay : '[Бүгүн саат] LT',
			nextDay : '[Эртең саат] LT',
			nextWeek : 'dddd [саат] LT',
			lastDay : '[Кече саат] LT',
			lastWeek : '[Өткен аптанын] dddd [күнү] [саат] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : '%s ичинде',
			past : '%s мурун',
			s : 'бирнече секунд',
			m : 'бир мүнөт',
			mm : '%d мүнөт',
			h : 'бир саат',
			hh : '%d саат',
			d : 'бир күн',
			dd : '%d күн',
			M : 'бир ай',
			MM : '%d ай',
			y : 'бир жыл',
			yy : '%d жыл'
		},
		ordinalParse: /\d{1,2}-(чи|чы|чү|чу)/,
		ordinal : function (number) {
			var a = number % 10,
				b = number >= 100 ? 100 : null;
			return number + (suffixes$2[number] || suffixes$2[a] || suffixes$2[b]);
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Luxembourgish [lb]
//! author : mweimerskirch : https://github.com/mweimerskirch
//! author : David Raison : https://github.com/kwisatz

	function processRelativeTime$3(number, withoutSuffix, key, isFuture) {
		var format = {
			'm': ['eng Minutt', 'enger Minutt'],
			'h': ['eng Stonn', 'enger Stonn'],
			'd': ['een Dag', 'engem Dag'],
			'M': ['ee Mount', 'engem Mount'],
			'y': ['ee Joer', 'engem Joer']
		};
		return withoutSuffix ? format[key][0] : format[key][1];
	}
	function processFutureTime(string) {
		var number = string.substr(0, string.indexOf(' '));
		if (eifelerRegelAppliesToNumber(number)) {
			return 'a ' + string;
		}
		return 'an ' + string;
	}
	function processPastTime(string) {
		var number = string.substr(0, string.indexOf(' '));
		if (eifelerRegelAppliesToNumber(number)) {
			return 'viru ' + string;
		}
		return 'virun ' + string;
	}
	/**
	 * Returns true if the word before the given number loses the '-n' ending.
	 * e.g. 'an 10 Deeg' but 'a 5 Deeg'
	 *
	 * @param number {integer}
	 * @returns {boolean}
	 */
	function eifelerRegelAppliesToNumber(number) {
		number = parseInt(number, 10);
		if (isNaN(number)) {
			return false;
		}
		if (number < 0) {
			// Negative Number --> always true
			return true;
		} else if (number < 10) {
			// Only 1 digit
			if (4 <= number && number <= 7) {
				return true;
			}
			return false;
		} else if (number < 100) {
			// 2 digits
			var lastDigit = number % 10, firstDigit = number / 10;
			if (lastDigit === 0) {
				return eifelerRegelAppliesToNumber(firstDigit);
			}
			return eifelerRegelAppliesToNumber(lastDigit);
		} else if (number < 10000) {
			// 3 or 4 digits --> recursively check first digit
			while (number >= 10) {
				number = number / 10;
			}
			return eifelerRegelAppliesToNumber(number);
		} else {
			// Anything larger than 4 digits: recursively check first n-3 digits
			number = number / 1000;
			return eifelerRegelAppliesToNumber(number);
		}
	}

	hooks.defineLocale('lb', {
		months: 'Januar_Februar_Mäerz_Abrëll_Mee_Juni_Juli_August_September_Oktober_November_Dezember'.split('_'),
		monthsShort: 'Jan._Febr._Mrz._Abr._Mee_Jun._Jul._Aug._Sept._Okt._Nov._Dez.'.split('_'),
		monthsParseExact : true,
		weekdays: 'Sonndeg_Méindeg_Dënschdeg_Mëttwoch_Donneschdeg_Freideg_Samschdeg'.split('_'),
		weekdaysShort: 'So._Mé._Dë._Më._Do._Fr._Sa.'.split('_'),
		weekdaysMin: 'So_Mé_Dë_Më_Do_Fr_Sa'.split('_'),
		weekdaysParseExact : true,
		longDateFormat: {
			LT: 'H:mm [Auer]',
			LTS: 'H:mm:ss [Auer]',
			L: 'DD.MM.YYYY',
			LL: 'D. MMMM YYYY',
			LLL: 'D. MMMM YYYY H:mm [Auer]',
			LLLL: 'dddd, D. MMMM YYYY H:mm [Auer]'
		},
		calendar: {
			sameDay: '[Haut um] LT',
			sameElse: 'L',
			nextDay: '[Muer um] LT',
			nextWeek: 'dddd [um] LT',
			lastDay: '[Gëschter um] LT',
			lastWeek: function () {
				// Different date string for 'Dënschdeg' (Tuesday) and 'Donneschdeg' (Thursday) due to phonological rule
				switch (this.day()) {
					case 2:
					case 4:
						return '[Leschten] dddd [um] LT';
					default:
						return '[Leschte] dddd [um] LT';
				}
			}
		},
		relativeTime : {
			future : processFutureTime,
			past : processPastTime,
			s : 'e puer Sekonnen',
			m : processRelativeTime$3,
			mm : '%d Minutten',
			h : processRelativeTime$3,
			hh : '%d Stonnen',
			d : processRelativeTime$3,
			dd : '%d Deeg',
			M : processRelativeTime$3,
			MM : '%d Méint',
			y : processRelativeTime$3,
			yy : '%d Joer'
		},
		ordinalParse: /\d{1,2}\./,
		ordinal: '%d.',
		week: {
			dow: 1, // Monday is the first day of the week.
			doy: 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Lao [lo]
//! author : Ryan Hart : https://github.com/ryanhart2

	hooks.defineLocale('lo', {
		months : 'ມັງກອນ_ກຸມພາ_ມີນາ_ເມສາ_ພຶດສະພາ_ມິຖຸນາ_ກໍລະກົດ_ສິງຫາ_ກັນຍາ_ຕຸລາ_ພະຈິກ_ທັນວາ'.split('_'),
		monthsShort : 'ມັງກອນ_ກຸມພາ_ມີນາ_ເມສາ_ພຶດສະພາ_ມິຖຸນາ_ກໍລະກົດ_ສິງຫາ_ກັນຍາ_ຕຸລາ_ພະຈິກ_ທັນວາ'.split('_'),
		weekdays : 'ອາທິດ_ຈັນ_ອັງຄານ_ພຸດ_ພະຫັດ_ສຸກ_ເສົາ'.split('_'),
		weekdaysShort : 'ທິດ_ຈັນ_ອັງຄານ_ພຸດ_ພະຫັດ_ສຸກ_ເສົາ'.split('_'),
		weekdaysMin : 'ທ_ຈ_ອຄ_ພ_ພຫ_ສກ_ສ'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'ວັນdddd D MMMM YYYY HH:mm'
		},
		meridiemParse: /ຕອນເຊົ້າ|ຕອນແລງ/,
		isPM: function (input) {
			return input === 'ຕອນແລງ';
		},
		meridiem : function (hour, minute, isLower) {
			if (hour < 12) {
				return 'ຕອນເຊົ້າ';
			} else {
				return 'ຕອນແລງ';
			}
		},
		calendar : {
			sameDay : '[ມື້ນີ້ເວລາ] LT',
			nextDay : '[ມື້ອື່ນເວລາ] LT',
			nextWeek : '[ວັນ]dddd[ໜ້າເວລາ] LT',
			lastDay : '[ມື້ວານນີ້ເວລາ] LT',
			lastWeek : '[ວັນ]dddd[ແລ້ວນີ້ເວລາ] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'ອີກ %s',
			past : '%sຜ່ານມາ',
			s : 'ບໍ່ເທົ່າໃດວິນາທີ',
			m : '1 ນາທີ',
			mm : '%d ນາທີ',
			h : '1 ຊົ່ວໂມງ',
			hh : '%d ຊົ່ວໂມງ',
			d : '1 ມື້',
			dd : '%d ມື້',
			M : '1 ເດືອນ',
			MM : '%d ເດືອນ',
			y : '1 ປີ',
			yy : '%d ປີ'
		},
		ordinalParse: /(ທີ່)\d{1,2}/,
		ordinal : function (number) {
			return 'ທີ່' + number;
		}
	});

//! moment.js locale configuration
//! locale : Lithuanian [lt]
//! author : Mindaugas Mozūras : https://github.com/mmozuras

	var units = {
		'm' : 'minutė_minutės_minutę',
		'mm': 'minutės_minučių_minutes',
		'h' : 'valanda_valandos_valandą',
		'hh': 'valandos_valandų_valandas',
		'd' : 'diena_dienos_dieną',
		'dd': 'dienos_dienų_dienas',
		'M' : 'mėnuo_mėnesio_mėnesį',
		'MM': 'mėnesiai_mėnesių_mėnesius',
		'y' : 'metai_metų_metus',
		'yy': 'metai_metų_metus'
	};
	function translateSeconds(number, withoutSuffix, key, isFuture) {
		if (withoutSuffix) {
			return 'kelios sekundės';
		} else {
			return isFuture ? 'kelių sekundžių' : 'kelias sekundes';
		}
	}
	function translateSingular(number, withoutSuffix, key, isFuture) {
		return withoutSuffix ? forms(key)[0] : (isFuture ? forms(key)[1] : forms(key)[2]);
	}
	function special(number) {
		return number % 10 === 0 || (number > 10 && number < 20);
	}
	function forms(key) {
		return units[key].split('_');
	}
	function translate$6(number, withoutSuffix, key, isFuture) {
		var result = number + ' ';
		if (number === 1) {
			return result + translateSingular(number, withoutSuffix, key[0], isFuture);
		} else if (withoutSuffix) {
			return result + (special(number) ? forms(key)[1] : forms(key)[0]);
		} else {
			if (isFuture) {
				return result + forms(key)[1];
			} else {
				return result + (special(number) ? forms(key)[1] : forms(key)[2]);
			}
		}
	}
	hooks.defineLocale('lt', {
		months : {
			format: 'sausio_vasario_kovo_balandžio_gegužės_birželio_liepos_rugpjūčio_rugsėjo_spalio_lapkričio_gruodžio'.split('_'),
			standalone: 'sausis_vasaris_kovas_balandis_gegužė_birželis_liepa_rugpjūtis_rugsėjis_spalis_lapkritis_gruodis'.split('_'),
			isFormat: /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?|MMMM?(\[[^\[\]]*\]|\s)+D[oD]?/
		},
		monthsShort : 'sau_vas_kov_bal_geg_bir_lie_rgp_rgs_spa_lap_grd'.split('_'),
		weekdays : {
			format: 'sekmadienį_pirmadienį_antradienį_trečiadienį_ketvirtadienį_penktadienį_šeštadienį'.split('_'),
			standalone: 'sekmadienis_pirmadienis_antradienis_trečiadienis_ketvirtadienis_penktadienis_šeštadienis'.split('_'),
			isFormat: /dddd HH:mm/
		},
		weekdaysShort : 'Sek_Pir_Ant_Tre_Ket_Pen_Šeš'.split('_'),
		weekdaysMin : 'S_P_A_T_K_Pn_Š'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'YYYY-MM-DD',
			LL : 'YYYY [m.] MMMM D [d.]',
			LLL : 'YYYY [m.] MMMM D [d.], HH:mm [val.]',
			LLLL : 'YYYY [m.] MMMM D [d.], dddd, HH:mm [val.]',
			l : 'YYYY-MM-DD',
			ll : 'YYYY [m.] MMMM D [d.]',
			lll : 'YYYY [m.] MMMM D [d.], HH:mm [val.]',
			llll : 'YYYY [m.] MMMM D [d.], ddd, HH:mm [val.]'
		},
		calendar : {
			sameDay : '[Šiandien] LT',
			nextDay : '[Rytoj] LT',
			nextWeek : 'dddd LT',
			lastDay : '[Vakar] LT',
			lastWeek : '[Praėjusį] dddd LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'po %s',
			past : 'prieš %s',
			s : translateSeconds,
			m : translateSingular,
			mm : translate$6,
			h : translateSingular,
			hh : translate$6,
			d : translateSingular,
			dd : translate$6,
			M : translateSingular,
			MM : translate$6,
			y : translateSingular,
			yy : translate$6
		},
		ordinalParse: /\d{1,2}-oji/,
		ordinal : function (number) {
			return number + '-oji';
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Latvian [lv]
//! author : Kristaps Karlsons : https://github.com/skakri
//! author : Jānis Elmeris : https://github.com/JanisE

	var units$1 = {
		'm': 'minūtes_minūtēm_minūte_minūtes'.split('_'),
		'mm': 'minūtes_minūtēm_minūte_minūtes'.split('_'),
		'h': 'stundas_stundām_stunda_stundas'.split('_'),
		'hh': 'stundas_stundām_stunda_stundas'.split('_'),
		'd': 'dienas_dienām_diena_dienas'.split('_'),
		'dd': 'dienas_dienām_diena_dienas'.split('_'),
		'M': 'mēneša_mēnešiem_mēnesis_mēneši'.split('_'),
		'MM': 'mēneša_mēnešiem_mēnesis_mēneši'.split('_'),
		'y': 'gada_gadiem_gads_gadi'.split('_'),
		'yy': 'gada_gadiem_gads_gadi'.split('_')
	};
	/**
	 * @param withoutSuffix boolean true = a length of time; false = before/after a period of time.
	 */
	function format$1(forms, number, withoutSuffix) {
		if (withoutSuffix) {
			// E.g. "21 minūte", "3 minūtes".
			return number % 10 === 1 && number % 100 !== 11 ? forms[2] : forms[3];
		} else {
			// E.g. "21 minūtes" as in "pēc 21 minūtes".
			// E.g. "3 minūtēm" as in "pēc 3 minūtēm".
			return number % 10 === 1 && number % 100 !== 11 ? forms[0] : forms[1];
		}
	}
	function relativeTimeWithPlural$1(number, withoutSuffix, key) {
		return number + ' ' + format$1(units$1[key], number, withoutSuffix);
	}
	function relativeTimeWithSingular(number, withoutSuffix, key) {
		return format$1(units$1[key], number, withoutSuffix);
	}
	function relativeSeconds(number, withoutSuffix) {
		return withoutSuffix ? 'dažas sekundes' : 'dažām sekundēm';
	}

	hooks.defineLocale('lv', {
		months : 'janvāris_februāris_marts_aprīlis_maijs_jūnijs_jūlijs_augusts_septembris_oktobris_novembris_decembris'.split('_'),
		monthsShort : 'jan_feb_mar_apr_mai_jūn_jūl_aug_sep_okt_nov_dec'.split('_'),
		weekdays : 'svētdiena_pirmdiena_otrdiena_trešdiena_ceturtdiena_piektdiena_sestdiena'.split('_'),
		weekdaysShort : 'Sv_P_O_T_C_Pk_S'.split('_'),
		weekdaysMin : 'Sv_P_O_T_C_Pk_S'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD.MM.YYYY.',
			LL : 'YYYY. [gada] D. MMMM',
			LLL : 'YYYY. [gada] D. MMMM, HH:mm',
			LLLL : 'YYYY. [gada] D. MMMM, dddd, HH:mm'
		},
		calendar : {
			sameDay : '[Šodien pulksten] LT',
			nextDay : '[Rīt pulksten] LT',
			nextWeek : 'dddd [pulksten] LT',
			lastDay : '[Vakar pulksten] LT',
			lastWeek : '[Pagājušā] dddd [pulksten] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'pēc %s',
			past : 'pirms %s',
			s : relativeSeconds,
			m : relativeTimeWithSingular,
			mm : relativeTimeWithPlural$1,
			h : relativeTimeWithSingular,
			hh : relativeTimeWithPlural$1,
			d : relativeTimeWithSingular,
			dd : relativeTimeWithPlural$1,
			M : relativeTimeWithSingular,
			MM : relativeTimeWithPlural$1,
			y : relativeTimeWithSingular,
			yy : relativeTimeWithPlural$1
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Montenegrin [me]
//! author : Miodrag Nikač <miodrag@restartit.me> : https://github.com/miodragnikac

	var translator = {
		words: { //Different grammatical cases
			m: ['jedan minut', 'jednog minuta'],
			mm: ['minut', 'minuta', 'minuta'],
			h: ['jedan sat', 'jednog sata'],
			hh: ['sat', 'sata', 'sati'],
			dd: ['dan', 'dana', 'dana'],
			MM: ['mjesec', 'mjeseca', 'mjeseci'],
			yy: ['godina', 'godine', 'godina']
		},
		correctGrammaticalCase: function (number, wordKey) {
			return number === 1 ? wordKey[0] : (number >= 2 && number <= 4 ? wordKey[1] : wordKey[2]);
		},
		translate: function (number, withoutSuffix, key) {
			var wordKey = translator.words[key];
			if (key.length === 1) {
				return withoutSuffix ? wordKey[0] : wordKey[1];
			} else {
				return number + ' ' + translator.correctGrammaticalCase(number, wordKey);
			}
		}
	};

	hooks.defineLocale('me', {
		months: 'januar_februar_mart_april_maj_jun_jul_avgust_septembar_oktobar_novembar_decembar'.split('_'),
		monthsShort: 'jan._feb._mar._apr._maj_jun_jul_avg._sep._okt._nov._dec.'.split('_'),
		monthsParseExact : true,
		weekdays: 'nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota'.split('_'),
		weekdaysShort: 'ned._pon._uto._sri._čet._pet._sub.'.split('_'),
		weekdaysMin: 'ne_po_ut_sr_če_pe_su'.split('_'),
		weekdaysParseExact : true,
		longDateFormat: {
			LT: 'H:mm',
			LTS : 'H:mm:ss',
			L: 'DD.MM.YYYY',
			LL: 'D. MMMM YYYY',
			LLL: 'D. MMMM YYYY H:mm',
			LLLL: 'dddd, D. MMMM YYYY H:mm'
		},
		calendar: {
			sameDay: '[danas u] LT',
			nextDay: '[sjutra u] LT',

			nextWeek: function () {
				switch (this.day()) {
					case 0:
						return '[u] [nedjelju] [u] LT';
					case 3:
						return '[u] [srijedu] [u] LT';
					case 6:
						return '[u] [subotu] [u] LT';
					case 1:
					case 2:
					case 4:
					case 5:
						return '[u] dddd [u] LT';
				}
			},
			lastDay  : '[juče u] LT',
			lastWeek : function () {
				var lastWeekDays = [
					'[prošle] [nedjelje] [u] LT',
					'[prošlog] [ponedjeljka] [u] LT',
					'[prošlog] [utorka] [u] LT',
					'[prošle] [srijede] [u] LT',
					'[prošlog] [četvrtka] [u] LT',
					'[prošlog] [petka] [u] LT',
					'[prošle] [subote] [u] LT'
				];
				return lastWeekDays[this.day()];
			},
			sameElse : 'L'
		},
		relativeTime : {
			future : 'za %s',
			past   : 'prije %s',
			s      : 'nekoliko sekundi',
			m      : translator.translate,
			mm     : translator.translate,
			h      : translator.translate,
			hh     : translator.translate,
			d      : 'dan',
			dd     : translator.translate,
			M      : 'mjesec',
			MM     : translator.translate,
			y      : 'godinu',
			yy     : translator.translate
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Maori [mi]
//! author : John Corrigan <robbiecloset@gmail.com> : https://github.com/johnideal

	hooks.defineLocale('mi', {
		months: 'Kohi-tāte_Hui-tanguru_Poutū-te-rangi_Paenga-whāwhā_Haratua_Pipiri_Hōngoingoi_Here-turi-kōkā_Mahuru_Whiringa-ā-nuku_Whiringa-ā-rangi_Hakihea'.split('_'),
		monthsShort: 'Kohi_Hui_Pou_Pae_Hara_Pipi_Hōngoi_Here_Mahu_Whi-nu_Whi-ra_Haki'.split('_'),
		monthsRegex: /(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i,
		monthsStrictRegex: /(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i,
		monthsShortRegex: /(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i,
		monthsShortStrictRegex: /(?:['a-z\u0101\u014D\u016B]+\-?){1,2}/i,
		weekdays: 'Rātapu_Mane_Tūrei_Wenerei_Tāite_Paraire_Hātarei'.split('_'),
		weekdaysShort: 'Ta_Ma_Tū_We_Tāi_Pa_Hā'.split('_'),
		weekdaysMin: 'Ta_Ma_Tū_We_Tāi_Pa_Hā'.split('_'),
		longDateFormat: {
			LT: 'HH:mm',
			LTS: 'HH:mm:ss',
			L: 'DD/MM/YYYY',
			LL: 'D MMMM YYYY',
			LLL: 'D MMMM YYYY [i] HH:mm',
			LLLL: 'dddd, D MMMM YYYY [i] HH:mm'
		},
		calendar: {
			sameDay: '[i teie mahana, i] LT',
			nextDay: '[apopo i] LT',
			nextWeek: 'dddd [i] LT',
			lastDay: '[inanahi i] LT',
			lastWeek: 'dddd [whakamutunga i] LT',
			sameElse: 'L'
		},
		relativeTime: {
			future: 'i roto i %s',
			past: '%s i mua',
			s: 'te hēkona ruarua',
			m: 'he meneti',
			mm: '%d meneti',
			h: 'te haora',
			hh: '%d haora',
			d: 'he ra',
			dd: '%d ra',
			M: 'he marama',
			MM: '%d marama',
			y: 'he tau',
			yy: '%d tau'
		},
		ordinalParse: /\d{1,2}º/,
		ordinal: '%dº',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Macedonian [mk]
//! author : Borislav Mickov : https://github.com/B0k0

	hooks.defineLocale('mk', {
		months : 'јануари_февруари_март_април_мај_јуни_јули_август_септември_октомври_ноември_декември'.split('_'),
		monthsShort : 'јан_фев_мар_апр_мај_јун_јул_авг_сеп_окт_ное_дек'.split('_'),
		weekdays : 'недела_понеделник_вторник_среда_четврток_петок_сабота'.split('_'),
		weekdaysShort : 'нед_пон_вто_сре_чет_пет_саб'.split('_'),
		weekdaysMin : 'нe_пo_вт_ср_че_пе_сa'.split('_'),
		longDateFormat : {
			LT : 'H:mm',
			LTS : 'H:mm:ss',
			L : 'D.MM.YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY H:mm',
			LLLL : 'dddd, D MMMM YYYY H:mm'
		},
		calendar : {
			sameDay : '[Денес во] LT',
			nextDay : '[Утре во] LT',
			nextWeek : '[Во] dddd [во] LT',
			lastDay : '[Вчера во] LT',
			lastWeek : function () {
				switch (this.day()) {
					case 0:
					case 3:
					case 6:
						return '[Изминатата] dddd [во] LT';
					case 1:
					case 2:
					case 4:
					case 5:
						return '[Изминатиот] dddd [во] LT';
				}
			},
			sameElse : 'L'
		},
		relativeTime : {
			future : 'после %s',
			past : 'пред %s',
			s : 'неколку секунди',
			m : 'минута',
			mm : '%d минути',
			h : 'час',
			hh : '%d часа',
			d : 'ден',
			dd : '%d дена',
			M : 'месец',
			MM : '%d месеци',
			y : 'година',
			yy : '%d години'
		},
		ordinalParse: /\d{1,2}-(ев|ен|ти|ви|ри|ми)/,
		ordinal : function (number) {
			var lastDigit = number % 10,
				last2Digits = number % 100;
			if (number === 0) {
				return number + '-ев';
			} else if (last2Digits === 0) {
				return number + '-ен';
			} else if (last2Digits > 10 && last2Digits < 20) {
				return number + '-ти';
			} else if (lastDigit === 1) {
				return number + '-ви';
			} else if (lastDigit === 2) {
				return number + '-ри';
			} else if (lastDigit === 7 || lastDigit === 8) {
				return number + '-ми';
			} else {
				return number + '-ти';
			}
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Malayalam [ml]
//! author : Floyd Pink : https://github.com/floydpink

	hooks.defineLocale('ml', {
		months : 'ജനുവരി_ഫെബ്രുവരി_മാർച്ച്_ഏപ്രിൽ_മേയ്_ജൂൺ_ജൂലൈ_ഓഗസ്റ്റ്_സെപ്റ്റംബർ_ഒക്ടോബർ_നവംബർ_ഡിസംബർ'.split('_'),
		monthsShort : 'ജനു._ഫെബ്രു._മാർ._ഏപ്രി._മേയ്_ജൂൺ_ജൂലൈ._ഓഗ._സെപ്റ്റ._ഒക്ടോ._നവം._ഡിസം.'.split('_'),
		monthsParseExact : true,
		weekdays : 'ഞായറാഴ്ച_തിങ്കളാഴ്ച_ചൊവ്വാഴ്ച_ബുധനാഴ്ച_വ്യാഴാഴ്ച_വെള്ളിയാഴ്ച_ശനിയാഴ്ച'.split('_'),
		weekdaysShort : 'ഞായർ_തിങ്കൾ_ചൊവ്വ_ബുധൻ_വ്യാഴം_വെള്ളി_ശനി'.split('_'),
		weekdaysMin : 'ഞാ_തി_ചൊ_ബു_വ്യാ_വെ_ശ'.split('_'),
		longDateFormat : {
			LT : 'A h:mm -നു',
			LTS : 'A h:mm:ss -നു',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY, A h:mm -നു',
			LLLL : 'dddd, D MMMM YYYY, A h:mm -നു'
		},
		calendar : {
			sameDay : '[ഇന്ന്] LT',
			nextDay : '[നാളെ] LT',
			nextWeek : 'dddd, LT',
			lastDay : '[ഇന്നലെ] LT',
			lastWeek : '[കഴിഞ്ഞ] dddd, LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : '%s കഴിഞ്ഞ്',
			past : '%s മുൻപ്',
			s : 'അൽപ നിമിഷങ്ങൾ',
			m : 'ഒരു മിനിറ്റ്',
			mm : '%d മിനിറ്റ്',
			h : 'ഒരു മണിക്കൂർ',
			hh : '%d മണിക്കൂർ',
			d : 'ഒരു ദിവസം',
			dd : '%d ദിവസം',
			M : 'ഒരു മാസം',
			MM : '%d മാസം',
			y : 'ഒരു വർഷം',
			yy : '%d വർഷം'
		},
		meridiemParse: /രാത്രി|രാവിലെ|ഉച്ച കഴിഞ്ഞ്|വൈകുന്നേരം|രാത്രി/i,
		meridiemHour : function (hour, meridiem) {
			if (hour === 12) {
				hour = 0;
			}
			if ((meridiem === 'രാത്രി' && hour >= 4) ||
				meridiem === 'ഉച്ച കഴിഞ്ഞ്' ||
				meridiem === 'വൈകുന്നേരം') {
				return hour + 12;
			} else {
				return hour;
			}
		},
		meridiem : function (hour, minute, isLower) {
			if (hour < 4) {
				return 'രാത്രി';
			} else if (hour < 12) {
				return 'രാവിലെ';
			} else if (hour < 17) {
				return 'ഉച്ച കഴിഞ്ഞ്';
			} else if (hour < 20) {
				return 'വൈകുന്നേരം';
			} else {
				return 'രാത്രി';
			}
		}
	});

//! moment.js locale configuration
//! locale : Marathi [mr]
//! author : Harshad Kale : https://github.com/kalehv
//! author : Vivek Athalye : https://github.com/vnathalye

	var symbolMap$7 = {
		'1': '१',
		'2': '२',
		'3': '३',
		'4': '४',
		'5': '५',
		'6': '६',
		'7': '७',
		'8': '८',
		'9': '९',
		'0': '०'
	};
	var numberMap$6 = {
		'१': '1',
		'२': '2',
		'३': '3',
		'४': '4',
		'५': '5',
		'६': '6',
		'७': '7',
		'८': '8',
		'९': '9',
		'०': '0'
	};

	function relativeTimeMr(number, withoutSuffix, string, isFuture)
	{
		var output = '';
		if (withoutSuffix) {
			switch (string) {
				case 's': output = 'काही सेकंद'; break;
				case 'm': output = 'एक मिनिट'; break;
				case 'mm': output = '%d मिनिटे'; break;
				case 'h': output = 'एक तास'; break;
				case 'hh': output = '%d तास'; break;
				case 'd': output = 'एक दिवस'; break;
				case 'dd': output = '%d दिवस'; break;
				case 'M': output = 'एक महिना'; break;
				case 'MM': output = '%d महिने'; break;
				case 'y': output = 'एक वर्ष'; break;
				case 'yy': output = '%d वर्षे'; break;
			}
		}
		else {
			switch (string) {
				case 's': output = 'काही सेकंदां'; break;
				case 'm': output = 'एका मिनिटा'; break;
				case 'mm': output = '%d मिनिटां'; break;
				case 'h': output = 'एका तासा'; break;
				case 'hh': output = '%d तासां'; break;
				case 'd': output = 'एका दिवसा'; break;
				case 'dd': output = '%d दिवसां'; break;
				case 'M': output = 'एका महिन्या'; break;
				case 'MM': output = '%d महिन्यां'; break;
				case 'y': output = 'एका वर्षा'; break;
				case 'yy': output = '%d वर्षां'; break;
			}
		}
		return output.replace(/%d/i, number);
	}

	hooks.defineLocale('mr', {
		months : 'जानेवारी_फेब्रुवारी_मार्च_एप्रिल_मे_जून_जुलै_ऑगस्ट_सप्टेंबर_ऑक्टोबर_नोव्हेंबर_डिसेंबर'.split('_'),
		monthsShort: 'जाने._फेब्रु._मार्च._एप्रि._मे._जून._जुलै._ऑग._सप्टें._ऑक्टो._नोव्हें._डिसें.'.split('_'),
		monthsParseExact : true,
		weekdays : 'रविवार_सोमवार_मंगळवार_बुधवार_गुरूवार_शुक्रवार_शनिवार'.split('_'),
		weekdaysShort : 'रवि_सोम_मंगळ_बुध_गुरू_शुक्र_शनि'.split('_'),
		weekdaysMin : 'र_सो_मं_बु_गु_शु_श'.split('_'),
		longDateFormat : {
			LT : 'A h:mm वाजता',
			LTS : 'A h:mm:ss वाजता',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY, A h:mm वाजता',
			LLLL : 'dddd, D MMMM YYYY, A h:mm वाजता'
		},
		calendar : {
			sameDay : '[आज] LT',
			nextDay : '[उद्या] LT',
			nextWeek : 'dddd, LT',
			lastDay : '[काल] LT',
			lastWeek: '[मागील] dddd, LT',
			sameElse : 'L'
		},
		relativeTime : {
			future: '%sमध्ये',
			past: '%sपूर्वी',
			s: relativeTimeMr,
			m: relativeTimeMr,
			mm: relativeTimeMr,
			h: relativeTimeMr,
			hh: relativeTimeMr,
			d: relativeTimeMr,
			dd: relativeTimeMr,
			M: relativeTimeMr,
			MM: relativeTimeMr,
			y: relativeTimeMr,
			yy: relativeTimeMr
		},
		preparse: function (string) {
			return string.replace(/[१२३४५६७८९०]/g, function (match) {
				return numberMap$6[match];
			});
		},
		postformat: function (string) {
			return string.replace(/\d/g, function (match) {
				return symbolMap$7[match];
			});
		},
		meridiemParse: /रात्री|सकाळी|दुपारी|सायंकाळी/,
		meridiemHour : function (hour, meridiem) {
			if (hour === 12) {
				hour = 0;
			}
			if (meridiem === 'रात्री') {
				return hour < 4 ? hour : hour + 12;
			} else if (meridiem === 'सकाळी') {
				return hour;
			} else if (meridiem === 'दुपारी') {
				return hour >= 10 ? hour : hour + 12;
			} else if (meridiem === 'सायंकाळी') {
				return hour + 12;
			}
		},
		meridiem: function (hour, minute, isLower) {
			if (hour < 4) {
				return 'रात्री';
			} else if (hour < 10) {
				return 'सकाळी';
			} else if (hour < 17) {
				return 'दुपारी';
			} else if (hour < 20) {
				return 'सायंकाळी';
			} else {
				return 'रात्री';
			}
		},
		week : {
			dow : 0, // Sunday is the first day of the week.
			doy : 6  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Malay [ms-my]
//! note : DEPRECATED, the correct one is [ms]
//! author : Weldan Jamili : https://github.com/weldan

	hooks.defineLocale('ms-my', {
		months : 'Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember'.split('_'),
		monthsShort : 'Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis'.split('_'),
		weekdays : 'Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu'.split('_'),
		weekdaysShort : 'Ahd_Isn_Sel_Rab_Kha_Jum_Sab'.split('_'),
		weekdaysMin : 'Ah_Is_Sl_Rb_Km_Jm_Sb'.split('_'),
		longDateFormat : {
			LT : 'HH.mm',
			LTS : 'HH.mm.ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY [pukul] HH.mm',
			LLLL : 'dddd, D MMMM YYYY [pukul] HH.mm'
		},
		meridiemParse: /pagi|tengahari|petang|malam/,
		meridiemHour: function (hour, meridiem) {
			if (hour === 12) {
				hour = 0;
			}
			if (meridiem === 'pagi') {
				return hour;
			} else if (meridiem === 'tengahari') {
				return hour >= 11 ? hour : hour + 12;
			} else if (meridiem === 'petang' || meridiem === 'malam') {
				return hour + 12;
			}
		},
		meridiem : function (hours, minutes, isLower) {
			if (hours < 11) {
				return 'pagi';
			} else if (hours < 15) {
				return 'tengahari';
			} else if (hours < 19) {
				return 'petang';
			} else {
				return 'malam';
			}
		},
		calendar : {
			sameDay : '[Hari ini pukul] LT',
			nextDay : '[Esok pukul] LT',
			nextWeek : 'dddd [pukul] LT',
			lastDay : '[Kelmarin pukul] LT',
			lastWeek : 'dddd [lepas pukul] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'dalam %s',
			past : '%s yang lepas',
			s : 'beberapa saat',
			m : 'seminit',
			mm : '%d minit',
			h : 'sejam',
			hh : '%d jam',
			d : 'sehari',
			dd : '%d hari',
			M : 'sebulan',
			MM : '%d bulan',
			y : 'setahun',
			yy : '%d tahun'
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Malay [ms]
//! author : Weldan Jamili : https://github.com/weldan

	hooks.defineLocale('ms', {
		months : 'Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember'.split('_'),
		monthsShort : 'Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis'.split('_'),
		weekdays : 'Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu'.split('_'),
		weekdaysShort : 'Ahd_Isn_Sel_Rab_Kha_Jum_Sab'.split('_'),
		weekdaysMin : 'Ah_Is_Sl_Rb_Km_Jm_Sb'.split('_'),
		longDateFormat : {
			LT : 'HH.mm',
			LTS : 'HH.mm.ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY [pukul] HH.mm',
			LLLL : 'dddd, D MMMM YYYY [pukul] HH.mm'
		},
		meridiemParse: /pagi|tengahari|petang|malam/,
		meridiemHour: function (hour, meridiem) {
			if (hour === 12) {
				hour = 0;
			}
			if (meridiem === 'pagi') {
				return hour;
			} else if (meridiem === 'tengahari') {
				return hour >= 11 ? hour : hour + 12;
			} else if (meridiem === 'petang' || meridiem === 'malam') {
				return hour + 12;
			}
		},
		meridiem : function (hours, minutes, isLower) {
			if (hours < 11) {
				return 'pagi';
			} else if (hours < 15) {
				return 'tengahari';
			} else if (hours < 19) {
				return 'petang';
			} else {
				return 'malam';
			}
		},
		calendar : {
			sameDay : '[Hari ini pukul] LT',
			nextDay : '[Esok pukul] LT',
			nextWeek : 'dddd [pukul] LT',
			lastDay : '[Kelmarin pukul] LT',
			lastWeek : 'dddd [lepas pukul] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'dalam %s',
			past : '%s yang lepas',
			s : 'beberapa saat',
			m : 'seminit',
			mm : '%d minit',
			h : 'sejam',
			hh : '%d jam',
			d : 'sehari',
			dd : '%d hari',
			M : 'sebulan',
			MM : '%d bulan',
			y : 'setahun',
			yy : '%d tahun'
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Burmese [my]
//! author : Squar team, mysquar.com
//! author : David Rossellat : https://github.com/gholadr
//! author : Tin Aung Lin : https://github.com/thanyawzinmin

	var symbolMap$8 = {
		'1': '၁',
		'2': '၂',
		'3': '၃',
		'4': '၄',
		'5': '၅',
		'6': '၆',
		'7': '၇',
		'8': '၈',
		'9': '၉',
		'0': '၀'
	};
	var numberMap$7 = {
		'၁': '1',
		'၂': '2',
		'၃': '3',
		'၄': '4',
		'၅': '5',
		'၆': '6',
		'၇': '7',
		'၈': '8',
		'၉': '9',
		'၀': '0'
	};

	hooks.defineLocale('my', {
		months: 'ဇန်နဝါရီ_ဖေဖော်ဝါရီ_မတ်_ဧပြီ_မေ_ဇွန်_ဇူလိုင်_သြဂုတ်_စက်တင်ဘာ_အောက်တိုဘာ_နိုဝင်ဘာ_ဒီဇင်ဘာ'.split('_'),
		monthsShort: 'ဇန်_ဖေ_မတ်_ပြီ_မေ_ဇွန်_လိုင်_သြ_စက်_အောက်_နို_ဒီ'.split('_'),
		weekdays: 'တနင်္ဂနွေ_တနင်္လာ_အင်္ဂါ_ဗုဒ္ဓဟူး_ကြာသပတေး_သောကြာ_စနေ'.split('_'),
		weekdaysShort: 'နွေ_လာ_ဂါ_ဟူး_ကြာ_သော_နေ'.split('_'),
		weekdaysMin: 'နွေ_လာ_ဂါ_ဟူး_ကြာ_သော_နေ'.split('_'),

		longDateFormat: {
			LT: 'HH:mm',
			LTS: 'HH:mm:ss',
			L: 'DD/MM/YYYY',
			LL: 'D MMMM YYYY',
			LLL: 'D MMMM YYYY HH:mm',
			LLLL: 'dddd D MMMM YYYY HH:mm'
		},
		calendar: {
			sameDay: '[ယနေ.] LT [မှာ]',
			nextDay: '[မနက်ဖြန်] LT [မှာ]',
			nextWeek: 'dddd LT [မှာ]',
			lastDay: '[မနေ.က] LT [မှာ]',
			lastWeek: '[ပြီးခဲ့သော] dddd LT [မှာ]',
			sameElse: 'L'
		},
		relativeTime: {
			future: 'လာမည့် %s မှာ',
			past: 'လွန်ခဲ့သော %s က',
			s: 'စက္ကန်.အနည်းငယ်',
			m: 'တစ်မိနစ်',
			mm: '%d မိနစ်',
			h: 'တစ်နာရီ',
			hh: '%d နာရီ',
			d: 'တစ်ရက်',
			dd: '%d ရက်',
			M: 'တစ်လ',
			MM: '%d လ',
			y: 'တစ်နှစ်',
			yy: '%d နှစ်'
		},
		preparse: function (string) {
			return string.replace(/[၁၂၃၄၅၆၇၈၉၀]/g, function (match) {
				return numberMap$7[match];
			});
		},
		postformat: function (string) {
			return string.replace(/\d/g, function (match) {
				return symbolMap$8[match];
			});
		},
		week: {
			dow: 1, // Monday is the first day of the week.
			doy: 4 // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Norwegian Bokmål [nb]
//! authors : Espen Hovlandsdal : https://github.com/rexxars
//!           Sigurd Gartmann : https://github.com/sigurdga

	hooks.defineLocale('nb', {
		months : 'januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember'.split('_'),
		monthsShort : 'jan._feb._mars_april_mai_juni_juli_aug._sep._okt._nov._des.'.split('_'),
		monthsParseExact : true,
		weekdays : 'søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag'.split('_'),
		weekdaysShort : 'sø._ma._ti._on._to._fr._lø.'.split('_'),
		weekdaysMin : 'sø_ma_ti_on_to_fr_lø'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'D. MMMM YYYY',
			LLL : 'D. MMMM YYYY [kl.] HH:mm',
			LLLL : 'dddd D. MMMM YYYY [kl.] HH:mm'
		},
		calendar : {
			sameDay: '[i dag kl.] LT',
			nextDay: '[i morgen kl.] LT',
			nextWeek: 'dddd [kl.] LT',
			lastDay: '[i går kl.] LT',
			lastWeek: '[forrige] dddd [kl.] LT',
			sameElse: 'L'
		},
		relativeTime : {
			future : 'om %s',
			past : '%s siden',
			s : 'noen sekunder',
			m : 'ett minutt',
			mm : '%d minutter',
			h : 'en time',
			hh : '%d timer',
			d : 'en dag',
			dd : '%d dager',
			M : 'en måned',
			MM : '%d måneder',
			y : 'ett år',
			yy : '%d år'
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Nepalese [ne]
//! author : suvash : https://github.com/suvash

	var symbolMap$9 = {
		'1': '१',
		'2': '२',
		'3': '३',
		'4': '४',
		'5': '५',
		'6': '६',
		'7': '७',
		'8': '८',
		'9': '९',
		'0': '०'
	};
	var numberMap$8 = {
		'१': '1',
		'२': '2',
		'३': '3',
		'४': '4',
		'५': '5',
		'६': '6',
		'७': '7',
		'८': '8',
		'९': '9',
		'०': '0'
	};

	hooks.defineLocale('ne', {
		months : 'जनवरी_फेब्रुवरी_मार्च_अप्रिल_मई_जुन_जुलाई_अगष्ट_सेप्टेम्बर_अक्टोबर_नोभेम्बर_डिसेम्बर'.split('_'),
		monthsShort : 'जन._फेब्रु._मार्च_अप्रि._मई_जुन_जुलाई._अग._सेप्ट._अक्टो._नोभे._डिसे.'.split('_'),
		monthsParseExact : true,
		weekdays : 'आइतबार_सोमबार_मङ्गलबार_बुधबार_बिहिबार_शुक्रबार_शनिबार'.split('_'),
		weekdaysShort : 'आइत._सोम._मङ्गल._बुध._बिहि._शुक्र._शनि.'.split('_'),
		weekdaysMin : 'आ._सो._मं._बु._बि._शु._श.'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'Aको h:mm बजे',
			LTS : 'Aको h:mm:ss बजे',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY, Aको h:mm बजे',
			LLLL : 'dddd, D MMMM YYYY, Aको h:mm बजे'
		},
		preparse: function (string) {
			return string.replace(/[१२३४५६७८९०]/g, function (match) {
				return numberMap$8[match];
			});
		},
		postformat: function (string) {
			return string.replace(/\d/g, function (match) {
				return symbolMap$9[match];
			});
		},
		meridiemParse: /राति|बिहान|दिउँसो|साँझ/,
		meridiemHour : function (hour, meridiem) {
			if (hour === 12) {
				hour = 0;
			}
			if (meridiem === 'राति') {
				return hour < 4 ? hour : hour + 12;
			} else if (meridiem === 'बिहान') {
				return hour;
			} else if (meridiem === 'दिउँसो') {
				return hour >= 10 ? hour : hour + 12;
			} else if (meridiem === 'साँझ') {
				return hour + 12;
			}
		},
		meridiem : function (hour, minute, isLower) {
			if (hour < 3) {
				return 'राति';
			} else if (hour < 12) {
				return 'बिहान';
			} else if (hour < 16) {
				return 'दिउँसो';
			} else if (hour < 20) {
				return 'साँझ';
			} else {
				return 'राति';
			}
		},
		calendar : {
			sameDay : '[आज] LT',
			nextDay : '[भोलि] LT',
			nextWeek : '[आउँदो] dddd[,] LT',
			lastDay : '[हिजो] LT',
			lastWeek : '[गएको] dddd[,] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : '%sमा',
			past : '%s अगाडि',
			s : 'केही क्षण',
			m : 'एक मिनेट',
			mm : '%d मिनेट',
			h : 'एक घण्टा',
			hh : '%d घण्टा',
			d : 'एक दिन',
			dd : '%d दिन',
			M : 'एक महिना',
			MM : '%d महिना',
			y : 'एक बर्ष',
			yy : '%d बर्ष'
		},
		week : {
			dow : 0, // Sunday is the first day of the week.
			doy : 6  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Dutch (Belgium) [nl-be]
//! author : Joris Röling : https://github.com/jorisroling
//! author : Jacob Middag : https://github.com/middagj

	var monthsShortWithDots$1 = 'jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.'.split('_');
	var monthsShortWithoutDots$1 = 'jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec'.split('_');

	var monthsParse = [/^jan/i, /^feb/i, /^maart|mrt.?$/i, /^apr/i, /^mei$/i, /^jun[i.]?$/i, /^jul[i.]?$/i, /^aug/i, /^sep/i, /^okt/i, /^nov/i, /^dec/i];
	var monthsRegex$1 = /^(januari|februari|maart|april|mei|april|ju[nl]i|augustus|september|oktober|november|december|jan\.?|feb\.?|mrt\.?|apr\.?|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i;

	hooks.defineLocale('nl-be', {
		months : 'januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december'.split('_'),
		monthsShort : function (m, format) {
			if (/-MMM-/.test(format)) {
				return monthsShortWithoutDots$1[m.month()];
			} else {
				return monthsShortWithDots$1[m.month()];
			}
		},

		monthsRegex: monthsRegex$1,
		monthsShortRegex: monthsRegex$1,
		monthsStrictRegex: /^(januari|februari|maart|mei|ju[nl]i|april|augustus|september|oktober|november|december)/i,
		monthsShortStrictRegex: /^(jan\.?|feb\.?|mrt\.?|apr\.?|mei|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i,

		monthsParse : monthsParse,
		longMonthsParse : monthsParse,
		shortMonthsParse : monthsParse,

		weekdays : 'zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag'.split('_'),
		weekdaysShort : 'zo._ma._di._wo._do._vr._za.'.split('_'),
		weekdaysMin : 'Zo_Ma_Di_Wo_Do_Vr_Za'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay: '[vandaag om] LT',
			nextDay: '[morgen om] LT',
			nextWeek: 'dddd [om] LT',
			lastDay: '[gisteren om] LT',
			lastWeek: '[afgelopen] dddd [om] LT',
			sameElse: 'L'
		},
		relativeTime : {
			future : 'over %s',
			past : '%s geleden',
			s : 'een paar seconden',
			m : 'één minuut',
			mm : '%d minuten',
			h : 'één uur',
			hh : '%d uur',
			d : 'één dag',
			dd : '%d dagen',
			M : 'één maand',
			MM : '%d maanden',
			y : 'één jaar',
			yy : '%d jaar'
		},
		ordinalParse: /\d{1,2}(ste|de)/,
		ordinal : function (number) {
			return number + ((number === 1 || number === 8 || number >= 20) ? 'ste' : 'de');
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Dutch [nl]
//! author : Joris Röling : https://github.com/jorisroling
//! author : Jacob Middag : https://github.com/middagj

	var monthsShortWithDots$2 = 'jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.'.split('_');
	var monthsShortWithoutDots$2 = 'jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec'.split('_');

	var monthsParse$1 = [/^jan/i, /^feb/i, /^maart|mrt.?$/i, /^apr/i, /^mei$/i, /^jun[i.]?$/i, /^jul[i.]?$/i, /^aug/i, /^sep/i, /^okt/i, /^nov/i, /^dec/i];
	var monthsRegex$2 = /^(januari|februari|maart|april|mei|april|ju[nl]i|augustus|september|oktober|november|december|jan\.?|feb\.?|mrt\.?|apr\.?|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i;

	hooks.defineLocale('nl', {
		months : 'januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december'.split('_'),
		monthsShort : function (m, format) {
			if (/-MMM-/.test(format)) {
				return monthsShortWithoutDots$2[m.month()];
			} else {
				return monthsShortWithDots$2[m.month()];
			}
		},

		monthsRegex: monthsRegex$2,
		monthsShortRegex: monthsRegex$2,
		monthsStrictRegex: /^(januari|februari|maart|mei|ju[nl]i|april|augustus|september|oktober|november|december)/i,
		monthsShortStrictRegex: /^(jan\.?|feb\.?|mrt\.?|apr\.?|mei|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i,

		monthsParse : monthsParse$1,
		longMonthsParse : monthsParse$1,
		shortMonthsParse : monthsParse$1,

		weekdays : 'zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag'.split('_'),
		weekdaysShort : 'zo._ma._di._wo._do._vr._za.'.split('_'),
		weekdaysMin : 'Zo_Ma_Di_Wo_Do_Vr_Za'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD-MM-YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay: '[vandaag om] LT',
			nextDay: '[morgen om] LT',
			nextWeek: 'dddd [om] LT',
			lastDay: '[gisteren om] LT',
			lastWeek: '[afgelopen] dddd [om] LT',
			sameElse: 'L'
		},
		relativeTime : {
			future : 'over %s',
			past : '%s geleden',
			s : 'een paar seconden',
			m : 'één minuut',
			mm : '%d minuten',
			h : 'één uur',
			hh : '%d uur',
			d : 'één dag',
			dd : '%d dagen',
			M : 'één maand',
			MM : '%d maanden',
			y : 'één jaar',
			yy : '%d jaar'
		},
		ordinalParse: /\d{1,2}(ste|de)/,
		ordinal : function (number) {
			return number + ((number === 1 || number === 8 || number >= 20) ? 'ste' : 'de');
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Nynorsk [no]
//! author : https://github.com/mechuwind

	hooks.defineLocale('no', {
		months : 'januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember'.split('_'),
		monthsShort : 'jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des'.split('_'),
		weekdays : 'sundag_måndag_tysdag_onsdag_torsdag_fredag_laurdag'.split('_'),
		weekdaysShort : 'sun_mån_tys_ons_tor_fre_lau'.split('_'),
		weekdaysMin : 'su_må_ty_on_to_fr_lø'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'D. MMMM YYYY',
			LLL : 'D. MMMM YYYY [kl.] H:mm',
			LLLL : 'dddd D. MMMM YYYY [kl.] HH:mm'
		},
		calendar : {
			sameDay: '[I dag klokka] LT',
			nextDay: '[I morgon klokka] LT',
			nextWeek: 'dddd [klokka] LT',
			lastDay: '[I går klokka] LT',
			lastWeek: '[Føregåande] dddd [klokka] LT',
			sameElse: 'L'
		},
		relativeTime : {
			future : 'om %s',
			past : '%s sidan',
			s : 'nokre sekund',
			m : 'eit minutt',
			mm : '%d minutt',
			h : 'ein time',
			hh : '%d timar',
			d : 'ein dag',
			dd : '%d dagar',
			M : 'ein månad',
			MM : '%d månader',
			y : 'eit år',
			yy : '%d år'
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Punjabi (India) [pa-in]
//! author : Harpreet Singh : https://github.com/harpreetkhalsagtbit

	var symbolMap$10 = {
		'1': '੧',
		'2': '੨',
		'3': '੩',
		'4': '੪',
		'5': '੫',
		'6': '੬',
		'7': '੭',
		'8': '੮',
		'9': '੯',
		'0': '੦'
	};
	var numberMap$9 = {
		'੧': '1',
		'੨': '2',
		'੩': '3',
		'੪': '4',
		'੫': '5',
		'੬': '6',
		'੭': '7',
		'੮': '8',
		'੯': '9',
		'੦': '0'
	};

	hooks.defineLocale('pa-in', {
		// There are months name as per Nanakshahi Calender but they are not used as rigidly in modern Punjabi.
		months : 'ਜਨਵਰੀ_ਫ਼ਰਵਰੀ_ਮਾਰਚ_ਅਪ੍ਰੈਲ_ਮਈ_ਜੂਨ_ਜੁਲਾਈ_ਅਗਸਤ_ਸਤੰਬਰ_ਅਕਤੂਬਰ_ਨਵੰਬਰ_ਦਸੰਬਰ'.split('_'),
		monthsShort : 'ਜਨਵਰੀ_ਫ਼ਰਵਰੀ_ਮਾਰਚ_ਅਪ੍ਰੈਲ_ਮਈ_ਜੂਨ_ਜੁਲਾਈ_ਅਗਸਤ_ਸਤੰਬਰ_ਅਕਤੂਬਰ_ਨਵੰਬਰ_ਦਸੰਬਰ'.split('_'),
		weekdays : 'ਐਤਵਾਰ_ਸੋਮਵਾਰ_ਮੰਗਲਵਾਰ_ਬੁਧਵਾਰ_ਵੀਰਵਾਰ_ਸ਼ੁੱਕਰਵਾਰ_ਸ਼ਨੀਚਰਵਾਰ'.split('_'),
		weekdaysShort : 'ਐਤ_ਸੋਮ_ਮੰਗਲ_ਬੁਧ_ਵੀਰ_ਸ਼ੁਕਰ_ਸ਼ਨੀ'.split('_'),
		weekdaysMin : 'ਐਤ_ਸੋਮ_ਮੰਗਲ_ਬੁਧ_ਵੀਰ_ਸ਼ੁਕਰ_ਸ਼ਨੀ'.split('_'),
		longDateFormat : {
			LT : 'A h:mm ਵਜੇ',
			LTS : 'A h:mm:ss ਵਜੇ',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY, A h:mm ਵਜੇ',
			LLLL : 'dddd, D MMMM YYYY, A h:mm ਵਜੇ'
		},
		calendar : {
			sameDay : '[ਅਜ] LT',
			nextDay : '[ਕਲ] LT',
			nextWeek : 'dddd, LT',
			lastDay : '[ਕਲ] LT',
			lastWeek : '[ਪਿਛਲੇ] dddd, LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : '%s ਵਿੱਚ',
			past : '%s ਪਿਛਲੇ',
			s : 'ਕੁਝ ਸਕਿੰਟ',
			m : 'ਇਕ ਮਿੰਟ',
			mm : '%d ਮਿੰਟ',
			h : 'ਇੱਕ ਘੰਟਾ',
			hh : '%d ਘੰਟੇ',
			d : 'ਇੱਕ ਦਿਨ',
			dd : '%d ਦਿਨ',
			M : 'ਇੱਕ ਮਹੀਨਾ',
			MM : '%d ਮਹੀਨੇ',
			y : 'ਇੱਕ ਸਾਲ',
			yy : '%d ਸਾਲ'
		},
		preparse: function (string) {
			return string.replace(/[੧੨੩੪੫੬੭੮੯੦]/g, function (match) {
				return numberMap$9[match];
			});
		},
		postformat: function (string) {
			return string.replace(/\d/g, function (match) {
				return symbolMap$10[match];
			});
		},
		// Punjabi notation for meridiems are quite fuzzy in practice. While there exists
		// a rigid notion of a 'Pahar' it is not used as rigidly in modern Punjabi.
		meridiemParse: /ਰਾਤ|ਸਵੇਰ|ਦੁਪਹਿਰ|ਸ਼ਾਮ/,
		meridiemHour : function (hour, meridiem) {
			if (hour === 12) {
				hour = 0;
			}
			if (meridiem === 'ਰਾਤ') {
				return hour < 4 ? hour : hour + 12;
			} else if (meridiem === 'ਸਵੇਰ') {
				return hour;
			} else if (meridiem === 'ਦੁਪਹਿਰ') {
				return hour >= 10 ? hour : hour + 12;
			} else if (meridiem === 'ਸ਼ਾਮ') {
				return hour + 12;
			}
		},
		meridiem : function (hour, minute, isLower) {
			if (hour < 4) {
				return 'ਰਾਤ';
			} else if (hour < 10) {
				return 'ਸਵੇਰ';
			} else if (hour < 17) {
				return 'ਦੁਪਹਿਰ';
			} else if (hour < 20) {
				return 'ਸ਼ਾਮ';
			} else {
				return 'ਰਾਤ';
			}
		},
		week : {
			dow : 0, // Sunday is the first day of the week.
			doy : 6  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Polish [pl]
//! author : Rafal Hirsz : https://github.com/evoL

	var monthsNominative = 'styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień'.split('_');
	var monthsSubjective = 'stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_października_listopada_grudnia'.split('_');
	function plural$3(n) {
		return (n % 10 < 5) && (n % 10 > 1) && ((~~(n / 10) % 10) !== 1);
	}
	function translate$7(number, withoutSuffix, key) {
		var result = number + ' ';
		switch (key) {
			case 'm':
				return withoutSuffix ? 'minuta' : 'minutę';
			case 'mm':
				return result + (plural$3(number) ? 'minuty' : 'minut');
			case 'h':
				return withoutSuffix  ? 'godzina'  : 'godzinę';
			case 'hh':
				return result + (plural$3(number) ? 'godziny' : 'godzin');
			case 'MM':
				return result + (plural$3(number) ? 'miesiące' : 'miesięcy');
			case 'yy':
				return result + (plural$3(number) ? 'lata' : 'lat');
		}
	}

	hooks.defineLocale('pl', {
		months : function (momentToFormat, format) {
			if (format === '') {
				// Hack: if format empty we know this is used to generate
				// RegExp by moment. Give then back both valid forms of months
				// in RegExp ready format.
				return '(' + monthsSubjective[momentToFormat.month()] + '|' + monthsNominative[momentToFormat.month()] + ')';
			} else if (/D MMMM/.test(format)) {
				return monthsSubjective[momentToFormat.month()];
			} else {
				return monthsNominative[momentToFormat.month()];
			}
		},
		monthsShort : 'sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paź_lis_gru'.split('_'),
		weekdays : 'niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota'.split('_'),
		weekdaysShort : 'ndz_pon_wt_śr_czw_pt_sob'.split('_'),
		weekdaysMin : 'Nd_Pn_Wt_Śr_Cz_Pt_So'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd, D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay: '[Dziś o] LT',
			nextDay: '[Jutro o] LT',
			nextWeek: '[W] dddd [o] LT',
			lastDay: '[Wczoraj o] LT',
			lastWeek: function () {
				switch (this.day()) {
					case 0:
						return '[W zeszłą niedzielę o] LT';
					case 3:
						return '[W zeszłą środę o] LT';
					case 6:
						return '[W zeszłą sobotę o] LT';
					default:
						return '[W zeszły] dddd [o] LT';
				}
			},
			sameElse: 'L'
		},
		relativeTime : {
			future : 'za %s',
			past : '%s temu',
			s : 'kilka sekund',
			m : translate$7,
			mm : translate$7,
			h : translate$7,
			hh : translate$7,
			d : '1 dzień',
			dd : '%d dni',
			M : 'miesiąc',
			MM : translate$7,
			y : 'rok',
			yy : translate$7
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Portuguese (Brazil) [pt-br]
//! author : Caio Ribeiro Pereira : https://github.com/caio-ribeiro-pereira

	hooks.defineLocale('pt-br', {
		months : 'Janeiro_Fevereiro_Março_Abril_Maio_Junho_Julho_Agosto_Setembro_Outubro_Novembro_Dezembro'.split('_'),
		monthsShort : 'Jan_Fev_Mar_Abr_Mai_Jun_Jul_Ago_Set_Out_Nov_Dez'.split('_'),
		weekdays : 'Domingo_Segunda-feira_Terça-feira_Quarta-feira_Quinta-feira_Sexta-feira_Sábado'.split('_'),
		weekdaysShort : 'Dom_Seg_Ter_Qua_Qui_Sex_Sáb'.split('_'),
		weekdaysMin : 'Dom_2ª_3ª_4ª_5ª_6ª_Sáb'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D [de] MMMM [de] YYYY',
			LLL : 'D [de] MMMM [de] YYYY [às] HH:mm',
			LLLL : 'dddd, D [de] MMMM [de] YYYY [às] HH:mm'
		},
		calendar : {
			sameDay: '[Hoje às] LT',
			nextDay: '[Amanhã às] LT',
			nextWeek: 'dddd [às] LT',
			lastDay: '[Ontem às] LT',
			lastWeek: function () {
				return (this.day() === 0 || this.day() === 6) ?
					'[Último] dddd [às] LT' : // Saturday + Sunday
					'[Última] dddd [às] LT'; // Monday - Friday
			},
			sameElse: 'L'
		},
		relativeTime : {
			future : 'em %s',
			past : '%s atrás',
			s : 'poucos segundos',
			m : 'um minuto',
			mm : '%d minutos',
			h : 'uma hora',
			hh : '%d horas',
			d : 'um dia',
			dd : '%d dias',
			M : 'um mês',
			MM : '%d meses',
			y : 'um ano',
			yy : '%d anos'
		},
		ordinalParse: /\d{1,2}º/,
		ordinal : '%dº'
	});

//! moment.js locale configuration
//! locale : Portuguese [pt]
//! author : Jefferson : https://github.com/jalex79

	hooks.defineLocale('pt', {
		months : 'Janeiro_Fevereiro_Março_Abril_Maio_Junho_Julho_Agosto_Setembro_Outubro_Novembro_Dezembro'.split('_'),
		monthsShort : 'Jan_Fev_Mar_Abr_Mai_Jun_Jul_Ago_Set_Out_Nov_Dez'.split('_'),
		weekdays : 'Domingo_Segunda-Feira_Terça-Feira_Quarta-Feira_Quinta-Feira_Sexta-Feira_Sábado'.split('_'),
		weekdaysShort : 'Dom_Seg_Ter_Qua_Qui_Sex_Sáb'.split('_'),
		weekdaysMin : 'Dom_2ª_3ª_4ª_5ª_6ª_Sáb'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D [de] MMMM [de] YYYY',
			LLL : 'D [de] MMMM [de] YYYY HH:mm',
			LLLL : 'dddd, D [de] MMMM [de] YYYY HH:mm'
		},
		calendar : {
			sameDay: '[Hoje às] LT',
			nextDay: '[Amanhã às] LT',
			nextWeek: 'dddd [às] LT',
			lastDay: '[Ontem às] LT',
			lastWeek: function () {
				return (this.day() === 0 || this.day() === 6) ?
					'[Último] dddd [às] LT' : // Saturday + Sunday
					'[Última] dddd [às] LT'; // Monday - Friday
			},
			sameElse: 'L'
		},
		relativeTime : {
			future : 'em %s',
			past : 'há %s',
			s : 'segundos',
			m : 'um minuto',
			mm : '%d minutos',
			h : 'uma hora',
			hh : '%d horas',
			d : 'um dia',
			dd : '%d dias',
			M : 'um mês',
			MM : '%d meses',
			y : 'um ano',
			yy : '%d anos'
		},
		ordinalParse: /\d{1,2}º/,
		ordinal : '%dº',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Romanian [ro]
//! author : Vlad Gurdiga : https://github.com/gurdiga
//! author : Valentin Agachi : https://github.com/avaly

	function relativeTimeWithPlural$2(number, withoutSuffix, key) {
		var format = {
				'mm': 'minute',
				'hh': 'ore',
				'dd': 'zile',
				'MM': 'luni',
				'yy': 'ani'
			},
			separator = ' ';
		if (number % 100 >= 20 || (number >= 100 && number % 100 === 0)) {
			separator = ' de ';
		}
		return number + separator + format[key];
	}

	hooks.defineLocale('ro', {
		months : 'ianuarie_februarie_martie_aprilie_mai_iunie_iulie_august_septembrie_octombrie_noiembrie_decembrie'.split('_'),
		monthsShort : 'ian._febr._mart._apr._mai_iun._iul._aug._sept._oct._nov._dec.'.split('_'),
		monthsParseExact: true,
		weekdays : 'duminică_luni_marți_miercuri_joi_vineri_sâmbătă'.split('_'),
		weekdaysShort : 'Dum_Lun_Mar_Mie_Joi_Vin_Sâm'.split('_'),
		weekdaysMin : 'Du_Lu_Ma_Mi_Jo_Vi_Sâ'.split('_'),
		longDateFormat : {
			LT : 'H:mm',
			LTS : 'H:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY H:mm',
			LLLL : 'dddd, D MMMM YYYY H:mm'
		},
		calendar : {
			sameDay: '[azi la] LT',
			nextDay: '[mâine la] LT',
			nextWeek: 'dddd [la] LT',
			lastDay: '[ieri la] LT',
			lastWeek: '[fosta] dddd [la] LT',
			sameElse: 'L'
		},
		relativeTime : {
			future : 'peste %s',
			past : '%s în urmă',
			s : 'câteva secunde',
			m : 'un minut',
			mm : relativeTimeWithPlural$2,
			h : 'o oră',
			hh : relativeTimeWithPlural$2,
			d : 'o zi',
			dd : relativeTimeWithPlural$2,
			M : 'o lună',
			MM : relativeTimeWithPlural$2,
			y : 'un an',
			yy : relativeTimeWithPlural$2
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Russian [ru]
//! author : Viktorminator : https://github.com/Viktorminator
//! Author : Menelion Elensúle : https://github.com/Oire
//! author : Коренберг Марк : https://github.com/socketpair

	function plural$4(word, num) {
		var forms = word.split('_');
		return num % 10 === 1 && num % 100 !== 11 ? forms[0] : (num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20) ? forms[1] : forms[2]);
	}
	function relativeTimeWithPlural$3(number, withoutSuffix, key) {
		var format = {
			'mm': withoutSuffix ? 'минута_минуты_минут' : 'минуту_минуты_минут',
			'hh': 'час_часа_часов',
			'dd': 'день_дня_дней',
			'MM': 'месяц_месяца_месяцев',
			'yy': 'год_года_лет'
		};
		if (key === 'm') {
			return withoutSuffix ? 'минута' : 'минуту';
		}
		else {
			return number + ' ' + plural$4(format[key], +number);
		}
	}
	var monthsParse$2 = [/^янв/i, /^фев/i, /^мар/i, /^апр/i, /^ма[йя]/i, /^июн/i, /^июл/i, /^авг/i, /^сен/i, /^окт/i, /^ноя/i, /^дек/i];

// http://new.gramota.ru/spravka/rules/139-prop : § 103
// Сокращения месяцев: http://new.gramota.ru/spravka/buro/search-answer?s=242637
// CLDR data:          http://www.unicode.org/cldr/charts/28/summary/ru.html#1753
	hooks.defineLocale('ru', {
		months : {
			format: 'января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря'.split('_'),
			standalone: 'январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь'.split('_')
		},
		monthsShort : {
			// по CLDR именно "июл." и "июн.", но какой смысл менять букву на точку ?
			format: 'янв._февр._мар._апр._мая_июня_июля_авг._сент._окт._нояб._дек.'.split('_'),
			standalone: 'янв._февр._март_апр._май_июнь_июль_авг._сент._окт._нояб._дек.'.split('_')
		},
		weekdays : {
			standalone: 'воскресенье_понедельник_вторник_среда_четверг_пятница_суббота'.split('_'),
			format: 'воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу'.split('_'),
			isFormat: /\[ ?[Вв] ?(?:прошлую|следующую|эту)? ?\] ?dddd/
		},
		weekdaysShort : 'вс_пн_вт_ср_чт_пт_сб'.split('_'),
		weekdaysMin : 'вс_пн_вт_ср_чт_пт_сб'.split('_'),
		monthsParse : monthsParse$2,
		longMonthsParse : monthsParse$2,
		shortMonthsParse : monthsParse$2,

		// полные названия с падежами, по три буквы, для некоторых, по 4 буквы, сокращения с точкой и без точки
		monthsRegex: /^(январ[ья]|янв\.?|феврал[ья]|февр?\.?|марта?|мар\.?|апрел[ья]|апр\.?|ма[йя]|июн[ья]|июн\.?|июл[ья]|июл\.?|августа?|авг\.?|сентябр[ья]|сент?\.?|октябр[ья]|окт\.?|ноябр[ья]|нояб?\.?|декабр[ья]|дек\.?)/i,

		// копия предыдущего
		monthsShortRegex: /^(январ[ья]|янв\.?|феврал[ья]|февр?\.?|марта?|мар\.?|апрел[ья]|апр\.?|ма[йя]|июн[ья]|июн\.?|июл[ья]|июл\.?|августа?|авг\.?|сентябр[ья]|сент?\.?|октябр[ья]|окт\.?|ноябр[ья]|нояб?\.?|декабр[ья]|дек\.?)/i,

		// полные названия с падежами
		monthsStrictRegex: /^(январ[яь]|феврал[яь]|марта?|апрел[яь]|ма[яй]|июн[яь]|июл[яь]|августа?|сентябр[яь]|октябр[яь]|ноябр[яь]|декабр[яь])/i,

		// Выражение, которое соотвествует только сокращённым формам
		monthsShortStrictRegex: /^(янв\.|февр?\.|мар[т.]|апр\.|ма[яй]|июн[ья.]|июл[ья.]|авг\.|сент?\.|окт\.|нояб?\.|дек\.)/i,
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'D MMMM YYYY г.',
			LLL : 'D MMMM YYYY г., HH:mm',
			LLLL : 'dddd, D MMMM YYYY г., HH:mm'
		},
		calendar : {
			sameDay: '[Сегодня в] LT',
			nextDay: '[Завтра в] LT',
			lastDay: '[Вчера в] LT',
			nextWeek: function (now) {
				if (now.week() !== this.week()) {
					switch (this.day()) {
						case 0:
							return '[В следующее] dddd [в] LT';
						case 1:
						case 2:
						case 4:
							return '[В следующий] dddd [в] LT';
						case 3:
						case 5:
						case 6:
							return '[В следующую] dddd [в] LT';
					}
				} else {
					if (this.day() === 2) {
						return '[Во] dddd [в] LT';
					} else {
						return '[В] dddd [в] LT';
					}
				}
			},
			lastWeek: function (now) {
				if (now.week() !== this.week()) {
					switch (this.day()) {
						case 0:
							return '[В прошлое] dddd [в] LT';
						case 1:
						case 2:
						case 4:
							return '[В прошлый] dddd [в] LT';
						case 3:
						case 5:
						case 6:
							return '[В прошлую] dddd [в] LT';
					}
				} else {
					if (this.day() === 2) {
						return '[Во] dddd [в] LT';
					} else {
						return '[В] dddd [в] LT';
					}
				}
			},
			sameElse: 'L'
		},
		relativeTime : {
			future : 'через %s',
			past : '%s назад',
			s : 'несколько секунд',
			m : relativeTimeWithPlural$3,
			mm : relativeTimeWithPlural$3,
			h : 'час',
			hh : relativeTimeWithPlural$3,
			d : 'день',
			dd : relativeTimeWithPlural$3,
			M : 'месяц',
			MM : relativeTimeWithPlural$3,
			y : 'год',
			yy : relativeTimeWithPlural$3
		},
		meridiemParse: /ночи|утра|дня|вечера/i,
		isPM : function (input) {
			return /^(дня|вечера)$/.test(input);
		},
		meridiem : function (hour, minute, isLower) {
			if (hour < 4) {
				return 'ночи';
			} else if (hour < 12) {
				return 'утра';
			} else if (hour < 17) {
				return 'дня';
			} else {
				return 'вечера';
			}
		},
		ordinalParse: /\d{1,2}-(й|го|я)/,
		ordinal: function (number, period) {
			switch (period) {
				case 'M':
				case 'd':
				case 'DDD':
					return number + '-й';
				case 'D':
					return number + '-го';
				case 'w':
				case 'W':
					return number + '-я';
				default:
					return number;
			}
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Northern Sami [se]
//! authors : Bård Rolstad Henriksen : https://github.com/karamell


	hooks.defineLocale('se', {
		months : 'ođđajagemánnu_guovvamánnu_njukčamánnu_cuoŋománnu_miessemánnu_geassemánnu_suoidnemánnu_borgemánnu_čakčamánnu_golggotmánnu_skábmamánnu_juovlamánnu'.split('_'),
		monthsShort : 'ođđj_guov_njuk_cuo_mies_geas_suoi_borg_čakč_golg_skáb_juov'.split('_'),
		weekdays : 'sotnabeaivi_vuossárga_maŋŋebárga_gaskavahkku_duorastat_bearjadat_lávvardat'.split('_'),
		weekdaysShort : 'sotn_vuos_maŋ_gask_duor_bear_láv'.split('_'),
		weekdaysMin : 's_v_m_g_d_b_L'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'MMMM D. [b.] YYYY',
			LLL : 'MMMM D. [b.] YYYY [ti.] HH:mm',
			LLLL : 'dddd, MMMM D. [b.] YYYY [ti.] HH:mm'
		},
		calendar : {
			sameDay: '[otne ti] LT',
			nextDay: '[ihttin ti] LT',
			nextWeek: 'dddd [ti] LT',
			lastDay: '[ikte ti] LT',
			lastWeek: '[ovddit] dddd [ti] LT',
			sameElse: 'L'
		},
		relativeTime : {
			future : '%s geažes',
			past : 'maŋit %s',
			s : 'moadde sekunddat',
			m : 'okta minuhta',
			mm : '%d minuhtat',
			h : 'okta diimmu',
			hh : '%d diimmut',
			d : 'okta beaivi',
			dd : '%d beaivvit',
			M : 'okta mánnu',
			MM : '%d mánut',
			y : 'okta jahki',
			yy : '%d jagit'
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Sinhalese [si]
//! author : Sampath Sitinamaluwa : https://github.com/sampathsris

    /*jshint -W100*/
	hooks.defineLocale('si', {
		months : 'ජනවාරි_පෙබරවාරි_මාර්තු_අප්‍රේල්_මැයි_ජූනි_ජූලි_අගෝස්තු_සැප්තැම්බර්_ඔක්තෝබර්_නොවැම්බර්_දෙසැම්බර්'.split('_'),
		monthsShort : 'ජන_පෙබ_මාර්_අප්_මැයි_ජූනි_ජූලි_අගෝ_සැප්_ඔක්_නොවැ_දෙසැ'.split('_'),
		weekdays : 'ඉරිදා_සඳුදා_අඟහරුවාදා_බදාදා_බ්‍රහස්පතින්දා_සිකුරාදා_සෙනසුරාදා'.split('_'),
		weekdaysShort : 'ඉරි_සඳු_අඟ_බදා_බ්‍රහ_සිකු_සෙන'.split('_'),
		weekdaysMin : 'ඉ_ස_අ_බ_බ්‍ර_සි_සෙ'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'a h:mm',
			LTS : 'a h:mm:ss',
			L : 'YYYY/MM/DD',
			LL : 'YYYY MMMM D',
			LLL : 'YYYY MMMM D, a h:mm',
			LLLL : 'YYYY MMMM D [වැනි] dddd, a h:mm:ss'
		},
		calendar : {
			sameDay : '[අද] LT[ට]',
			nextDay : '[හෙට] LT[ට]',
			nextWeek : 'dddd LT[ට]',
			lastDay : '[ඊයේ] LT[ට]',
			lastWeek : '[පසුගිය] dddd LT[ට]',
			sameElse : 'L'
		},
		relativeTime : {
			future : '%sකින්',
			past : '%sකට පෙර',
			s : 'තත්පර කිහිපය',
			m : 'මිනිත්තුව',
			mm : 'මිනිත්තු %d',
			h : 'පැය',
			hh : 'පැය %d',
			d : 'දිනය',
			dd : 'දින %d',
			M : 'මාසය',
			MM : 'මාස %d',
			y : 'වසර',
			yy : 'වසර %d'
		},
		ordinalParse: /\d{1,2} වැනි/,
		ordinal : function (number) {
			return number + ' වැනි';
		},
		meridiemParse : /පෙර වරු|පස් වරු|පෙ.ව|ප.ව./,
		isPM : function (input) {
			return input === 'ප.ව.' || input === 'පස් වරු';
		},
		meridiem : function (hours, minutes, isLower) {
			if (hours > 11) {
				return isLower ? 'ප.ව.' : 'පස් වරු';
			} else {
				return isLower ? 'පෙ.ව.' : 'පෙර වරු';
			}
		}
	});

//! moment.js locale configuration
//! locale : Slovak [sk]
//! author : Martin Minka : https://github.com/k2s
//! based on work of petrbela : https://github.com/petrbela

	var months$6 = 'január_február_marec_apríl_máj_jún_júl_august_september_október_november_december'.split('_');
	var monthsShort$4 = 'jan_feb_mar_apr_máj_jún_júl_aug_sep_okt_nov_dec'.split('_');
	function plural$5(n) {
		return (n > 1) && (n < 5);
	}
	function translate$8(number, withoutSuffix, key, isFuture) {
		var result = number + ' ';
		switch (key) {
			case 's':  // a few seconds / in a few seconds / a few seconds ago
				return (withoutSuffix || isFuture) ? 'pár sekúnd' : 'pár sekundami';
			case 'm':  // a minute / in a minute / a minute ago
				return withoutSuffix ? 'minúta' : (isFuture ? 'minútu' : 'minútou');
			case 'mm': // 9 minutes / in 9 minutes / 9 minutes ago
				if (withoutSuffix || isFuture) {
					return result + (plural$5(number) ? 'minúty' : 'minút');
				} else {
					return result + 'minútami';
				}
				break;
			case 'h':  // an hour / in an hour / an hour ago
				return withoutSuffix ? 'hodina' : (isFuture ? 'hodinu' : 'hodinou');
			case 'hh': // 9 hours / in 9 hours / 9 hours ago
				if (withoutSuffix || isFuture) {
					return result + (plural$5(number) ? 'hodiny' : 'hodín');
				} else {
					return result + 'hodinami';
				}
				break;
			case 'd':  // a day / in a day / a day ago
				return (withoutSuffix || isFuture) ? 'deň' : 'dňom';
			case 'dd': // 9 days / in 9 days / 9 days ago
				if (withoutSuffix || isFuture) {
					return result + (plural$5(number) ? 'dni' : 'dní');
				} else {
					return result + 'dňami';
				}
				break;
			case 'M':  // a month / in a month / a month ago
				return (withoutSuffix || isFuture) ? 'mesiac' : 'mesiacom';
			case 'MM': // 9 months / in 9 months / 9 months ago
				if (withoutSuffix || isFuture) {
					return result + (plural$5(number) ? 'mesiace' : 'mesiacov');
				} else {
					return result + 'mesiacmi';
				}
				break;
			case 'y':  // a year / in a year / a year ago
				return (withoutSuffix || isFuture) ? 'rok' : 'rokom';
			case 'yy': // 9 years / in 9 years / 9 years ago
				if (withoutSuffix || isFuture) {
					return result + (plural$5(number) ? 'roky' : 'rokov');
				} else {
					return result + 'rokmi';
				}
				break;
		}
	}

	hooks.defineLocale('sk', {
		months : months$6,
		monthsShort : monthsShort$4,
		weekdays : 'nedeľa_pondelok_utorok_streda_štvrtok_piatok_sobota'.split('_'),
		weekdaysShort : 'ne_po_ut_st_št_pi_so'.split('_'),
		weekdaysMin : 'ne_po_ut_st_št_pi_so'.split('_'),
		longDateFormat : {
			LT: 'H:mm',
			LTS : 'H:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'D. MMMM YYYY',
			LLL : 'D. MMMM YYYY H:mm',
			LLLL : 'dddd D. MMMM YYYY H:mm'
		},
		calendar : {
			sameDay: '[dnes o] LT',
			nextDay: '[zajtra o] LT',
			nextWeek: function () {
				switch (this.day()) {
					case 0:
						return '[v nedeľu o] LT';
					case 1:
					case 2:
						return '[v] dddd [o] LT';
					case 3:
						return '[v stredu o] LT';
					case 4:
						return '[vo štvrtok o] LT';
					case 5:
						return '[v piatok o] LT';
					case 6:
						return '[v sobotu o] LT';
				}
			},
			lastDay: '[včera o] LT',
			lastWeek: function () {
				switch (this.day()) {
					case 0:
						return '[minulú nedeľu o] LT';
					case 1:
					case 2:
						return '[minulý] dddd [o] LT';
					case 3:
						return '[minulú stredu o] LT';
					case 4:
					case 5:
						return '[minulý] dddd [o] LT';
					case 6:
						return '[minulú sobotu o] LT';
				}
			},
			sameElse: 'L'
		},
		relativeTime : {
			future : 'za %s',
			past : 'pred %s',
			s : translate$8,
			m : translate$8,
			mm : translate$8,
			h : translate$8,
			hh : translate$8,
			d : translate$8,
			dd : translate$8,
			M : translate$8,
			MM : translate$8,
			y : translate$8,
			yy : translate$8
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Slovenian [sl]
//! author : Robert Sedovšek : https://github.com/sedovsek

	function processRelativeTime$4(number, withoutSuffix, key, isFuture) {
		var result = number + ' ';
		switch (key) {
			case 's':
				return withoutSuffix || isFuture ? 'nekaj sekund' : 'nekaj sekundami';
			case 'm':
				return withoutSuffix ? 'ena minuta' : 'eno minuto';
			case 'mm':
				if (number === 1) {
					result += withoutSuffix ? 'minuta' : 'minuto';
				} else if (number === 2) {
					result += withoutSuffix || isFuture ? 'minuti' : 'minutama';
				} else if (number < 5) {
					result += withoutSuffix || isFuture ? 'minute' : 'minutami';
				} else {
					result += withoutSuffix || isFuture ? 'minut' : 'minutami';
				}
				return result;
			case 'h':
				return withoutSuffix ? 'ena ura' : 'eno uro';
			case 'hh':
				if (number === 1) {
					result += withoutSuffix ? 'ura' : 'uro';
				} else if (number === 2) {
					result += withoutSuffix || isFuture ? 'uri' : 'urama';
				} else if (number < 5) {
					result += withoutSuffix || isFuture ? 'ure' : 'urami';
				} else {
					result += withoutSuffix || isFuture ? 'ur' : 'urami';
				}
				return result;
			case 'd':
				return withoutSuffix || isFuture ? 'en dan' : 'enim dnem';
			case 'dd':
				if (number === 1) {
					result += withoutSuffix || isFuture ? 'dan' : 'dnem';
				} else if (number === 2) {
					result += withoutSuffix || isFuture ? 'dni' : 'dnevoma';
				} else {
					result += withoutSuffix || isFuture ? 'dni' : 'dnevi';
				}
				return result;
			case 'M':
				return withoutSuffix || isFuture ? 'en mesec' : 'enim mesecem';
			case 'MM':
				if (number === 1) {
					result += withoutSuffix || isFuture ? 'mesec' : 'mesecem';
				} else if (number === 2) {
					result += withoutSuffix || isFuture ? 'meseca' : 'mesecema';
				} else if (number < 5) {
					result += withoutSuffix || isFuture ? 'mesece' : 'meseci';
				} else {
					result += withoutSuffix || isFuture ? 'mesecev' : 'meseci';
				}
				return result;
			case 'y':
				return withoutSuffix || isFuture ? 'eno leto' : 'enim letom';
			case 'yy':
				if (number === 1) {
					result += withoutSuffix || isFuture ? 'leto' : 'letom';
				} else if (number === 2) {
					result += withoutSuffix || isFuture ? 'leti' : 'letoma';
				} else if (number < 5) {
					result += withoutSuffix || isFuture ? 'leta' : 'leti';
				} else {
					result += withoutSuffix || isFuture ? 'let' : 'leti';
				}
				return result;
		}
	}

	hooks.defineLocale('sl', {
		months : 'januar_februar_marec_april_maj_junij_julij_avgust_september_oktober_november_december'.split('_'),
		monthsShort : 'jan._feb._mar._apr._maj._jun._jul._avg._sep._okt._nov._dec.'.split('_'),
		monthsParseExact: true,
		weekdays : 'nedelja_ponedeljek_torek_sreda_četrtek_petek_sobota'.split('_'),
		weekdaysShort : 'ned._pon._tor._sre._čet._pet._sob.'.split('_'),
		weekdaysMin : 'ne_po_to_sr_če_pe_so'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'H:mm',
			LTS : 'H:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'D. MMMM YYYY',
			LLL : 'D. MMMM YYYY H:mm',
			LLLL : 'dddd, D. MMMM YYYY H:mm'
		},
		calendar : {
			sameDay  : '[danes ob] LT',
			nextDay  : '[jutri ob] LT',

			nextWeek : function () {
				switch (this.day()) {
					case 0:
						return '[v] [nedeljo] [ob] LT';
					case 3:
						return '[v] [sredo] [ob] LT';
					case 6:
						return '[v] [soboto] [ob] LT';
					case 1:
					case 2:
					case 4:
					case 5:
						return '[v] dddd [ob] LT';
				}
			},
			lastDay  : '[včeraj ob] LT',
			lastWeek : function () {
				switch (this.day()) {
					case 0:
						return '[prejšnjo] [nedeljo] [ob] LT';
					case 3:
						return '[prejšnjo] [sredo] [ob] LT';
					case 6:
						return '[prejšnjo] [soboto] [ob] LT';
					case 1:
					case 2:
					case 4:
					case 5:
						return '[prejšnji] dddd [ob] LT';
				}
			},
			sameElse : 'L'
		},
		relativeTime : {
			future : 'čez %s',
			past   : 'pred %s',
			s      : processRelativeTime$4,
			m      : processRelativeTime$4,
			mm     : processRelativeTime$4,
			h      : processRelativeTime$4,
			hh     : processRelativeTime$4,
			d      : processRelativeTime$4,
			dd     : processRelativeTime$4,
			M      : processRelativeTime$4,
			MM     : processRelativeTime$4,
			y      : processRelativeTime$4,
			yy     : processRelativeTime$4
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Albanian [sq]
//! author : Flakërim Ismani : https://github.com/flakerimi
//! author : Menelion Elensúle : https://github.com/Oire
//! author : Oerd Cukalla : https://github.com/oerd

	hooks.defineLocale('sq', {
		months : 'Janar_Shkurt_Mars_Prill_Maj_Qershor_Korrik_Gusht_Shtator_Tetor_Nëntor_Dhjetor'.split('_'),
		monthsShort : 'Jan_Shk_Mar_Pri_Maj_Qer_Kor_Gus_Sht_Tet_Nën_Dhj'.split('_'),
		weekdays : 'E Diel_E Hënë_E Martë_E Mërkurë_E Enjte_E Premte_E Shtunë'.split('_'),
		weekdaysShort : 'Die_Hën_Mar_Mër_Enj_Pre_Sht'.split('_'),
		weekdaysMin : 'D_H_Ma_Më_E_P_Sh'.split('_'),
		weekdaysParseExact : true,
		meridiemParse: /PD|MD/,
		isPM: function (input) {
			return input.charAt(0) === 'M';
		},
		meridiem : function (hours, minutes, isLower) {
			return hours < 12 ? 'PD' : 'MD';
		},
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd, D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay : '[Sot në] LT',
			nextDay : '[Nesër në] LT',
			nextWeek : 'dddd [në] LT',
			lastDay : '[Dje në] LT',
			lastWeek : 'dddd [e kaluar në] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'në %s',
			past : '%s më parë',
			s : 'disa sekonda',
			m : 'një minutë',
			mm : '%d minuta',
			h : 'një orë',
			hh : '%d orë',
			d : 'një ditë',
			dd : '%d ditë',
			M : 'një muaj',
			MM : '%d muaj',
			y : 'një vit',
			yy : '%d vite'
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Serbian Cyrillic [sr-cyrl]
//! author : Milan Janačković<milanjanackovic@gmail.com> : https://github.com/milan-j

	var translator$1 = {
		words: { //Different grammatical cases
			m: ['један минут', 'једне минуте'],
			mm: ['минут', 'минуте', 'минута'],
			h: ['један сат', 'једног сата'],
			hh: ['сат', 'сата', 'сати'],
			dd: ['дан', 'дана', 'дана'],
			MM: ['месец', 'месеца', 'месеци'],
			yy: ['година', 'године', 'година']
		},
		correctGrammaticalCase: function (number, wordKey) {
			return number === 1 ? wordKey[0] : (number >= 2 && number <= 4 ? wordKey[1] : wordKey[2]);
		},
		translate: function (number, withoutSuffix, key) {
			var wordKey = translator$1.words[key];
			if (key.length === 1) {
				return withoutSuffix ? wordKey[0] : wordKey[1];
			} else {
				return number + ' ' + translator$1.correctGrammaticalCase(number, wordKey);
			}
		}
	};

	hooks.defineLocale('sr-cyrl', {
		months: 'јануар_фебруар_март_април_мај_јун_јул_август_септембар_октобар_новембар_децембар'.split('_'),
		monthsShort: 'јан._феб._мар._апр._мај_јун_јул_авг._сеп._окт._нов._дец.'.split('_'),
		monthsParseExact: true,
		weekdays: 'недеља_понедељак_уторак_среда_четвртак_петак_субота'.split('_'),
		weekdaysShort: 'нед._пон._уто._сре._чет._пет._суб.'.split('_'),
		weekdaysMin: 'не_по_ут_ср_че_пе_су'.split('_'),
		weekdaysParseExact : true,
		longDateFormat: {
			LT: 'H:mm',
			LTS : 'H:mm:ss',
			L: 'DD.MM.YYYY',
			LL: 'D. MMMM YYYY',
			LLL: 'D. MMMM YYYY H:mm',
			LLLL: 'dddd, D. MMMM YYYY H:mm'
		},
		calendar: {
			sameDay: '[данас у] LT',
			nextDay: '[сутра у] LT',
			nextWeek: function () {
				switch (this.day()) {
					case 0:
						return '[у] [недељу] [у] LT';
					case 3:
						return '[у] [среду] [у] LT';
					case 6:
						return '[у] [суботу] [у] LT';
					case 1:
					case 2:
					case 4:
					case 5:
						return '[у] dddd [у] LT';
				}
			},
			lastDay  : '[јуче у] LT',
			lastWeek : function () {
				var lastWeekDays = [
					'[прошле] [недеље] [у] LT',
					'[прошлог] [понедељка] [у] LT',
					'[прошлог] [уторка] [у] LT',
					'[прошле] [среде] [у] LT',
					'[прошлог] [четвртка] [у] LT',
					'[прошлог] [петка] [у] LT',
					'[прошле] [суботе] [у] LT'
				];
				return lastWeekDays[this.day()];
			},
			sameElse : 'L'
		},
		relativeTime : {
			future : 'за %s',
			past   : 'пре %s',
			s      : 'неколико секунди',
			m      : translator$1.translate,
			mm     : translator$1.translate,
			h      : translator$1.translate,
			hh     : translator$1.translate,
			d      : 'дан',
			dd     : translator$1.translate,
			M      : 'месец',
			MM     : translator$1.translate,
			y      : 'годину',
			yy     : translator$1.translate
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Serbian [sr]
//! author : Milan Janačković<milanjanackovic@gmail.com> : https://github.com/milan-j

	var translator$2 = {
		words: { //Different grammatical cases
			m: ['jedan minut', 'jedne minute'],
			mm: ['minut', 'minute', 'minuta'],
			h: ['jedan sat', 'jednog sata'],
			hh: ['sat', 'sata', 'sati'],
			dd: ['dan', 'dana', 'dana'],
			MM: ['mesec', 'meseca', 'meseci'],
			yy: ['godina', 'godine', 'godina']
		},
		correctGrammaticalCase: function (number, wordKey) {
			return number === 1 ? wordKey[0] : (number >= 2 && number <= 4 ? wordKey[1] : wordKey[2]);
		},
		translate: function (number, withoutSuffix, key) {
			var wordKey = translator$2.words[key];
			if (key.length === 1) {
				return withoutSuffix ? wordKey[0] : wordKey[1];
			} else {
				return number + ' ' + translator$2.correctGrammaticalCase(number, wordKey);
			}
		}
	};

	hooks.defineLocale('sr', {
		months: 'januar_februar_mart_april_maj_jun_jul_avgust_septembar_oktobar_novembar_decembar'.split('_'),
		monthsShort: 'jan._feb._mar._apr._maj_jun_jul_avg._sep._okt._nov._dec.'.split('_'),
		monthsParseExact: true,
		weekdays: 'nedelja_ponedeljak_utorak_sreda_četvrtak_petak_subota'.split('_'),
		weekdaysShort: 'ned._pon._uto._sre._čet._pet._sub.'.split('_'),
		weekdaysMin: 'ne_po_ut_sr_če_pe_su'.split('_'),
		weekdaysParseExact : true,
		longDateFormat: {
			LT: 'H:mm',
			LTS : 'H:mm:ss',
			L: 'DD.MM.YYYY',
			LL: 'D. MMMM YYYY',
			LLL: 'D. MMMM YYYY H:mm',
			LLLL: 'dddd, D. MMMM YYYY H:mm'
		},
		calendar: {
			sameDay: '[danas u] LT',
			nextDay: '[sutra u] LT',
			nextWeek: function () {
				switch (this.day()) {
					case 0:
						return '[u] [nedelju] [u] LT';
					case 3:
						return '[u] [sredu] [u] LT';
					case 6:
						return '[u] [subotu] [u] LT';
					case 1:
					case 2:
					case 4:
					case 5:
						return '[u] dddd [u] LT';
				}
			},
			lastDay  : '[juče u] LT',
			lastWeek : function () {
				var lastWeekDays = [
					'[prošle] [nedelje] [u] LT',
					'[prošlog] [ponedeljka] [u] LT',
					'[prošlog] [utorka] [u] LT',
					'[prošle] [srede] [u] LT',
					'[prošlog] [četvrtka] [u] LT',
					'[prošlog] [petka] [u] LT',
					'[prošle] [subote] [u] LT'
				];
				return lastWeekDays[this.day()];
			},
			sameElse : 'L'
		},
		relativeTime : {
			future : 'za %s',
			past   : 'pre %s',
			s      : 'nekoliko sekundi',
			m      : translator$2.translate,
			mm     : translator$2.translate,
			h      : translator$2.translate,
			hh     : translator$2.translate,
			d      : 'dan',
			dd     : translator$2.translate,
			M      : 'mesec',
			MM     : translator$2.translate,
			y      : 'godinu',
			yy     : translator$2.translate
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : siSwati [ss]
//! author : Nicolai Davies<mail@nicolai.io> : https://github.com/nicolaidavies


	hooks.defineLocale('ss', {
		months : "Bhimbidvwane_Indlovana_Indlov'lenkhulu_Mabasa_Inkhwekhweti_Inhlaba_Kholwane_Ingci_Inyoni_Imphala_Lweti_Ingongoni".split('_'),
		monthsShort : 'Bhi_Ina_Inu_Mab_Ink_Inh_Kho_Igc_Iny_Imp_Lwe_Igo'.split('_'),
		weekdays : 'Lisontfo_Umsombuluko_Lesibili_Lesitsatfu_Lesine_Lesihlanu_Umgcibelo'.split('_'),
		weekdaysShort : 'Lis_Umb_Lsb_Les_Lsi_Lsh_Umg'.split('_'),
		weekdaysMin : 'Li_Us_Lb_Lt_Ls_Lh_Ug'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'h:mm A',
			LTS : 'h:mm:ss A',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY h:mm A',
			LLLL : 'dddd, D MMMM YYYY h:mm A'
		},
		calendar : {
			sameDay : '[Namuhla nga] LT',
			nextDay : '[Kusasa nga] LT',
			nextWeek : 'dddd [nga] LT',
			lastDay : '[Itolo nga] LT',
			lastWeek : 'dddd [leliphelile] [nga] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'nga %s',
			past : 'wenteka nga %s',
			s : 'emizuzwana lomcane',
			m : 'umzuzu',
			mm : '%d emizuzu',
			h : 'lihora',
			hh : '%d emahora',
			d : 'lilanga',
			dd : '%d emalanga',
			M : 'inyanga',
			MM : '%d tinyanga',
			y : 'umnyaka',
			yy : '%d iminyaka'
		},
		meridiemParse: /ekuseni|emini|entsambama|ebusuku/,
		meridiem : function (hours, minutes, isLower) {
			if (hours < 11) {
				return 'ekuseni';
			} else if (hours < 15) {
				return 'emini';
			} else if (hours < 19) {
				return 'entsambama';
			} else {
				return 'ebusuku';
			}
		},
		meridiemHour : function (hour, meridiem) {
			if (hour === 12) {
				hour = 0;
			}
			if (meridiem === 'ekuseni') {
				return hour;
			} else if (meridiem === 'emini') {
				return hour >= 11 ? hour : hour + 12;
			} else if (meridiem === 'entsambama' || meridiem === 'ebusuku') {
				if (hour === 0) {
					return 0;
				}
				return hour + 12;
			}
		},
		ordinalParse: /\d{1,2}/,
		ordinal : '%d',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Swedish [sv]
//! author : Jens Alm : https://github.com/ulmus

	hooks.defineLocale('sv', {
		months : 'januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december'.split('_'),
		monthsShort : 'jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec'.split('_'),
		weekdays : 'söndag_måndag_tisdag_onsdag_torsdag_fredag_lördag'.split('_'),
		weekdaysShort : 'sön_mån_tis_ons_tor_fre_lör'.split('_'),
		weekdaysMin : 'sö_må_ti_on_to_fr_lö'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'YYYY-MM-DD',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY [kl.] HH:mm',
			LLLL : 'dddd D MMMM YYYY [kl.] HH:mm',
			lll : 'D MMM YYYY HH:mm',
			llll : 'ddd D MMM YYYY HH:mm'
		},
		calendar : {
			sameDay: '[Idag] LT',
			nextDay: '[Imorgon] LT',
			lastDay: '[Igår] LT',
			nextWeek: '[På] dddd LT',
			lastWeek: '[I] dddd[s] LT',
			sameElse: 'L'
		},
		relativeTime : {
			future : 'om %s',
			past : 'för %s sedan',
			s : 'några sekunder',
			m : 'en minut',
			mm : '%d minuter',
			h : 'en timme',
			hh : '%d timmar',
			d : 'en dag',
			dd : '%d dagar',
			M : 'en månad',
			MM : '%d månader',
			y : 'ett år',
			yy : '%d år'
		},
		ordinalParse: /\d{1,2}(e|a)/,
		ordinal : function (number) {
			var b = number % 10,
				output = (~~(number % 100 / 10) === 1) ? 'e' :
					(b === 1) ? 'a' :
						(b === 2) ? 'a' :
							(b === 3) ? 'e' : 'e';
			return number + output;
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Swahili [sw]
//! author : Fahad Kassim : https://github.com/fadsel

	hooks.defineLocale('sw', {
		months : 'Januari_Februari_Machi_Aprili_Mei_Juni_Julai_Agosti_Septemba_Oktoba_Novemba_Desemba'.split('_'),
		monthsShort : 'Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ago_Sep_Okt_Nov_Des'.split('_'),
		weekdays : 'Jumapili_Jumatatu_Jumanne_Jumatano_Alhamisi_Ijumaa_Jumamosi'.split('_'),
		weekdaysShort : 'Jpl_Jtat_Jnne_Jtan_Alh_Ijm_Jmos'.split('_'),
		weekdaysMin : 'J2_J3_J4_J5_Al_Ij_J1'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd, D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay : '[leo saa] LT',
			nextDay : '[kesho saa] LT',
			nextWeek : '[wiki ijayo] dddd [saat] LT',
			lastDay : '[jana] LT',
			lastWeek : '[wiki iliyopita] dddd [saat] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : '%s baadaye',
			past : 'tokea %s',
			s : 'hivi punde',
			m : 'dakika moja',
			mm : 'dakika %d',
			h : 'saa limoja',
			hh : 'masaa %d',
			d : 'siku moja',
			dd : 'masiku %d',
			M : 'mwezi mmoja',
			MM : 'miezi %d',
			y : 'mwaka mmoja',
			yy : 'miaka %d'
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Tamil [ta]
//! author : Arjunkumar Krishnamoorthy : https://github.com/tk120404

	var symbolMap$11 = {
		'1': '௧',
		'2': '௨',
		'3': '௩',
		'4': '௪',
		'5': '௫',
		'6': '௬',
		'7': '௭',
		'8': '௮',
		'9': '௯',
		'0': '௦'
	};
	var numberMap$10 = {
		'௧': '1',
		'௨': '2',
		'௩': '3',
		'௪': '4',
		'௫': '5',
		'௬': '6',
		'௭': '7',
		'௮': '8',
		'௯': '9',
		'௦': '0'
	};

	hooks.defineLocale('ta', {
		months : 'ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்'.split('_'),
		monthsShort : 'ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்'.split('_'),
		weekdays : 'ஞாயிற்றுக்கிழமை_திங்கட்கிழமை_செவ்வாய்கிழமை_புதன்கிழமை_வியாழக்கிழமை_வெள்ளிக்கிழமை_சனிக்கிழமை'.split('_'),
		weekdaysShort : 'ஞாயிறு_திங்கள்_செவ்வாய்_புதன்_வியாழன்_வெள்ளி_சனி'.split('_'),
		weekdaysMin : 'ஞா_தி_செ_பு_வி_வெ_ச'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY, HH:mm',
			LLLL : 'dddd, D MMMM YYYY, HH:mm'
		},
		calendar : {
			sameDay : '[இன்று] LT',
			nextDay : '[நாளை] LT',
			nextWeek : 'dddd, LT',
			lastDay : '[நேற்று] LT',
			lastWeek : '[கடந்த வாரம்] dddd, LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : '%s இல்',
			past : '%s முன்',
			s : 'ஒரு சில விநாடிகள்',
			m : 'ஒரு நிமிடம்',
			mm : '%d நிமிடங்கள்',
			h : 'ஒரு மணி நேரம்',
			hh : '%d மணி நேரம்',
			d : 'ஒரு நாள்',
			dd : '%d நாட்கள்',
			M : 'ஒரு மாதம்',
			MM : '%d மாதங்கள்',
			y : 'ஒரு வருடம்',
			yy : '%d ஆண்டுகள்'
		},
		ordinalParse: /\d{1,2}வது/,
		ordinal : function (number) {
			return number + 'வது';
		},
		preparse: function (string) {
			return string.replace(/[௧௨௩௪௫௬௭௮௯௦]/g, function (match) {
				return numberMap$10[match];
			});
		},
		postformat: function (string) {
			return string.replace(/\d/g, function (match) {
				return symbolMap$11[match];
			});
		},
		// refer http://ta.wikipedia.org/s/1er1
		meridiemParse: /யாமம்|வைகறை|காலை|நண்பகல்|எற்பாடு|மாலை/,
		meridiem : function (hour, minute, isLower) {
			if (hour < 2) {
				return ' யாமம்';
			} else if (hour < 6) {
				return ' வைகறை';  // வைகறை
			} else if (hour < 10) {
				return ' காலை'; // காலை
			} else if (hour < 14) {
				return ' நண்பகல்'; // நண்பகல்
			} else if (hour < 18) {
				return ' எற்பாடு'; // எற்பாடு
			} else if (hour < 22) {
				return ' மாலை'; // மாலை
			} else {
				return ' யாமம்';
			}
		},
		meridiemHour : function (hour, meridiem) {
			if (hour === 12) {
				hour = 0;
			}
			if (meridiem === 'யாமம்') {
				return hour < 2 ? hour : hour + 12;
			} else if (meridiem === 'வைகறை' || meridiem === 'காலை') {
				return hour;
			} else if (meridiem === 'நண்பகல்') {
				return hour >= 10 ? hour : hour + 12;
			} else {
				return hour + 12;
			}
		},
		week : {
			dow : 0, // Sunday is the first day of the week.
			doy : 6  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Telugu [te]
//! author : Krishna Chaitanya Thota : https://github.com/kcthota

	hooks.defineLocale('te', {
		months : 'జనవరి_ఫిబ్రవరి_మార్చి_ఏప్రిల్_మే_జూన్_జూలై_ఆగస్టు_సెప్టెంబర్_అక్టోబర్_నవంబర్_డిసెంబర్'.split('_'),
		monthsShort : 'జన._ఫిబ్ర._మార్చి_ఏప్రి._మే_జూన్_జూలై_ఆగ._సెప్._అక్టో._నవ._డిసె.'.split('_'),
		monthsParseExact : true,
		weekdays : 'ఆదివారం_సోమవారం_మంగళవారం_బుధవారం_గురువారం_శుక్రవారం_శనివారం'.split('_'),
		weekdaysShort : 'ఆది_సోమ_మంగళ_బుధ_గురు_శుక్ర_శని'.split('_'),
		weekdaysMin : 'ఆ_సో_మం_బు_గు_శు_శ'.split('_'),
		longDateFormat : {
			LT : 'A h:mm',
			LTS : 'A h:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY, A h:mm',
			LLLL : 'dddd, D MMMM YYYY, A h:mm'
		},
		calendar : {
			sameDay : '[నేడు] LT',
			nextDay : '[రేపు] LT',
			nextWeek : 'dddd, LT',
			lastDay : '[నిన్న] LT',
			lastWeek : '[గత] dddd, LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : '%s లో',
			past : '%s క్రితం',
			s : 'కొన్ని క్షణాలు',
			m : 'ఒక నిమిషం',
			mm : '%d నిమిషాలు',
			h : 'ఒక గంట',
			hh : '%d గంటలు',
			d : 'ఒక రోజు',
			dd : '%d రోజులు',
			M : 'ఒక నెల',
			MM : '%d నెలలు',
			y : 'ఒక సంవత్సరం',
			yy : '%d సంవత్సరాలు'
		},
		ordinalParse : /\d{1,2}వ/,
		ordinal : '%dవ',
		meridiemParse: /రాత్రి|ఉదయం|మధ్యాహ్నం|సాయంత్రం/,
		meridiemHour : function (hour, meridiem) {
			if (hour === 12) {
				hour = 0;
			}
			if (meridiem === 'రాత్రి') {
				return hour < 4 ? hour : hour + 12;
			} else if (meridiem === 'ఉదయం') {
				return hour;
			} else if (meridiem === 'మధ్యాహ్నం') {
				return hour >= 10 ? hour : hour + 12;
			} else if (meridiem === 'సాయంత్రం') {
				return hour + 12;
			}
		},
		meridiem : function (hour, minute, isLower) {
			if (hour < 4) {
				return 'రాత్రి';
			} else if (hour < 10) {
				return 'ఉదయం';
			} else if (hour < 17) {
				return 'మధ్యాహ్నం';
			} else if (hour < 20) {
				return 'సాయంత్రం';
			} else {
				return 'రాత్రి';
			}
		},
		week : {
			dow : 0, // Sunday is the first day of the week.
			doy : 6  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Tetun Dili (East Timor) [tet]
//! author : Joshua Brooks : https://github.com/joshbrooks
//! author : Onorio De J. Afonso : https://github.com/marobo

	hooks.defineLocale('tet', {
		months : 'Janeiru_Fevereiru_Marsu_Abril_Maiu_Juniu_Juliu_Augustu_Setembru_Outubru_Novembru_Dezembru'.split('_'),
		monthsShort : 'Jan_Fev_Mar_Abr_Mai_Jun_Jul_Aug_Set_Out_Nov_Dez'.split('_'),
		weekdays : 'Domingu_Segunda_Tersa_Kuarta_Kinta_Sexta_Sabadu'.split('_'),
		weekdaysShort : 'Dom_Seg_Ters_Kua_Kint_Sext_Sab'.split('_'),
		weekdaysMin : 'Do_Seg_Te_Ku_Ki_Sex_Sa'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd, D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay: '[Ohin iha] LT',
			nextDay: '[Aban iha] LT',
			nextWeek: 'dddd [iha] LT',
			lastDay: '[Horiseik iha] LT',
			lastWeek: 'dddd [semana kotuk] [iha] LT',
			sameElse: 'L'
		},
		relativeTime : {
			future : 'iha %s',
			past : '%s liuba',
			s : 'minutu balun',
			m : 'minutu ida',
			mm : 'minutus %d',
			h : 'horas ida',
			hh : 'horas %d',
			d : 'loron ida',
			dd : 'loron %d',
			M : 'fulan ida',
			MM : 'fulan %d',
			y : 'tinan ida',
			yy : 'tinan %d'
		},
		ordinalParse: /\d{1,2}(st|nd|rd|th)/,
		ordinal : function (number) {
			var b = number % 10,
				output = (~~(number % 100 / 10) === 1) ? 'th' :
					(b === 1) ? 'st' :
						(b === 2) ? 'nd' :
							(b === 3) ? 'rd' : 'th';
			return number + output;
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Thai [th]
//! author : Kridsada Thanabulpong : https://github.com/sirn

	hooks.defineLocale('th', {
		months : 'มกราคม_กุมภาพันธ์_มีนาคม_เมษายน_พฤษภาคม_มิถุนายน_กรกฎาคม_สิงหาคม_กันยายน_ตุลาคม_พฤศจิกายน_ธันวาคม'.split('_'),
		monthsShort : 'ม.ค._ก.พ._มี.ค._เม.ย._พ.ค._มิ.ย._ก.ค._ส.ค._ก.ย._ต.ค._พ.ย._ธ.ค.'.split('_'),
		monthsParseExact: true,
		weekdays : 'อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัสบดี_ศุกร์_เสาร์'.split('_'),
		weekdaysShort : 'อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัส_ศุกร์_เสาร์'.split('_'), // yes, three characters difference
		weekdaysMin : 'อา._จ._อ._พ._พฤ._ศ._ส.'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'H:mm',
			LTS : 'H:mm:ss',
			L : 'YYYY/MM/DD',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY เวลา H:mm',
			LLLL : 'วันddddที่ D MMMM YYYY เวลา H:mm'
		},
		meridiemParse: /ก่อนเที่ยง|หลังเที่ยง/,
		isPM: function (input) {
			return input === 'หลังเที่ยง';
		},
		meridiem : function (hour, minute, isLower) {
			if (hour < 12) {
				return 'ก่อนเที่ยง';
			} else {
				return 'หลังเที่ยง';
			}
		},
		calendar : {
			sameDay : '[วันนี้ เวลา] LT',
			nextDay : '[พรุ่งนี้ เวลา] LT',
			nextWeek : 'dddd[หน้า เวลา] LT',
			lastDay : '[เมื่อวานนี้ เวลา] LT',
			lastWeek : '[วัน]dddd[ที่แล้ว เวลา] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'อีก %s',
			past : '%sที่แล้ว',
			s : 'ไม่กี่วินาที',
			m : '1 นาที',
			mm : '%d นาที',
			h : '1 ชั่วโมง',
			hh : '%d ชั่วโมง',
			d : '1 วัน',
			dd : '%d วัน',
			M : '1 เดือน',
			MM : '%d เดือน',
			y : '1 ปี',
			yy : '%d ปี'
		}
	});

//! moment.js locale configuration
//! locale : Tagalog (Philippines) [tl-ph]
//! author : Dan Hagman : https://github.com/hagmandan

	hooks.defineLocale('tl-ph', {
		months : 'Enero_Pebrero_Marso_Abril_Mayo_Hunyo_Hulyo_Agosto_Setyembre_Oktubre_Nobyembre_Disyembre'.split('_'),
		monthsShort : 'Ene_Peb_Mar_Abr_May_Hun_Hul_Ago_Set_Okt_Nob_Dis'.split('_'),
		weekdays : 'Linggo_Lunes_Martes_Miyerkules_Huwebes_Biyernes_Sabado'.split('_'),
		weekdaysShort : 'Lin_Lun_Mar_Miy_Huw_Biy_Sab'.split('_'),
		weekdaysMin : 'Li_Lu_Ma_Mi_Hu_Bi_Sab'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'MM/D/YYYY',
			LL : 'MMMM D, YYYY',
			LLL : 'MMMM D, YYYY HH:mm',
			LLLL : 'dddd, MMMM DD, YYYY HH:mm'
		},
		calendar : {
			sameDay: 'LT [ngayong araw]',
			nextDay: '[Bukas ng] LT',
			nextWeek: 'LT [sa susunod na] dddd',
			lastDay: 'LT [kahapon]',
			lastWeek: 'LT [noong nakaraang] dddd',
			sameElse: 'L'
		},
		relativeTime : {
			future : 'sa loob ng %s',
			past : '%s ang nakalipas',
			s : 'ilang segundo',
			m : 'isang minuto',
			mm : '%d minuto',
			h : 'isang oras',
			hh : '%d oras',
			d : 'isang araw',
			dd : '%d araw',
			M : 'isang buwan',
			MM : '%d buwan',
			y : 'isang taon',
			yy : '%d taon'
		},
		ordinalParse: /\d{1,2}/,
		ordinal : function (number) {
			return number;
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Klingon [tlh]
//! author : Dominika Kruk : https://github.com/amaranthrose

	var numbersNouns = 'pagh_wa’_cha’_wej_loS_vagh_jav_Soch_chorgh_Hut'.split('_');

	function translateFuture(output) {
		var time = output;
		time = (output.indexOf('jaj') !== -1) ?
			time.slice(0, -3) + 'leS' :
			(output.indexOf('jar') !== -1) ?
				time.slice(0, -3) + 'waQ' :
				(output.indexOf('DIS') !== -1) ?
					time.slice(0, -3) + 'nem' :
					time + ' pIq';
		return time;
	}

	function translatePast(output) {
		var time = output;
		time = (output.indexOf('jaj') !== -1) ?
			time.slice(0, -3) + 'Hu’' :
			(output.indexOf('jar') !== -1) ?
				time.slice(0, -3) + 'wen' :
				(output.indexOf('DIS') !== -1) ?
					time.slice(0, -3) + 'ben' :
					time + ' ret';
		return time;
	}

	function translate$9(number, withoutSuffix, string, isFuture) {
		var numberNoun = numberAsNoun(number);
		switch (string) {
			case 'mm':
				return numberNoun + ' tup';
			case 'hh':
				return numberNoun + ' rep';
			case 'dd':
				return numberNoun + ' jaj';
			case 'MM':
				return numberNoun + ' jar';
			case 'yy':
				return numberNoun + ' DIS';
		}
	}

	function numberAsNoun(number) {
		var hundred = Math.floor((number % 1000) / 100),
			ten = Math.floor((number % 100) / 10),
			one = number % 10,
			word = '';
		if (hundred > 0) {
			word += numbersNouns[hundred] + 'vatlh';
		}
		if (ten > 0) {
			word += ((word !== '') ? ' ' : '') + numbersNouns[ten] + 'maH';
		}
		if (one > 0) {
			word += ((word !== '') ? ' ' : '') + numbersNouns[one];
		}
		return (word === '') ? 'pagh' : word;
	}

	hooks.defineLocale('tlh', {
		months : 'tera’ jar wa’_tera’ jar cha’_tera’ jar wej_tera’ jar loS_tera’ jar vagh_tera’ jar jav_tera’ jar Soch_tera’ jar chorgh_tera’ jar Hut_tera’ jar wa’maH_tera’ jar wa’maH wa’_tera’ jar wa’maH cha’'.split('_'),
		monthsShort : 'jar wa’_jar cha’_jar wej_jar loS_jar vagh_jar jav_jar Soch_jar chorgh_jar Hut_jar wa’maH_jar wa’maH wa’_jar wa’maH cha’'.split('_'),
		monthsParseExact : true,
		weekdays : 'lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj'.split('_'),
		weekdaysShort : 'lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj'.split('_'),
		weekdaysMin : 'lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd, D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay: '[DaHjaj] LT',
			nextDay: '[wa’leS] LT',
			nextWeek: 'LLL',
			lastDay: '[wa’Hu’] LT',
			lastWeek: 'LLL',
			sameElse: 'L'
		},
		relativeTime : {
			future : translateFuture,
			past : translatePast,
			s : 'puS lup',
			m : 'wa’ tup',
			mm : translate$9,
			h : 'wa’ rep',
			hh : translate$9,
			d : 'wa’ jaj',
			dd : translate$9,
			M : 'wa’ jar',
			MM : translate$9,
			y : 'wa’ DIS',
			yy : translate$9
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Turkish [tr]
//! authors : Erhan Gundogan : https://github.com/erhangundogan,
//!           Burak Yiğit Kaya: https://github.com/BYK

	var suffixes$3 = {
		1: '\'inci',
		5: '\'inci',
		8: '\'inci',
		70: '\'inci',
		80: '\'inci',
		2: '\'nci',
		7: '\'nci',
		20: '\'nci',
		50: '\'nci',
		3: '\'üncü',
		4: '\'üncü',
		100: '\'üncü',
		6: '\'ncı',
		9: '\'uncu',
		10: '\'uncu',
		30: '\'uncu',
		60: '\'ıncı',
		90: '\'ıncı'
	};

	hooks.defineLocale('tr', {
		months : 'Ocak_Şubat_Mart_Nisan_Mayıs_Haziran_Temmuz_Ağustos_Eylül_Ekim_Kasım_Aralık'.split('_'),
		monthsShort : 'Oca_Şub_Mar_Nis_May_Haz_Tem_Ağu_Eyl_Eki_Kas_Ara'.split('_'),
		weekdays : 'Pazar_Pazartesi_Salı_Çarşamba_Perşembe_Cuma_Cumartesi'.split('_'),
		weekdaysShort : 'Paz_Pts_Sal_Çar_Per_Cum_Cts'.split('_'),
		weekdaysMin : 'Pz_Pt_Sa_Ça_Pe_Cu_Ct'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd, D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay : '[bugün saat] LT',
			nextDay : '[yarın saat] LT',
			nextWeek : '[haftaya] dddd [saat] LT',
			lastDay : '[dün] LT',
			lastWeek : '[geçen hafta] dddd [saat] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : '%s sonra',
			past : '%s önce',
			s : 'birkaç saniye',
			m : 'bir dakika',
			mm : '%d dakika',
			h : 'bir saat',
			hh : '%d saat',
			d : 'bir gün',
			dd : '%d gün',
			M : 'bir ay',
			MM : '%d ay',
			y : 'bir yıl',
			yy : '%d yıl'
		},
		ordinalParse: /\d{1,2}'(inci|nci|üncü|ncı|uncu|ıncı)/,
		ordinal : function (number) {
			if (number === 0) {  // special case for zero
				return number + '\'ıncı';
			}
			var a = number % 10,
				b = number % 100 - a,
				c = number >= 100 ? 100 : null;
			return number + (suffixes$3[a] || suffixes$3[b] || suffixes$3[c]);
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Talossan [tzl]
//! author : Robin van der Vliet : https://github.com/robin0van0der0v
//! author : Iustì Canun

// After the year there should be a slash and the amount of years since December 26, 1979 in Roman numerals.
// This is currently too difficult (maybe even impossible) to add.
	hooks.defineLocale('tzl', {
		months : 'Januar_Fevraglh_Març_Avrïu_Mai_Gün_Julia_Guscht_Setemvar_Listopäts_Noemvar_Zecemvar'.split('_'),
		monthsShort : 'Jan_Fev_Mar_Avr_Mai_Gün_Jul_Gus_Set_Lis_Noe_Zec'.split('_'),
		weekdays : 'Súladi_Lúneçi_Maitzi_Márcuri_Xhúadi_Viénerçi_Sáturi'.split('_'),
		weekdaysShort : 'Súl_Lún_Mai_Már_Xhú_Vié_Sát'.split('_'),
		weekdaysMin : 'Sú_Lú_Ma_Má_Xh_Vi_Sá'.split('_'),
		longDateFormat : {
			LT : 'HH.mm',
			LTS : 'HH.mm.ss',
			L : 'DD.MM.YYYY',
			LL : 'D. MMMM [dallas] YYYY',
			LLL : 'D. MMMM [dallas] YYYY HH.mm',
			LLLL : 'dddd, [li] D. MMMM [dallas] YYYY HH.mm'
		},
		meridiemParse: /d\'o|d\'a/i,
		isPM : function (input) {
			return 'd\'o' === input.toLowerCase();
		},
		meridiem : function (hours, minutes, isLower) {
			if (hours > 11) {
				return isLower ? 'd\'o' : 'D\'O';
			} else {
				return isLower ? 'd\'a' : 'D\'A';
			}
		},
		calendar : {
			sameDay : '[oxhi à] LT',
			nextDay : '[demà à] LT',
			nextWeek : 'dddd [à] LT',
			lastDay : '[ieiri à] LT',
			lastWeek : '[sür el] dddd [lasteu à] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'osprei %s',
			past : 'ja%s',
			s : processRelativeTime$5,
			m : processRelativeTime$5,
			mm : processRelativeTime$5,
			h : processRelativeTime$5,
			hh : processRelativeTime$5,
			d : processRelativeTime$5,
			dd : processRelativeTime$5,
			M : processRelativeTime$5,
			MM : processRelativeTime$5,
			y : processRelativeTime$5,
			yy : processRelativeTime$5
		},
		ordinalParse: /\d{1,2}\./,
		ordinal : '%d.',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

	function processRelativeTime$5(number, withoutSuffix, key, isFuture) {
		var format = {
			's': ['viensas secunds', '\'iensas secunds'],
			'm': ['\'n míut', '\'iens míut'],
			'mm': [number + ' míuts', '' + number + ' míuts'],
			'h': ['\'n þora', '\'iensa þora'],
			'hh': [number + ' þoras', '' + number + ' þoras'],
			'd': ['\'n ziua', '\'iensa ziua'],
			'dd': [number + ' ziuas', '' + number + ' ziuas'],
			'M': ['\'n mes', '\'iens mes'],
			'MM': [number + ' mesen', '' + number + ' mesen'],
			'y': ['\'n ar', '\'iens ar'],
			'yy': [number + ' ars', '' + number + ' ars']
		};
		return isFuture ? format[key][0] : (withoutSuffix ? format[key][0] : format[key][1]);
	}

//! moment.js locale configuration
//! locale : Central Atlas Tamazight Latin [tzm-latn]
//! author : Abdel Said : https://github.com/abdelsaid

	hooks.defineLocale('tzm-latn', {
		months : 'innayr_brˤayrˤ_marˤsˤ_ibrir_mayyw_ywnyw_ywlywz_ɣwšt_šwtanbir_ktˤwbrˤ_nwwanbir_dwjnbir'.split('_'),
		monthsShort : 'innayr_brˤayrˤ_marˤsˤ_ibrir_mayyw_ywnyw_ywlywz_ɣwšt_šwtanbir_ktˤwbrˤ_nwwanbir_dwjnbir'.split('_'),
		weekdays : 'asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas'.split('_'),
		weekdaysShort : 'asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas'.split('_'),
		weekdaysMin : 'asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay: '[asdkh g] LT',
			nextDay: '[aska g] LT',
			nextWeek: 'dddd [g] LT',
			lastDay: '[assant g] LT',
			lastWeek: 'dddd [g] LT',
			sameElse: 'L'
		},
		relativeTime : {
			future : 'dadkh s yan %s',
			past : 'yan %s',
			s : 'imik',
			m : 'minuḍ',
			mm : '%d minuḍ',
			h : 'saɛa',
			hh : '%d tassaɛin',
			d : 'ass',
			dd : '%d ossan',
			M : 'ayowr',
			MM : '%d iyyirn',
			y : 'asgas',
			yy : '%d isgasn'
		},
		week : {
			dow : 6, // Saturday is the first day of the week.
			doy : 12  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Central Atlas Tamazight [tzm]
//! author : Abdel Said : https://github.com/abdelsaid

	hooks.defineLocale('tzm', {
		months : 'ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ'.split('_'),
		monthsShort : 'ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ'.split('_'),
		weekdays : 'ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ'.split('_'),
		weekdaysShort : 'ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ'.split('_'),
		weekdaysMin : 'ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS: 'HH:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay: '[ⴰⵙⴷⵅ ⴴ] LT',
			nextDay: '[ⴰⵙⴽⴰ ⴴ] LT',
			nextWeek: 'dddd [ⴴ] LT',
			lastDay: '[ⴰⵚⴰⵏⵜ ⴴ] LT',
			lastWeek: 'dddd [ⴴ] LT',
			sameElse: 'L'
		},
		relativeTime : {
			future : 'ⴷⴰⴷⵅ ⵙ ⵢⴰⵏ %s',
			past : 'ⵢⴰⵏ %s',
			s : 'ⵉⵎⵉⴽ',
			m : 'ⵎⵉⵏⵓⴺ',
			mm : '%d ⵎⵉⵏⵓⴺ',
			h : 'ⵙⴰⵄⴰ',
			hh : '%d ⵜⴰⵙⵙⴰⵄⵉⵏ',
			d : 'ⴰⵙⵙ',
			dd : '%d oⵙⵙⴰⵏ',
			M : 'ⴰⵢoⵓⵔ',
			MM : '%d ⵉⵢⵢⵉⵔⵏ',
			y : 'ⴰⵙⴳⴰⵙ',
			yy : '%d ⵉⵙⴳⴰⵙⵏ'
		},
		week : {
			dow : 6, // Saturday is the first day of the week.
			doy : 12  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Ukrainian [uk]
//! author : zemlanin : https://github.com/zemlanin
//! Author : Menelion Elensúle : https://github.com/Oire

	function plural$6(word, num) {
		var forms = word.split('_');
		return num % 10 === 1 && num % 100 !== 11 ? forms[0] : (num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20) ? forms[1] : forms[2]);
	}
	function relativeTimeWithPlural$4(number, withoutSuffix, key) {
		var format = {
			'mm': withoutSuffix ? 'хвилина_хвилини_хвилин' : 'хвилину_хвилини_хвилин',
			'hh': withoutSuffix ? 'година_години_годин' : 'годину_години_годин',
			'dd': 'день_дні_днів',
			'MM': 'місяць_місяці_місяців',
			'yy': 'рік_роки_років'
		};
		if (key === 'm') {
			return withoutSuffix ? 'хвилина' : 'хвилину';
		}
		else if (key === 'h') {
			return withoutSuffix ? 'година' : 'годину';
		}
		else {
			return number + ' ' + plural$6(format[key], +number);
		}
	}
	function weekdaysCaseReplace(m, format) {
		var weekdays = {
				'nominative': 'неділя_понеділок_вівторок_середа_четвер_п’ятниця_субота'.split('_'),
				'accusative': 'неділю_понеділок_вівторок_середу_четвер_п’ятницю_суботу'.split('_'),
				'genitive': 'неділі_понеділка_вівторка_середи_четверга_п’ятниці_суботи'.split('_')
			},
			nounCase = (/(\[[ВвУу]\]) ?dddd/).test(format) ?
				'accusative' :
				((/\[?(?:минулої|наступної)? ?\] ?dddd/).test(format) ?
					'genitive' :
					'nominative');
		return weekdays[nounCase][m.day()];
	}
	function processHoursFunction(str) {
		return function () {
			return str + 'о' + (this.hours() === 11 ? 'б' : '') + '] LT';
		};
	}

	hooks.defineLocale('uk', {
		months : {
			'format': 'січня_лютого_березня_квітня_травня_червня_липня_серпня_вересня_жовтня_листопада_грудня'.split('_'),
			'standalone': 'січень_лютий_березень_квітень_травень_червень_липень_серпень_вересень_жовтень_листопад_грудень'.split('_')
		},
		monthsShort : 'січ_лют_бер_квіт_трав_черв_лип_серп_вер_жовт_лист_груд'.split('_'),
		weekdays : weekdaysCaseReplace,
		weekdaysShort : 'нд_пн_вт_ср_чт_пт_сб'.split('_'),
		weekdaysMin : 'нд_пн_вт_ср_чт_пт_сб'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD.MM.YYYY',
			LL : 'D MMMM YYYY р.',
			LLL : 'D MMMM YYYY р., HH:mm',
			LLLL : 'dddd, D MMMM YYYY р., HH:mm'
		},
		calendar : {
			sameDay: processHoursFunction('[Сьогодні '),
			nextDay: processHoursFunction('[Завтра '),
			lastDay: processHoursFunction('[Вчора '),
			nextWeek: processHoursFunction('[У] dddd ['),
			lastWeek: function () {
				switch (this.day()) {
					case 0:
					case 3:
					case 5:
					case 6:
						return processHoursFunction('[Минулої] dddd [').call(this);
					case 1:
					case 2:
					case 4:
						return processHoursFunction('[Минулого] dddd [').call(this);
				}
			},
			sameElse: 'L'
		},
		relativeTime : {
			future : 'за %s',
			past : '%s тому',
			s : 'декілька секунд',
			m : relativeTimeWithPlural$4,
			mm : relativeTimeWithPlural$4,
			h : 'годину',
			hh : relativeTimeWithPlural$4,
			d : 'день',
			dd : relativeTimeWithPlural$4,
			M : 'місяць',
			MM : relativeTimeWithPlural$4,
			y : 'рік',
			yy : relativeTimeWithPlural$4
		},
		// M. E.: those two are virtually unused but a user might want to implement them for his/her website for some reason
		meridiemParse: /ночі|ранку|дня|вечора/,
		isPM: function (input) {
			return /^(дня|вечора)$/.test(input);
		},
		meridiem : function (hour, minute, isLower) {
			if (hour < 4) {
				return 'ночі';
			} else if (hour < 12) {
				return 'ранку';
			} else if (hour < 17) {
				return 'дня';
			} else {
				return 'вечора';
			}
		},
		ordinalParse: /\d{1,2}-(й|го)/,
		ordinal: function (number, period) {
			switch (period) {
				case 'M':
				case 'd':
				case 'DDD':
				case 'w':
				case 'W':
					return number + '-й';
				case 'D':
					return number + '-го';
				default:
					return number;
			}
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 1st is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Uzbek [uz]
//! author : Sardor Muminov : https://github.com/muminoff

	hooks.defineLocale('uz', {
		months : 'январ_феврал_март_апрел_май_июн_июл_август_сентябр_октябр_ноябр_декабр'.split('_'),
		monthsShort : 'янв_фев_мар_апр_май_июн_июл_авг_сен_окт_ноя_дек'.split('_'),
		weekdays : 'Якшанба_Душанба_Сешанба_Чоршанба_Пайшанба_Жума_Шанба'.split('_'),
		weekdaysShort : 'Якш_Душ_Сеш_Чор_Пай_Жум_Шан'.split('_'),
		weekdaysMin : 'Як_Ду_Се_Чо_Па_Жу_Ша'.split('_'),
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'D MMMM YYYY, dddd HH:mm'
		},
		calendar : {
			sameDay : '[Бугун соат] LT [да]',
			nextDay : '[Эртага] LT [да]',
			nextWeek : 'dddd [куни соат] LT [да]',
			lastDay : '[Кеча соат] LT [да]',
			lastWeek : '[Утган] dddd [куни соат] LT [да]',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'Якин %s ичида',
			past : 'Бир неча %s олдин',
			s : 'фурсат',
			m : 'бир дакика',
			mm : '%d дакика',
			h : 'бир соат',
			hh : '%d соат',
			d : 'бир кун',
			dd : '%d кун',
			M : 'бир ой',
			MM : '%d ой',
			y : 'бир йил',
			yy : '%d йил'
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 7  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Vietnamese [vi]
//! author : Bang Nguyen : https://github.com/bangnk

	hooks.defineLocale('vi', {
		months : 'tháng 1_tháng 2_tháng 3_tháng 4_tháng 5_tháng 6_tháng 7_tháng 8_tháng 9_tháng 10_tháng 11_tháng 12'.split('_'),
		monthsShort : 'Th01_Th02_Th03_Th04_Th05_Th06_Th07_Th08_Th09_Th10_Th11_Th12'.split('_'),
		monthsParseExact : true,
		weekdays : 'chủ nhật_thứ hai_thứ ba_thứ tư_thứ năm_thứ sáu_thứ bảy'.split('_'),
		weekdaysShort : 'CN_T2_T3_T4_T5_T6_T7'.split('_'),
		weekdaysMin : 'CN_T2_T3_T4_T5_T6_T7'.split('_'),
		weekdaysParseExact : true,
		meridiemParse: /sa|ch/i,
		isPM : function (input) {
			return /^ch$/i.test(input);
		},
		meridiem : function (hours, minutes, isLower) {
			if (hours < 12) {
				return isLower ? 'sa' : 'SA';
			} else {
				return isLower ? 'ch' : 'CH';
			}
		},
		longDateFormat : {
			LT : 'HH:mm',
			LTS : 'HH:mm:ss',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM [năm] YYYY',
			LLL : 'D MMMM [năm] YYYY HH:mm',
			LLLL : 'dddd, D MMMM [năm] YYYY HH:mm',
			l : 'DD/M/YYYY',
			ll : 'D MMM YYYY',
			lll : 'D MMM YYYY HH:mm',
			llll : 'ddd, D MMM YYYY HH:mm'
		},
		calendar : {
			sameDay: '[Hôm nay lúc] LT',
			nextDay: '[Ngày mai lúc] LT',
			nextWeek: 'dddd [tuần tới lúc] LT',
			lastDay: '[Hôm qua lúc] LT',
			lastWeek: 'dddd [tuần rồi lúc] LT',
			sameElse: 'L'
		},
		relativeTime : {
			future : '%s tới',
			past : '%s trước',
			s : 'vài giây',
			m : 'một phút',
			mm : '%d phút',
			h : 'một giờ',
			hh : '%d giờ',
			d : 'một ngày',
			dd : '%d ngày',
			M : 'một tháng',
			MM : '%d tháng',
			y : 'một năm',
			yy : '%d năm'
		},
		ordinalParse: /\d{1,2}/,
		ordinal : function (number) {
			return number;
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Pseudo [x-pseudo]
//! author : Andrew Hood : https://github.com/andrewhood125

	hooks.defineLocale('x-pseudo', {
		months : 'J~áñúá~rý_F~ébrú~árý_~Márc~h_Áp~ríl_~Máý_~Júñé~_Júl~ý_Áú~gúst~_Sép~témb~ér_Ó~ctób~ér_Ñ~óvém~bér_~Décé~mbér'.split('_'),
		monthsShort : 'J~áñ_~Féb_~Már_~Ápr_~Máý_~Júñ_~Júl_~Áúg_~Sép_~Óct_~Ñóv_~Déc'.split('_'),
		monthsParseExact : true,
		weekdays : 'S~úñdá~ý_Mó~ñdáý~_Túé~sdáý~_Wéd~ñésd~áý_T~húrs~dáý_~Fríd~áý_S~átúr~dáý'.split('_'),
		weekdaysShort : 'S~úñ_~Móñ_~Túé_~Wéd_~Thú_~Frí_~Sát'.split('_'),
		weekdaysMin : 'S~ú_Mó~_Tú_~Wé_T~h_Fr~_Sá'.split('_'),
		weekdaysParseExact : true,
		longDateFormat : {
			LT : 'HH:mm',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY HH:mm',
			LLLL : 'dddd, D MMMM YYYY HH:mm'
		},
		calendar : {
			sameDay : '[T~ódá~ý át] LT',
			nextDay : '[T~ómó~rró~w át] LT',
			nextWeek : 'dddd [át] LT',
			lastDay : '[Ý~ést~érdá~ý át] LT',
			lastWeek : '[L~ást] dddd [át] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'í~ñ %s',
			past : '%s á~gó',
			s : 'á ~féw ~sécó~ñds',
			m : 'á ~míñ~úté',
			mm : '%d m~íñú~tés',
			h : 'á~ñ hó~úr',
			hh : '%d h~óúrs',
			d : 'á ~dáý',
			dd : '%d d~áýs',
			M : 'á ~móñ~th',
			MM : '%d m~óñt~hs',
			y : 'á ~ýéár',
			yy : '%d ý~éárs'
		},
		ordinalParse: /\d{1,2}(th|st|nd|rd)/,
		ordinal : function (number) {
			var b = number % 10,
				output = (~~(number % 100 / 10) === 1) ? 'th' :
					(b === 1) ? 'st' :
						(b === 2) ? 'nd' :
							(b === 3) ? 'rd' : 'th';
			return number + output;
		},
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Yoruba Nigeria [yo]
//! author : Atolagbe Abisoye : https://github.com/andela-batolagbe

	hooks.defineLocale('yo', {
		months : 'Sẹ́rẹ́_Èrèlè_Ẹrẹ̀nà_Ìgbé_Èbibi_Òkùdu_Agẹmo_Ògún_Owewe_Ọ̀wàrà_Bélú_Ọ̀pẹ̀̀'.split('_'),
		monthsShort : 'Sẹ́r_Èrl_Ẹrn_Ìgb_Èbi_Òkù_Agẹ_Ògú_Owe_Ọ̀wà_Bél_Ọ̀pẹ̀̀'.split('_'),
		weekdays : 'Àìkú_Ajé_Ìsẹ́gun_Ọjọ́rú_Ọjọ́bọ_Ẹtì_Àbámẹ́ta'.split('_'),
		weekdaysShort : 'Àìk_Ajé_Ìsẹ́_Ọjr_Ọjb_Ẹtì_Àbá'.split('_'),
		weekdaysMin : 'Àì_Aj_Ìs_Ọr_Ọb_Ẹt_Àb'.split('_'),
		longDateFormat : {
			LT : 'h:mm A',
			LTS : 'h:mm:ss A',
			L : 'DD/MM/YYYY',
			LL : 'D MMMM YYYY',
			LLL : 'D MMMM YYYY h:mm A',
			LLLL : 'dddd, D MMMM YYYY h:mm A'
		},
		calendar : {
			sameDay : '[Ònì ni] LT',
			nextDay : '[Ọ̀la ni] LT',
			nextWeek : 'dddd [Ọsẹ̀ tón\'bọ] [ni] LT',
			lastDay : '[Àna ni] LT',
			lastWeek : 'dddd [Ọsẹ̀ tólọ́] [ni] LT',
			sameElse : 'L'
		},
		relativeTime : {
			future : 'ní %s',
			past : '%s kọjá',
			s : 'ìsẹjú aayá die',
			m : 'ìsẹjú kan',
			mm : 'ìsẹjú %d',
			h : 'wákati kan',
			hh : 'wákati %d',
			d : 'ọjọ́ kan',
			dd : 'ọjọ́ %d',
			M : 'osù kan',
			MM : 'osù %d',
			y : 'ọdún kan',
			yy : 'ọdún %d'
		},
		ordinalParse : /ọjọ́\s\d{1,2}/,
		ordinal : 'ọjọ́ %d',
		week : {
			dow : 1, // Monday is the first day of the week.
			doy : 4 // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Chinese (China) [zh-cn]
//! author : suupic : https://github.com/suupic
//! author : Zeno Zeng : https://github.com/zenozeng

	hooks.defineLocale('zh-cn', {
		months : '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'),
		monthsShort : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'),
		weekdays : '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'),
		weekdaysShort : '周日_周一_周二_周三_周四_周五_周六'.split('_'),
		weekdaysMin : '日_一_二_三_四_五_六'.split('_'),
		longDateFormat : {
			LT : 'Ah点mm分',
			LTS : 'Ah点m分s秒',
			L : 'YYYY-MM-DD',
			LL : 'YYYY年MMMD日',
			LLL : 'YYYY年MMMD日Ah点mm分',
			LLLL : 'YYYY年MMMD日ddddAh点mm分',
			l : 'YYYY-MM-DD',
			ll : 'YYYY年MMMD日',
			lll : 'YYYY年MMMD日Ah点mm分',
			llll : 'YYYY年MMMD日ddddAh点mm分'
		},
		meridiemParse: /凌晨|早上|上午|中午|下午|晚上/,
		meridiemHour: function (hour, meridiem) {
			if (hour === 12) {
				hour = 0;
			}
			if (meridiem === '凌晨' || meridiem === '早上' ||
				meridiem === '上午') {
				return hour;
			} else if (meridiem === '下午' || meridiem === '晚上') {
				return hour + 12;
			} else {
				// '中午'
				return hour >= 11 ? hour : hour + 12;
			}
		},
		meridiem : function (hour, minute, isLower) {
			var hm = hour * 100 + minute;
			if (hm < 600) {
				return '凌晨';
			} else if (hm < 900) {
				return '早上';
			} else if (hm < 1130) {
				return '上午';
			} else if (hm < 1230) {
				return '中午';
			} else if (hm < 1800) {
				return '下午';
			} else {
				return '晚上';
			}
		},
		calendar : {
			sameDay : function () {
				return this.minutes() === 0 ? '[今天]Ah[点整]' : '[今天]LT';
			},
			nextDay : function () {
				return this.minutes() === 0 ? '[明天]Ah[点整]' : '[明天]LT';
			},
			lastDay : function () {
				return this.minutes() === 0 ? '[昨天]Ah[点整]' : '[昨天]LT';
			},
			nextWeek : function () {
				var startOfWeek, prefix;
				startOfWeek = hooks().startOf('week');
				prefix = this.diff(startOfWeek, 'days') >= 7 ? '[下]' : '[本]';
				return this.minutes() === 0 ? prefix + 'dddAh点整' : prefix + 'dddAh点mm';
			},
			lastWeek : function () {
				var startOfWeek, prefix;
				startOfWeek = hooks().startOf('week');
				prefix = this.unix() < startOfWeek.unix()  ? '[上]' : '[本]';
				return this.minutes() === 0 ? prefix + 'dddAh点整' : prefix + 'dddAh点mm';
			},
			sameElse : 'LL'
		},
		ordinalParse: /\d{1,2}(日|月|周)/,
		ordinal : function (number, period) {
			switch (period) {
				case 'd':
				case 'D':
				case 'DDD':
					return number + '日';
				case 'M':
					return number + '月';
				case 'w':
				case 'W':
					return number + '周';
				default:
					return number;
			}
		},
		relativeTime : {
			future : '%s内',
			past : '%s前',
			s : '几秒',
			m : '1 分钟',
			mm : '%d 分钟',
			h : '1 小时',
			hh : '%d 小时',
			d : '1 天',
			dd : '%d 天',
			M : '1 个月',
			MM : '%d 个月',
			y : '1 年',
			yy : '%d 年'
		},
		week : {
			// GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效
			dow : 1, // Monday is the first day of the week.
			doy : 4  // The week that contains Jan 4th is the first week of the year.
		}
	});

//! moment.js locale configuration
//! locale : Chinese (Hong Kong) [zh-hk]
//! author : Ben : https://github.com/ben-lin
//! author : Chris Lam : https://github.com/hehachris
//! author : Konstantin : https://github.com/skfd

	hooks.defineLocale('zh-hk', {
		months : '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'),
		monthsShort : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'),
		weekdays : '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'),
		weekdaysShort : '週日_週一_週二_週三_週四_週五_週六'.split('_'),
		weekdaysMin : '日_一_二_三_四_五_六'.split('_'),
		longDateFormat : {
			LT : 'Ah點mm分',
			LTS : 'Ah點m分s秒',
			L : 'YYYY年MMMD日',
			LL : 'YYYY年MMMD日',
			LLL : 'YYYY年MMMD日Ah點mm分',
			LLLL : 'YYYY年MMMD日ddddAh點mm分',
			l : 'YYYY年MMMD日',
			ll : 'YYYY年MMMD日',
			lll : 'YYYY年MMMD日Ah點mm分',
			llll : 'YYYY年MMMD日ddddAh點mm分'
		},
		meridiemParse: /凌晨|早上|上午|中午|下午|晚上/,
		meridiemHour : function (hour, meridiem) {
			if (hour === 12) {
				hour = 0;
			}
			if (meridiem === '凌晨' || meridiem === '早上' || meridiem === '上午') {
				return hour;
			} else if (meridiem === '中午') {
				return hour >= 11 ? hour : hour + 12;
			} else if (meridiem === '下午' || meridiem === '晚上') {
				return hour + 12;
			}
		},
		meridiem : function (hour, minute, isLower) {
			var hm = hour * 100 + minute;
			if (hm < 600) {
				return '凌晨';
			} else if (hm < 900) {
				return '早上';
			} else if (hm < 1130) {
				return '上午';
			} else if (hm < 1230) {
				return '中午';
			} else if (hm < 1800) {
				return '下午';
			} else {
				return '晚上';
			}
		},
		calendar : {
			sameDay : '[今天]LT',
			nextDay : '[明天]LT',
			nextWeek : '[下]ddddLT',
			lastDay : '[昨天]LT',
			lastWeek : '[上]ddddLT',
			sameElse : 'L'
		},
		ordinalParse: /\d{1,2}(日|月|週)/,
		ordinal : function (number, period) {
			switch (period) {
				case 'd' :
				case 'D' :
				case 'DDD' :
					return number + '日';
				case 'M' :
					return number + '月';
				case 'w' :
				case 'W' :
					return number + '週';
				default :
					return number;
			}
		},
		relativeTime : {
			future : '%s內',
			past : '%s前',
			s : '幾秒',
			m : '1 分鐘',
			mm : '%d 分鐘',
			h : '1 小時',
			hh : '%d 小時',
			d : '1 天',
			dd : '%d 天',
			M : '1 個月',
			MM : '%d 個月',
			y : '1 年',
			yy : '%d 年'
		}
	});

//! moment.js locale configuration
//! locale : Chinese (Taiwan) [zh-tw]
//! author : Ben : https://github.com/ben-lin
//! author : Chris Lam : https://github.com/hehachris

	hooks.defineLocale('zh-tw', {
		months : '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'),
		monthsShort : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'),
		weekdays : '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'),
		weekdaysShort : '週日_週一_週二_週三_週四_週五_週六'.split('_'),
		weekdaysMin : '日_一_二_三_四_五_六'.split('_'),
		longDateFormat : {
			LT : 'Ah點mm分',
			LTS : 'Ah點m分s秒',
			L : 'YYYY年MMMD日',
			LL : 'YYYY年MMMD日',
			LLL : 'YYYY年MMMD日Ah點mm分',
			LLLL : 'YYYY年MMMD日ddddAh點mm分',
			l : 'YYYY年MMMD日',
			ll : 'YYYY年MMMD日',
			lll : 'YYYY年MMMD日Ah點mm分',
			llll : 'YYYY年MMMD日ddddAh點mm分'
		},
		meridiemParse: /凌晨|早上|上午|中午|下午|晚上/,
		meridiemHour : function (hour, meridiem) {
			if (hour === 12) {
				hour = 0;
			}
			if (meridiem === '凌晨' || meridiem === '早上' || meridiem === '上午') {
				return hour;
			} else if (meridiem === '中午') {
				return hour >= 11 ? hour : hour + 12;
			} else if (meridiem === '下午' || meridiem === '晚上') {
				return hour + 12;
			}
		},
		meridiem : function (hour, minute, isLower) {
			var hm = hour * 100 + minute;
			if (hm < 600) {
				return '凌晨';
			} else if (hm < 900) {
				return '早上';
			} else if (hm < 1130) {
				return '上午';
			} else if (hm < 1230) {
				return '中午';
			} else if (hm < 1800) {
				return '下午';
			} else {
				return '晚上';
			}
		},
		calendar : {
			sameDay : '[今天]LT',
			nextDay : '[明天]LT',
			nextWeek : '[下]ddddLT',
			lastDay : '[昨天]LT',
			lastWeek : '[上]ddddLT',
			sameElse : 'L'
		},
		ordinalParse: /\d{1,2}(日|月|週)/,
		ordinal : function (number, period) {
			switch (period) {
				case 'd' :
				case 'D' :
				case 'DDD' :
					return number + '日';
				case 'M' :
					return number + '月';
				case 'w' :
				case 'W' :
					return number + '週';
				default :
					return number;
			}
		},
		relativeTime : {
			future : '%s內',
			past : '%s前',
			s : '幾秒',
			m : '1 分鐘',
			mm : '%d 分鐘',
			h : '1 小時',
			hh : '%d 小時',
			d : '1 天',
			dd : '%d 天',
			M : '1 個月',
			MM : '%d 個月',
			y : '1 年',
			yy : '%d 年'
		}
	});

	hooks.locale('en');

	return hooks;

})));
};


modules['helpcenter/primary/plugins/froala/align'] = function(require, exports, module, modules) {
/*!
 * froala_editor v2.3.4 (https://www.froala.com/wysiwyg-editor)
 * License https://froala.com/wysiwyg-editor/terms/
 * Copyright 2014-2016 Froala Labs
 */

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof module === 'object' && module.exports) {
        // Node/CommonJS
        module.exports = function( root, jQuery ) {
            if ( jQuery === undefined ) {
                // require('jQuery') returns a factory that requires window to
                // build a jQuery instance, we normalize how we use modules
                // that require this pattern but the window provided is a noop
                // if it's defined (how jquery works)
                if ( typeof window !== 'undefined' ) {
                    jQuery = require('jquery');
                }
                else {
                    jQuery = require('jquery')(root);
                }
            }
            factory(jQuery);
            return jQuery;
        };
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {

  'use strict';

  $.FE.PLUGINS.align = function (editor) {
    function apply (val) {
      // Wrap.
      editor.selection.save();
      editor.html.wrap(true, true, true, true);
      editor.selection.restore();

      var blocks = editor.selection.blocks();

      for (var i = 0; i < blocks.length; i++) {
        $(blocks[i]).css('text-align', val).removeClass('fr-temp-div');
        if ($(blocks[i]).attr('class') === '') $(blocks[i]).removeAttr('class');
      }

      editor.selection.save();
      editor.html.unwrap();
      editor.selection.restore();
    }

    function refresh ($btn) {
      var blocks = editor.selection.blocks();
      if (blocks.length) {
        var alignment = editor.helpers.getAlignment($(blocks[0]));

        $btn.find('> *:first').replaceWith(editor.icon.create('align-' + alignment));
      }
    }

    function refreshOnShow($btn, $dropdown) {
      var blocks = editor.selection.blocks();
      if (blocks.length) {
        var alignment = editor.helpers.getAlignment($(blocks[0]));

        $dropdown.find('a.fr-command[data-param1="' + alignment + '"]').addClass('fr-active');
      }
    }

    return {
      apply: apply,
      refresh: refresh,
      refreshOnShow: refreshOnShow
    }
  }

  $.FE.DefineIcon('align', { NAME: 'align-left' });
  $.FE.DefineIcon('align-left', { NAME: 'align-left' });
  $.FE.DefineIcon('align-right', { NAME: 'align-right' });
  $.FE.DefineIcon('align-center', { NAME: 'align-center' });
  $.FE.DefineIcon('align-justify', { NAME: 'align-justify' });
  $.FE.RegisterCommand('align', {
    type: 'dropdown',
    title: 'Align',
    options: {
      left: 'Align Left',
      center: 'Align Center',
      right: 'Align Right',
      justify: 'Align Justify'
    },
    html: function () {
      var c = '<ul class="fr-dropdown-list">';
      var options =  $.FE.COMMANDS.align.options;
      for (var val in options) {
        if (options.hasOwnProperty(val)) {
          c += '<li><a class="fr-command fr-title" data-cmd="align" data-param1="' + val + '" title="' + this.language.translate(options[val]) + '">' + this.icon.create('align-' + val) + '</a></li>';
        }
      }
      c += '</ul>';

      return c;
    },
    callback: function (cmd, val) {
      this.align.apply(val);
    },
    refresh: function ($btn) {
      this.align.refresh($btn);
    },
    refreshOnShow: function ($btn, $dropdown) {
      this.align.refreshOnShow($btn, $dropdown);
    },
    plugin: 'align'
  })

}));

};


modules['helpcenter/primary/views/likes'] = function(require, exports, module, modules) {
var likesModel   = require('helpcenter/primary/models/like');
var commentModel = require('helpcenter/primary/models/retrieve-comment');

module.exports = Backbone.View.extend({
    el: $('.js-like-view'),

    events: {
        'click .js-like': 'like',
        'click .js-unlike': 'unlike'
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Gets options from router while instantiating view
     * @param  {Object}
     * @return {Object}
     */
    initialize: function (options) {
        this.options = options || {};
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Likes a comment and re-render likes counter
     * @param  event
     */
    like: function(e) {
        var elem = $(e.target);
        var articleId = this.options.articleId.split('-')[0];
        var commentId = $(e.target).closest('.js-comment').attr('data-comment-id');
        var unlikeTemplate = _.template($('#tpl-unlike').html());

        elem.addClass('is-disabled');
        new likesModel({ 'articleId': articleId, 'commentId': commentId }).save({}, {
            success: function() {
                new commentModel({ 'articleId': articleId, 'commentId': commentId }).fetch({
                    success: function(response) {
                        elem.closest('.js-like-view')
                        .html(unlikeTemplate({ likeClass: 'js-unlike', likesCount: response.attributes.data.like_count }));
                    }
                });
            }
        });
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Unlikes a comment and re-render likes counter
     * @param  event
     */
    unlike: function(e) {
        var elem = $(e.target);
        var articleId = this.options.articleId.split('-')[0];
        var commentId = $(e.target).closest('.js-comment').attr('data-comment-id');
        var likeTemplate = _.template($('#tpl-like').html());

        elem.addClass('is-disabled');
        new likesModel({ 'articleId': articleId, 'commentId': commentId, 'unlike': true }).destroy({
            success: function() {
                new commentModel({ 'articleId': articleId, 'commentId': commentId }).fetch({
                    success: function(response) {
                        elem.closest('.js-like-view')
                        .html(likeTemplate({ likeClass: 'js-like', likesCount: response.attributes.data.like_count }));
                    }
                });
            }
        });
    }
});
};


modules['helpcenter/primary/plugins/tags'] = function(require, exports, module, modules) {
var jQuery = require('jquery');
/**
 * jQuery TextExt Plugin
 * http://textextjs.com
 *
 * @version 1.3.1
 * @copyright Copyright (C) 2011 Alex Gorbatchev. All rights reserved.
 * @license MIT License
 */
(function($)
{
	/**
	 * Tags plugin brings in the traditional tag functionality where user can assemble and
	 * edit list of tags. Tags plugin works especially well together with Autocomplete, Filter,
	 * Suggestions and Ajax plugins to provide full spectrum of features. It can also work on
	 * its own and just do one thing -- tags.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExtTags
	 */
	function TextExtTags() {};

	$.fn.textext.TextExtTags = TextExtTags;
	$.fn.textext.addPlugin('tags', TextExtTags);

	var p = TextExtTags.prototype,

		CSS_DOT             = '.',
		CSS_TAGS_ON_TOP     = 'text-tags-on-top',
		CSS_DOT_TAGS_ON_TOP = CSS_DOT + CSS_TAGS_ON_TOP,
		CSS_TAG             = 'text-tag',
		CSS_DOT_TAG         = CSS_DOT + CSS_TAG,
		CSS_TAGS            = 'text-tags',
		CSS_DOT_TAGS        = CSS_DOT + CSS_TAGS,
		CSS_LABEL           = 'text-label',
		CSS_DOT_LABEL       = CSS_DOT + CSS_LABEL,
		CSS_REMOVE          = 'text-remove',
		CSS_DOT_REMOVE      = CSS_DOT + CSS_REMOVE,

		/**
		 * Tags plugin options are grouped under `tags` when passed to the
		 * `$().textext()` function. For example:
		 *
		 *     $('textarea').textext({
		 *         plugins: 'tags',
		 *         tags: {
		 *             items: [ "tag1", "tag2" ]
		 *         }
		 *     })
		 *
		 * @author agorbatchev
		 * @date 2011/08/19
		 * @id TextExtTags.options
		 */

		/**
		 * This is a toggle switch to enable or disable the Tags plugin. The value is checked
		 * each time at the top level which allows you to toggle this setting on the fly.
		 *
		 * @name tags.enabled
		 * @default true
		 * @author agorbatchev
		 * @date 2011/08/19
		 * @id TextExtTags.options.tags.enabled
		 */
		OPT_ENABLED = 'tags.enabled',

		/**
		 * Allows to specify tags which will be added to the input by default upon initialization.
		 * Each item in the array must be of the type that current `ItemManager` can understand.
		 * Default type is `String`.
		 *
		 * @name tags.items
		 * @default null
		 * @author agorbatchev
		 * @date 2011/08/19
		 * @id TextExtTags.options.tags.items
		 */
		OPT_ITEMS = 'tags.items',

		/**
		 * HTML source that is used to generate a single tag.
		 *
		 * @name html.tag
		 * @default '<div class="text-tags"/>'
		 * @author agorbatchev
		 * @date 2011/08/19
		 * @id TextExtTags.options.html.tag
		 */
		OPT_HTML_TAG  = 'html.tag',

		/**
		 * HTML source that is used to generate container for the tags.
		 *
		 * @name html.tags
		 * @default '<div class="text-tag"><div class="text-button"><span class="text-label"/><a class="text-remove"/></div></div>'
		 * @author agorbatchev
		 * @date 2011/08/19
		 * @id TextExtTags.options.html.tags
		 */
		OPT_HTML_TAGS = 'html.tags',

		/**
		 * Tags plugin dispatches or reacts to the following events.
		 *
		 * @author agorbatchev
		 * @date 2011/08/17
		 * @id TextExtTags.events
		 */

		/**
		 * Tags plugin triggers the `isTagAllowed` event before adding each tag to the tag list. Other plugins have
		 * an opportunity to interrupt this by setting `result` of the second argument to `false`. For example:
		 *
		 *     $('textarea').textext({...}).bind('isTagAllowed', function(e, data)
		 *     {
		 *         if(data.tag === 'foo')
		 *             data.result = false;
		 *     })
		 *
		 * The second argument `data` has the following format: `{ tag : {Object}, result : {Boolean} }`. `tag`
		 * property is in the format that the current `ItemManager` can understand.
		 *
		 * @name isTagAllowed
		 * @author agorbatchev
		 * @date 2011/08/19
		 * @id TextExtTags.events.isTagAllowed
		 */
		EVENT_IS_TAG_ALLOWED = 'isTagAllowed',

		/**
		 * Tags plugin triggers the `tagClick` event when user clicks on one of the tags. This allows to process
		 * the click and potentially change the value of the tag (for example in case of user feedback).
		 *
		 *     $('textarea').textext({...}).bind('tagClick', function(e, tag, value, callback)
		 *     {
		 *         var newValue = window.prompt('New value', value);

		 *         if(newValue)
		 *             callback(newValue, true);
		 *     })
		 *
		 *  Callback argument has the following signature:
		 *
		 *     function(newValue, refocus)
		 *     {
		 *         ...
		 *     }
		 *
		 * Please check out [example](/manual/examples/tags-changing.html).
		 *
		 * @name tagClick
		 * @version 1.3.0
		 * @author s.stok
		 * @date 2011/01/23
		 * @id TextExtTags.events.tagClick
		 */
		EVENT_TAG_CLICK = 'tagClick',

		DEFAULT_OPTS = {
			tags : {
				enabled : true,
				items   : null
			},

			html : {
				tags : '<div class="text-tags"/>',
				tag  : '<div class="text-tag"><div class="text-button"><span class="text-label"/><i class="text-remove icon icon-alert--close"/></div></div>'
			}
		}
		;

	/**
	 * Initialization method called by the core during plugin instantiation.
	 *
	 * @signature TextExtTags.init(core)
	 *
	 * @param core {TextExt} Instance of the TextExt core class.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExtTags.init
	 */
	p.init = function(core)
	{
		this.baseInit(core, DEFAULT_OPTS);
		var self  = this,
			input = self.input(),
			container
			;

		if(self.opts(OPT_ENABLED))
		{
			container = $(self.opts(OPT_HTML_TAGS));
			input.after(container);

			$(self).data('container', container);

			self.on({
				enterKeyPress    : self.onEnterKeyPress,
				backspaceKeyDown : self.onBackspaceKeyDown,
				preInvalidate    : self.onPreInvalidate,
				postInit         : self.onPostInit,
				getFormData      : self.onGetFormData
			});

			self.on(container, {
				click     : self.onClick,
				mousemove : self.onContainerMouseMove
			});

			self.on(input, {
				mousemove : self.onInputMouseMove
			});
		}

		self._originalPadding = {
			left : parseInt(input.css('paddingLeft') || 0),
			top  : parseInt(input.css('paddingTop') || 0)
		};

		self._paddingBox = {
			left : 0,
			top  : 0
		};

		self.updateFormCache();
	};

	/**
	 * Returns HTML element in which all tag HTML elements are residing.
	 *
	 * @signature TextExtTags.containerElement()
	 *
	 * @author agorbatchev
	 * @date 2011/08/15
	 * @id TextExtTags.containerElement
	 */
	p.containerElement = function()
	{
		return $(this).data('container');
	};

	//--------------------------------------------------------------------------------
	// Event handlers

	/**
	 * Reacts to the `postInit` event triggered by the core and sets default tags
	 * if any were specified.
	 *
	 * @signature TextExtTags.onPostInit(e)
	 *
	 * @param e {Object} jQuery event.
	 *
	 * @author agorbatchev
	 * @date 2011/08/09
	 * @id TextExtTags.onPostInit
	 */
	p.onPostInit = function(e)
	{
		var self = this;
		self.addTags(self.opts(OPT_ITEMS));
	};

	/**
	 * Reacts to the [`getFormData`][1] event triggered by the core. Returns data with the
	 * weight of 200 to be *greater than the Autocomplete plugin* data weight. The weights
	 * system is covered in greater detail in the [`getFormData`][1] event documentation.
	 *
	 * [1]: /manual/textext.html#getformdata
	 *
	 * @signature TextExtTags.onGetFormData(e, data, keyCode)
	 *
	 * @param e {Object} jQuery event.
	 * @param data {Object} Data object to be populated.
	 * @param keyCode {Number} Key code that triggered the original update request.
	 *
	 * @author agorbatchev
	 * @date 2011/08/22
	 * @id TextExtTags.onGetFormData
	 */
	p.onGetFormData = function(e, data, keyCode)
	{
		var self       = this,
			inputValue = keyCode === 13 ? '' : self.val(),
			formValue  = self._formData
			;

		data[200] = self.formDataObject(inputValue, formValue);
	};

	/**
	 * Returns initialization priority of the Tags plugin which is expected to be
	 * *less than the Autocomplete plugin* because of the dependencies. The value is
	 * 100.
	 *
	 * @signature TextExtTags.initPriority()
	 *
	 * @author agorbatchev
	 * @date 2011/08/22
	 * @id TextExtTags.initPriority
	 */
	p.initPriority = function()
	{
		return 100;
	};

	/**
	 * Reacts to user moving mouse over the text area when cursor is over the text
	 * and not over the tags. Whenever mouse cursor is over the area covered by
	 * tags, the tags container is flipped to be on top of the text area which
	 * makes all tags functional with the mouse.
	 *
	 * @signature TextExtTags.onInputMouseMove(e)
	 *
	 * @param e {Object} jQuery event.
	 *
	 * @author agorbatchev
	 * @date 2011/08/08
	 * @id TextExtTags.onInputMouseMove
	 */
	p.onInputMouseMove = function(e)
	{
		this.toggleZIndex(e);
	};

	/**
	 * Reacts to user moving mouse over the tags. Whenever the cursor moves out
	 * of the tags and back into where the text input is happening visually,
	 * the tags container is sent back under the text area which allows user
	 * to interact with the text using mouse cursor as expected.
	 *
	 * @signature TextExtTags.onContainerMouseMove(e)
	 *
	 * @param e {Object} jQuery event.
	 *
	 * @author agorbatchev
	 * @date 2011/08/08
	 * @id TextExtTags.onContainerMouseMove
	 */
	p.onContainerMouseMove = function(e)
	{
		this.toggleZIndex(e);
	};

	/**
	 * Reacts to the `backspaceKeyDown` event. When backspace key is pressed in an empty text field,
	 * deletes last tag from the list.
	 *
	 * @signature TextExtTags.onBackspaceKeyDown(e)
	 *
	 * @param e {Object} jQuery event.
	 *
	 * @author agorbatchev
	 * @date 2011/08/02
	 * @id TextExtTags.onBackspaceKeyDown
	 */
	p.onBackspaceKeyDown = function(e)
	{
		var self    = this,
			lastTag = self.tagElements().last()
			;

		if(self.val().length == 0)
			self.removeTag(lastTag);
	};

	/**
	 * Reacts to the `preInvalidate` event and updates the input box to look like the tags are
	 * positioned inside it.
	 *
	 * @signature TextExtTags.onPreInvalidate(e)
	 *
	 * @param e {Object} jQuery event.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExtTags.onPreInvalidate
	 */
	p.onPreInvalidate = function(e)
	{
		var self    = this,
			lastTag = self.tagElements().last(),
			pos     = lastTag.position()
			;

		if(lastTag.length > 0)
			pos.left += lastTag.innerWidth();
		else
			pos = self._originalPadding;

		self._paddingBox = pos;

		self.input().css({
			paddingLeft : pos.left + 3,
			paddingTop  : pos.top - 1
		});
	};

	/**
	 * Reacts to the mouse `click` event.
	 *
	 * @signature TextExtTags.onClick(e)
	 *
	 * @param e {Object} jQuery event.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExtTags.onClick
	 */
	p.onClick = function(e)
	{
		var self   = this,
			core   = self.core(),
			source = $(e.target),
			focus  = 0,
			tag
			;

		if(source.is(CSS_DOT_TAGS))
		{
			focus = 1;
		}
		else if(source.is(CSS_DOT_REMOVE))
		{
			self.removeTag(source.parents(CSS_DOT_TAG + ':first'));
			focus = 1;

			var elm = $('#section-agent-teams');
			var teamPlaceholder = $('#section-team-placeholder').attr('placeholder');

			if(elm.textext()[0].tags().tagElements().length > 0) {
				elm.attr('placeholder', '');
			} else {
				elm.attr('placeholder', teamPlaceholder);
			}
		}
		else if(source.is(CSS_DOT_LABEL))
		{
			tag = source.parents(CSS_DOT_TAG + ':first');
			self.trigger(EVENT_TAG_CLICK, tag, tag.data(CSS_TAG), tagClickCallback);
		}

		function tagClickCallback(newValue, refocus)
		{
			tag.data(CSS_TAG, newValue);
			tag.find(CSS_DOT_LABEL).text(self.itemManager().itemToString(newValue));

			self.updateFormCache();
			core.getFormData();
			core.invalidateBounds();

			if(refocus)
				core.focusInput();
		}

		if(focus)
			core.focusInput();
	};

	/**
	 * Reacts to the `enterKeyPress` event and adds whatever is currently in the text input
	 * as a new tag. Triggers `isTagAllowed` to check if the tag could be added first.
	 *
	 * @signature TextExtTags.onEnterKeyPress(e)
	 *
	 * @param e {Object} jQuery event.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExtTags.onEnterKeyPress
	 */
	p.onEnterKeyPress = function(e)
	{
		var self = this,
			val  = self.val().trim(),
			tag  = self.itemManager().stringToItem(val);

		// Do not allow tags to duplicate
		if( $('#tags-array').val().match(new RegExp("(?:^|,)" + tag + "(?:,|$)"))) {
			return false;
		}
		self.addTags([ tag ]);
		// refocus the textarea just in case it lost the focus
		self.core().focusInput();
	};

	//--------------------------------------------------------------------------------
	// Core functionality

	/**
	 * Creates a cache object with all the tags currently added which will be returned
	 * in the `onGetFormData` handler.
	 *
	 * @signature TextExtTags.updateFormCache()
	 *
	 * @author agorbatchev
	 * @date 2011/08/09
	 * @id TextExtTags.updateFormCache
	 */
	p.updateFormCache = function()
	{
		var self   = this,
			result = [];

		self.tagElements().each(function()
		{
			result.push($(this).data(CSS_TAG));
		});

		// cache the results to be used in the onGetFormData
		self._formData = result.toString();
	};

	/**
	 * Toggles tag container to be on top of the text area or under based on where
	 * the mouse cursor is located. When cursor is above the text input and out of
	 * any of the tags, the tags container is sent under the text area. If cursor
	 * is over any of the tags, the tag container is brought to be over the text
	 * area.
	 *
	 * @signature TextExtTags.toggleZIndex(e)
	 *
	 * @param e {Object} jQuery event.
	 *
	 * @author agorbatchev
	 * @date 2011/08/08
	 * @id TextExtTags.toggleZIndex
	 */
	p.toggleZIndex = function(e)
	{
		var self            = this,
			offset          = self.input().offset(),
			mouseX          = e.clientX - offset.left,
			mouseY          = e.clientY - offset.top,
			box             = self._paddingBox,
			container       = self.containerElement(),
			isOnTop         = container.is(CSS_DOT_TAGS_ON_TOP),
			isMouseOverText = mouseX > box.left && mouseY > box.top
			;

		if(!isOnTop && !isMouseOverText || isOnTop && isMouseOverText)
			container[(!isOnTop ? 'add' : 'remove') + 'Class'](CSS_TAGS_ON_TOP);
	};

	/**
	 * Returns all tag HTML elements.
	 *
	 * @signature TextExtTags.tagElements()
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExtTags.tagElements
	 */
	p.tagElements = function()
	{
		return this.containerElement().find(CSS_DOT_TAG);
	};

	/**
	 * Wrapper around the `isTagAllowed` event which triggers it and returns `true`
	 * if `result` property of the second argument remains `true`.
	 *
	 * @signature TextExtTags.isTagAllowed(tag)
	 *
	 * @param tag {Object} Tag object that the current `ItemManager` can understand.
	 * Default is `String`.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExtTags.isTagAllowed
	 */
	p.isTagAllowed = function(tag)
	{
		var opts = { tag : tag, result : true };
		this.trigger(EVENT_IS_TAG_ALLOWED, opts);
		return opts.result === true;
	};

	/**
	 * Adds specified tags to the tag list. Triggers `isTagAllowed` event for each tag
	 * to insure that it could be added. Calls `TextExt.getFormData()` to refresh the data.
	 *
	 * @signature TextExtTags.addTags(tags)
	 *
	 * @param tags {Array} List of tags that current `ItemManager` can understand. Default
	 * is `String`.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExtTags.addTags
	 */
	p.addTags = function(tags)
	{
		if(!tags || tags.length == 0)
			return;

		var self      = this,
			core      = self.core(),
			container = self.containerElement(),
			i, tag
			;

		for(i = 0; i < tags.length; i++)
		{
			tag = tags[i];

			if(tag && self.isTagAllowed(tag))
				container.append(self.renderTag(tag));
		}

		self.updateFormCache();
		core.getFormData();
		core.invalidateBounds();
	};

	/**
	 * Returns HTML element for the specified tag.
	 *
	 * @signature TextExtTags.getTagElement(tag)
	 *
	 * @param tag {Object} Tag object in the format that current `ItemManager` can understand.
	 * Default is `String`.

	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExtTags.getTagElement
	 */
	p.getTagElement = function(tag)
	{
		var self = this,
			list = self.tagElements(),
			i, item
			;

		for(i = 0; i < list.length; i++) {
			item = $(list[i]);
			if(self.itemManager().compareItems(item.data(CSS_TAG), tag))
				return item;
		}
		
		return null;
	};

	/**
	 * Removes specified tag from the list. Calls `TextExt.getFormData()` to refresh the data.
	 *
	 * @signature TextExtTags.removeTag(tag)
	 *
	 * @param tag {Object} Tag object in the format that current `ItemManager` can understand.
	 * Default is `String`.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExtTags.removeTag
	 */
	p.removeTag = function(tag)
	{
		var self = this,
			core = self.core(),
			element
			;

		if(tag instanceof $)
		{
			element = tag;
			tag = tag.data(CSS_TAG);
		}
		else
		{
			element = self.getTagElement(tag);
			if (element === null) {
				//Tag does not exist
				return;
			}
		}

		element.remove();
		self.updateFormCache();
		core.getFormData();
		core.invalidateBounds();
	};

	/**
	 * Creates and returns new HTML element from the source code specified in the `html.tag` option.
	 *
	 * @signature TextExtTags.renderTag(tag)
	 *
	 * @param tag {Object} Tag object in the format that current `ItemManager` can understand.
	 * Default is `String`.
	 *
	 * @author agorbatchev
	 * @date 2011/08/19
	 * @id TextExtTags.renderTag
	 */
	p.renderTag = function(tag)
	{
		var self = this,
			node = $(self.opts(OPT_HTML_TAG))
			;

		node.find('.text-label').text(self.itemManager().itemToString(tag));
		node.data(CSS_TAG, tag);
		return node;
	};
})(jQuery);
};


modules['helpcenter/primary/models/like'] = function(require, exports, module, modules) {
module.exports = Backbone.Model.extend({
    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @param  {Object}
     */
    initialize: function (options) {
    	this.options = options || {};
        if (options.unlike) {
            this.idAttribute = 'commentId';
        }
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @return {Object}
     */
    url: function() {
    	return _Payload.portalpath + 'api/v1/articles/' + this.options.articleId + '/comment/' + this.options.commentId + '/like.json?include=*';
    }
});
};


modules['helpcenter/primary/plugins/minicolors'] = function(require, exports, module, modules) {
/*
 * jQuery MiniColors: A tiny color picker built on jQuery
 *
 * Copyright: Cory LaViska for A Beautiful Site, LLC: http://www.abeautifulsite.net/
 *
 * Contribute: https://github.com/claviska/jquery-minicolors
 *
 * @license: http://opensource.org/licenses/MIT
 *
 */
!function(i){"function"==typeof define&&define.amd?define(["jquery"],i):"object"==typeof exports?module.exports=i(require("jquery")):i(jQuery)}(function($){"use strict";function i(i,t){var o=$('<div class="minicolors" />'),a=$.minicolors.defaults,s=i.attr("data-opacity"),n;i.data("minicolors-initialized")||(t=$.extend(!0,{},a,t),o.addClass("minicolors-theme-"+t.theme).toggleClass("minicolors-with-opacity",t.opacity).toggleClass("minicolors-no-data-uris",t.dataUris!==!0),void 0!==t.position&&$.each(t.position.split(" "),function(){o.addClass("minicolors-position-"+this)}),n="rgb"===t.format?t.opacity?"25":"20":t.keywords?"11":"7",i.addClass("minicolors-input").data("minicolors-initialized",!1).data("minicolors-settings",t).prop("size",n).wrap(o).after('<div class="minicolors-panel minicolors-slider-'+t.control+'"><div class="minicolors-slider minicolors-sprite"><div class="minicolors-picker"></div></div><div class="minicolors-opacity-slider minicolors-sprite"><div class="minicolors-picker"></div></div><div class="minicolors-grid minicolors-sprite"><div class="minicolors-grid-inner"></div><div class="minicolors-picker"><div></div></div></div></div>'),t.inline||(i.after('<span class="minicolors-swatch minicolors-sprite"><span class="minicolors-swatch-color"></span></span>'),i.next(".minicolors-swatch").on("click",function(t){t.preventDefault(),i.focus()})),i.parent().find(".minicolors-panel").on("selectstart",function(){return!1}).end(),t.inline&&i.parent().addClass("minicolors-inline"),r(i,!1),i.data("minicolors-initialized",!0))}function t(i){var t=i.parent();i.removeData("minicolors-initialized").removeData("minicolors-settings").removeProp("size").removeClass("minicolors-input"),t.before(i).remove()}function o(i){var t=i.parent(),o=t.find(".minicolors-panel"),s=i.data("minicolors-settings");!i.data("minicolors-initialized")||i.prop("disabled")||t.hasClass("minicolors-inline")||t.hasClass("minicolors-focus")||(a(),t.addClass("minicolors-focus"),o.stop(!0,!0).fadeIn(s.showSpeed,function(){s.show&&s.show.call(i.get(0))}))}function a(){$(".minicolors-focus").each(function(){var i=$(this),t=i.find(".minicolors-input"),o=i.find(".minicolors-panel"),a=t.data("minicolors-settings");o.fadeOut(a.hideSpeed,function(){a.hide&&a.hide.call(t.get(0)),i.removeClass("minicolors-focus")})})}function s(i,t,o){var a=i.parents(".minicolors").find(".minicolors-input"),s=a.data("minicolors-settings"),r=i.find("[class$=-picker]"),e=i.offset().left,c=i.offset().top,l=Math.round(t.pageX-e),h=Math.round(t.pageY-c),d=o?s.animationSpeed:0,u,p,g,m;t.originalEvent.changedTouches&&(l=t.originalEvent.changedTouches[0].pageX-e,h=t.originalEvent.changedTouches[0].pageY-c),0>l&&(l=0),0>h&&(h=0),l>i.width()&&(l=i.width()),h>i.height()&&(h=i.height()),i.parent().is(".minicolors-slider-wheel")&&r.parent().is(".minicolors-grid")&&(u=75-l,p=75-h,g=Math.sqrt(u*u+p*p),m=Math.atan2(p,u),0>m&&(m+=2*Math.PI),g>75&&(g=75,l=75-75*Math.cos(m),h=75-75*Math.sin(m)),l=Math.round(l),h=Math.round(h)),i.is(".minicolors-grid")?r.stop(!0).animate({top:h+"px",left:l+"px"},d,s.animationEasing,function(){n(a,i)}):r.stop(!0).animate({top:h+"px"},d,s.animationEasing,function(){n(a,i)})}function n(i,t){function o(i,t){var o,a;return i.length&&t?(o=i.offset().left,a=i.offset().top,{x:o-t.offset().left+i.outerWidth()/2,y:a-t.offset().top+i.outerHeight()/2}):null}var a,s,n,r,c,l,d,u=i.val(),p=i.attr("data-opacity"),m,f=i.parent(),v=i.data("minicolors-settings"),b=f.find(".minicolors-swatch"),y=f.find(".minicolors-grid"),C=f.find(".minicolors-slider"),M=f.find(".minicolors-opacity-slider"),x=y.find("[class$=-picker]"),I=C.find("[class$=-picker]"),S=M.find("[class$=-picker]"),z=o(x,y),F=o(I,C),j=o(S,M);if(t.is(".minicolors-grid, .minicolors-slider, .minicolors-opacity-slider")){switch(v.control){case"wheel":r=y.width()/2-z.x,c=y.height()/2-z.y,l=Math.sqrt(r*r+c*c),d=Math.atan2(c,r),0>d&&(d+=2*Math.PI),l>75&&(l=75,z.x=69-75*Math.cos(d),z.y=69-75*Math.sin(d)),s=g(l/.75,0,100),a=g(180*d/Math.PI,0,360),n=g(100-Math.floor(F.y*(100/C.height())),0,100),u=w({h:a,s:s,b:n}),C.css("backgroundColor",w({h:a,s:s,b:100}));break;case"saturation":a=g(parseInt(z.x*(360/y.width()),10),0,360),s=g(100-Math.floor(F.y*(100/C.height())),0,100),n=g(100-Math.floor(z.y*(100/y.height())),0,100),u=w({h:a,s:s,b:n}),C.css("backgroundColor",w({h:a,s:100,b:n})),f.find(".minicolors-grid-inner").css("opacity",s/100);break;case"brightness":a=g(parseInt(z.x*(360/y.width()),10),0,360),s=g(100-Math.floor(z.y*(100/y.height())),0,100),n=g(100-Math.floor(F.y*(100/C.height())),0,100),u=w({h:a,s:s,b:n}),C.css("backgroundColor",w({h:a,s:s,b:100})),f.find(".minicolors-grid-inner").css("opacity",1-n/100);break;default:a=g(360-parseInt(F.y*(360/C.height()),10),0,360),s=g(Math.floor(z.x*(100/y.width())),0,100),n=g(100-Math.floor(z.y*(100/y.height())),0,100),u=w({h:a,s:s,b:n}),y.css("backgroundColor",w({h:a,s:100,b:100}))}if(p=v.opacity?parseFloat(1-j.y/M.height()).toFixed(2):1,v.opacity&&i.attr("data-opacity",p),"rgb"===v.format){var D=k(u),p=""===i.attr("data-opacity")?1:g(parseFloat(i.attr("data-opacity")).toFixed(2),0,1);(isNaN(p)||!v.opacity)&&(p=1),m=i.minicolors("rgbObject").a<=1&&D&&v.opacity?"rgba("+D.r+", "+D.g+", "+D.b+", "+parseFloat(p)+")":"rgb("+D.r+", "+D.g+", "+D.b+")"}else m=h(u,v.letterCase);i.val(m)}b.find("span").css({backgroundColor:u,opacity:p}),e(i,m,p)}function r(i,t){var o,a,s,n,r,c,l,v,y,M,k=i.parent(),x=i.data("minicolors-settings"),I=k.find(".minicolors-swatch"),S=k.find(".minicolors-grid"),z=k.find(".minicolors-slider"),F=k.find(".minicolors-opacity-slider"),j=S.find("[class$=-picker]"),D=z.find("[class$=-picker]"),T=F.find("[class$=-picker]");switch(m(i.val())?(o=b(i.val()),r=g(parseFloat(f(i.val())).toFixed(2),0,1),r&&i.attr("data-opacity",r)):o=h(d(i.val(),!0),x.letterCase),o||(o=h(p(x.defaultValue,!0),x.letterCase)),a=C(o),n=x.keywords?$.map(x.keywords.split(","),function(i){return $.trim(i.toLowerCase())}):[],c=""!==i.val()&&$.inArray(i.val().toLowerCase(),n)>-1?h(i.val()):m(i.val())?u(i.val()):o,t||i.val(c),x.opacity&&(s=""===i.attr("data-opacity")?1:g(parseFloat(i.attr("data-opacity")).toFixed(2),0,1),isNaN(s)&&(s=1),i.attr("data-opacity",s),I.find("span").css("opacity",s),v=g(F.height()-F.height()*s,0,F.height()),T.css("top",v+"px")),"transparent"===i.val().toLowerCase()&&I.find("span").css("opacity",0),I.find("span").css("backgroundColor",o),x.control){case"wheel":y=g(Math.ceil(.75*a.s),0,S.height()/2),M=a.h*Math.PI/180,l=g(75-Math.cos(M)*y,0,S.width()),v=g(75-Math.sin(M)*y,0,S.height()),j.css({top:v+"px",left:l+"px"}),v=150-a.b/(100/S.height()),""===o&&(v=0),D.css("top",v+"px"),z.css("backgroundColor",w({h:a.h,s:a.s,b:100}));break;case"saturation":l=g(5*a.h/12,0,150),v=g(S.height()-Math.ceil(a.b/(100/S.height())),0,S.height()),j.css({top:v+"px",left:l+"px"}),v=g(z.height()-a.s*(z.height()/100),0,z.height()),D.css("top",v+"px"),z.css("backgroundColor",w({h:a.h,s:100,b:a.b})),k.find(".minicolors-grid-inner").css("opacity",a.s/100);break;case"brightness":l=g(5*a.h/12,0,150),v=g(S.height()-Math.ceil(a.s/(100/S.height())),0,S.height()),j.css({top:v+"px",left:l+"px"}),v=g(z.height()-a.b*(z.height()/100),0,z.height()),D.css("top",v+"px"),z.css("backgroundColor",w({h:a.h,s:a.s,b:100})),k.find(".minicolors-grid-inner").css("opacity",1-a.b/100);break;default:l=g(Math.ceil(a.s/(100/S.width())),0,S.width()),v=g(S.height()-Math.ceil(a.b/(100/S.height())),0,S.height()),j.css({top:v+"px",left:l+"px"}),v=g(z.height()-a.h/(360/z.height()),0,z.height()),D.css("top",v+"px"),S.css("backgroundColor",w({h:a.h,s:100,b:100}))}i.data("minicolors-initialized")&&e(i,c,s)}function e(i,t,o){var a=i.data("minicolors-settings"),s=i.data("minicolors-lastChange");s&&s.value===t&&s.opacity===o||(i.data("minicolors-lastChange",{value:t,opacity:o}),a.change&&(a.changeDelay?(clearTimeout(i.data("minicolors-changeTimeout")),i.data("minicolors-changeTimeout",setTimeout(function(){a.change.call(i.get(0),t,o)},a.changeDelay))):a.change.call(i.get(0),t,o)),i.trigger("change").trigger("input"))}function c(i){var t=d($(i).val(),!0),o=k(t),a=$(i).attr("data-opacity");return o?(void 0!==a&&$.extend(o,{a:parseFloat(a)}),o):null}function l(i,t){var o=d($(i).val(),!0),a=k(o),s=$(i).attr("data-opacity");return a?(void 0===s&&(s=1),t?"rgba("+a.r+", "+a.g+", "+a.b+", "+parseFloat(s)+")":"rgb("+a.r+", "+a.g+", "+a.b+")"):null}function h(i,t){return"uppercase"===t?i.toUpperCase():i.toLowerCase()}function d(i,t){return i=i.replace(/^#/g,""),i.match(/^[A-F0-9]{3,6}/gi)?3!==i.length&&6!==i.length?"":(3===i.length&&t&&(i=i[0]+i[0]+i[1]+i[1]+i[2]+i[2]),"#"+i):""}function u(i,t){var o=i.replace(/[^\d,.]/g,""),a=o.split(","),s;return a[0]=g(parseInt(a[0],10),0,255),a[1]=g(parseInt(a[1],10),0,255),a[2]=g(parseInt(a[2],10),0,255),a[3]&&(a[3]=g(parseFloat(a[3],10),0,1)),t?{r:a[0],g:a[1],b:a[2],a:a[3]?a[3]:null}:"undefined"!=typeof a[3]&&a[3]<=1?"rgba("+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+")":"rgb("+a[0]+", "+a[1]+", "+a[2]+")"}function p(i,t){return m(i)?u(i):d(i,t)}function g(i,t,o){return t>i&&(i=t),i>o&&(i=o),i}function m(i){var t=i.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);return t&&4===t.length?!0:!1}function f(i){var i=i.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+(\.\d{1,2})?|\.\d{1,2})[\s+]?/i);return i&&6===i.length?i[4]:"1"}function v(i){var t={},o=Math.round(i.h),a=Math.round(255*i.s/100),s=Math.round(255*i.b/100);if(0===a)t.r=t.g=t.b=s;else{var n=s,r=(255-a)*s/255,e=(n-r)*(o%60)/60;360===o&&(o=0),60>o?(t.r=n,t.b=r,t.g=r+e):120>o?(t.g=n,t.b=r,t.r=n-e):180>o?(t.g=n,t.r=r,t.b=r+e):240>o?(t.b=n,t.r=r,t.g=n-e):300>o?(t.b=n,t.g=r,t.r=r+e):360>o?(t.r=n,t.g=r,t.b=n-e):(t.r=0,t.g=0,t.b=0)}return{r:Math.round(t.r),g:Math.round(t.g),b:Math.round(t.b)}}function b(i){return i=i.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i),i&&4===i.length?"#"+("0"+parseInt(i[1],10).toString(16)).slice(-2)+("0"+parseInt(i[2],10).toString(16)).slice(-2)+("0"+parseInt(i[3],10).toString(16)).slice(-2):""}function y(i){var t=[i.r.toString(16),i.g.toString(16),i.b.toString(16)];return $.each(t,function(i,o){1===o.length&&(t[i]="0"+o)}),"#"+t.join("")}function w(i){return y(v(i))}function C(i){var t=M(k(i));return 0===t.s&&(t.h=360),t}function M(i){var t={h:0,s:0,b:0},o=Math.min(i.r,i.g,i.b),a=Math.max(i.r,i.g,i.b),s=a-o;return t.b=a,t.s=0!==a?255*s/a:0,0!==t.s?i.r===a?t.h=(i.g-i.b)/s:i.g===a?t.h=2+(i.b-i.r)/s:t.h=4+(i.r-i.g)/s:t.h=-1,t.h*=60,t.h<0&&(t.h+=360),t.s*=100/255,t.b*=100/255,t}function k(i){return i=parseInt(i.indexOf("#")>-1?i.substring(1):i,16),{r:i>>16,g:(65280&i)>>8,b:255&i}}$.minicolors={defaults:{animationSpeed:50,animationEasing:"swing",change:null,changeDelay:0,control:"hue",dataUris:!0,defaultValue:"",format:"hex",hide:null,hideSpeed:100,inline:!1,keywords:"",letterCase:"lowercase",opacity:!1,position:"bottom left",show:null,showSpeed:100,theme:"default"}},$.extend($.fn,{minicolors:function(s,n){switch(s){case"destroy":return $(this).each(function(){t($(this))}),$(this);case"hide":return a(),$(this);case"opacity":return void 0===n?$(this).attr("data-opacity"):($(this).each(function(){r($(this).attr("data-opacity",n))}),$(this));case"rgbObject":return c($(this),"rgbaObject"===s);case"rgbString":case"rgbaString":return l($(this),"rgbaString"===s);case"settings":return void 0===n?$(this).data("minicolors-settings"):($(this).each(function(){var i=$(this).data("minicolors-settings")||{};t($(this)),$(this).minicolors($.extend(!0,i,n))}),$(this));case"show":return o($(this).eq(0)),$(this);case"value":return void 0===n?$(this).val():($(this).each(function(){"object"==typeof n?(n.opacity&&$(this).attr("data-opacity",g(n.opacity,0,1)),n.color&&$(this).val(n.color)):$(this).val(n),r($(this))}),$(this));default:return"create"!==s&&(n=s),$(this).each(function(){i($(this),n)}),$(this)}}}),$(document).on("mousedown.minicolors touchstart.minicolors",function(i){$(i.target).parents().add(i.target).hasClass("minicolors")||a()}).on("mousedown.minicolors touchstart.minicolors",".minicolors-grid, .minicolors-slider, .minicolors-opacity-slider",function(i){var t=$(this);i.preventDefault(),$(document).data("minicolors-target",t),s(t,i,!0)}).on("mousemove.minicolors touchmove.minicolors",function(i){var t=$(document).data("minicolors-target");t&&s(t,i)}).on("mouseup.minicolors touchend.minicolors",function(){$(this).removeData("minicolors-target")}).on("mousedown.minicolors touchstart.minicolors",".minicolors-swatch",function(i){var t=$(this).parent().find(".minicolors-input");i.preventDefault(),o(t)}).on("focus.minicolors",".minicolors-input",function(){var i=$(this);i.data("minicolors-initialized")&&o(i)}).on("blur.minicolors",".minicolors-input",function(){var i=$(this),t=i.data("minicolors-settings"),o,a,s,n,r;i.data("minicolors-initialized")&&(o=t.keywords?$.map(t.keywords.split(","),function(i){return $.trim(i.toLowerCase())}):[],""!==i.val()&&$.inArray(i.val().toLowerCase(),o)>-1?r=i.val():(m(i.val())?s=u(i.val(),!0):(a=d(i.val(),!0),s=a?k(a):null),r=null===s?t.defaultValue:"rgb"===t.format?u(t.opacity?"rgba("+s.r+","+s.g+","+s.b+","+i.attr("data-opacity")+")":"rgb("+s.r+","+s.g+","+s.b+")"):y(s)),n=t.opacity?i.attr("data-opacity"):1,"transparent"===r.toLowerCase()&&(n=0),i.closest(".minicolors").find(".minicolors-swatch > span").css("opacity",n),i.val(r),""===i.val()&&i.val(p(t.defaultValue,!0)),i.val(h(i.val(),t.letterCase)))}).on("keydown.minicolors",".minicolors-input",function(i){var t=$(this);if(t.data("minicolors-initialized"))switch(i.keyCode){case 9:a();break;case 13:case 27:a(),t.blur()}}).on("keyup.minicolors",".minicolors-input",function(){var i=$(this);i.data("minicolors-initialized")&&r(i,!0)}).on("paste.minicolors",".minicolors-input",function(){var i=$(this);i.data("minicolors-initialized")&&setTimeout(function(){r(i,!0)},1)})});
};


modules['helpcenter/primary/plugins/froala'] = function(require, exports, module, modules) {
/*!
 * froala_editor v2.3.4 (https://www.froala.com/wysiwyg-editor)
 * License https://froala.com/wysiwyg-editor/terms/
 * Copyright 2014-2016 Froala Labs
 */

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof module === 'object' && module.exports) {
        // Node/CommonJS
        module.exports = function( root, jQuery ) {
            if ( jQuery === undefined ) {
                // require('jQuery') returns a factory that requires window to
                // build a jQuery instance, we normalize how we use modules
                // that require this pattern but the window provided is a noop
                // if it's defined (how jquery works)
                if ( typeof window !== 'undefined' ) {
                    jQuery = require('jquery');
                }
                else {
                    jQuery = require('jquery')(root);
                }
            }
            factory(jQuery);
            return jQuery;
        };
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {
  /*jslint browser: true, debug: true, vars: true, devel: true, expr: true, jQuery: true */
  // EDITABLE CLASS DEFINITION
  // =========================

  'use strict';

  var FE = function (element, options) {
    this.id = ++$.FE.ID;

    this.opts = $.extend(true, {}, $.extend({}, FE.DEFAULTS, typeof options == 'object' && options));
    var opts_string = JSON.stringify(this.opts);

    $.FE.OPTS_MAPPING[opts_string] = $.FE.OPTS_MAPPING[opts_string] || this.id;
    this.sid = $.FE.OPTS_MAPPING[opts_string];
    $.FE.SHARED[this.sid] = $.FE.SHARED[this.sid] || {};
    this.shared = $.FE.SHARED[this.sid];
    this.shared.count = (this.shared.count || 0) + 1;

    this.$oel = $(element);
    this.$oel.data('froala.editor', this);

    this.o_doc = element.ownerDocument;
    this.o_win = 'defaultView' in this.o_doc ? this.o_doc.defaultView :   this.o_doc.parentWindow;
    var c_scroll = $(this.o_win).scrollTop();

    this.$oel.on('froala.doInit', $.proxy(function () {
      this.$oel.off('froala.doInit');
      this.doc = this.$el.get(0).ownerDocument;
      this.win = 'defaultView' in this.doc ? this.doc.defaultView : this.doc.parentWindow;
      this.$doc = $(this.doc);
      this.$win = $(this.win);

      if (!this.opts.pluginsEnabled) this.opts.pluginsEnabled = Object.keys($.FE.PLUGINS);

      if (this.opts.initOnClick) {
        this.load($.FE.MODULES);

        // https://github.com/froala/wysiwyg-editor/issues/1207.
        this.$el.on('touchstart.init', function () {
          $(this).data('touched', true);
        });

        this.$el.on('touchmove.init', function () {
          $(this).removeData('touched');
        })

        this.$el.on('mousedown.init touchend.init dragenter.init focus.init', $.proxy(function (e) {
          if (e.type == 'touchend' && !this.$el.data('touched')) {
            return true;
          }

          if (e.which === 1 || e.which === 0) {
            this.$el.off('mousedown.init touchstart.init touchmove.init touchend.init dragenter.init focus.init');

            this.load($.FE.MODULES);
            this.load($.FE.PLUGINS);

            var target = e.originalEvent && e.originalEvent.originalTarget;
            if (target && target.tagName == 'IMG') $(target).trigger('mousedown');

            if (typeof this.ul == 'undefined') this.destroy();

            if (e.type == 'touchend' && this.image && e.originalEvent && e.originalEvent.target && $(e.originalEvent.target).is('img')) {
              setTimeout($.proxy(function () {
                this.image.edit($(e.originalEvent.target));
              }, this), 100);
            }

            this.events.trigger('initialized');
          }
        }, this));
      }
      else {
        this.load($.FE.MODULES);
        this.load($.FE.PLUGINS);

        $(this.o_win).scrollTop(c_scroll);

        if (typeof this.ul == 'undefined') this.destroy();

        this.events.trigger('initialized');
      }
    }, this));

    this._init();
  };

  FE.DEFAULTS = {
    initOnClick: false,
    pluginsEnabled: null
  };

  FE.MODULES = {};

  FE.PLUGINS = {};

  FE.VERSION = '2.3.4';

  FE.INSTANCES = [];

  FE.OPTS_MAPPING = {};

  FE.SHARED = {};

  FE.ID = 0;

  FE.prototype._init = function () {
    // Get the tag name of the original element.
    var tag_name = this.$oel.prop('tagName');

    // Initialize on anything else.
    var initOnDefault = $.proxy(function () {
      this._original_html = (this._original_html || this.$oel.html());
      this.$box = this.$box || this.$oel;

      // Turn on iframe if fullPage is on.
      if (this.opts.fullPage) this.opts.iframe = true;

      if (!this.opts.iframe) {
        this.$el = $('<div></div>');
        this.$wp = $('<div></div>').append(this.$el);
        this.$box.html(this.$wp);
        this.$oel.trigger('froala.doInit');
      }
      else {
        this.$iframe = $('<iframe src="about:blank" frameBorder="0">');
        this.$wp = $('<div></div>');
        this.$box.html(this.$wp);
        this.$wp.append(this.$iframe);
        this.$iframe.get(0).contentWindow.document.open();
        this.$iframe.get(0).contentWindow.document.write('<!DOCTYPE html>');
        this.$iframe.get(0).contentWindow.document.write('<html><head></head><body></body></html>');
        this.$iframe.get(0).contentWindow.document.close();

        this.$el = this.$iframe.contents().find('body');
        this.$head = this.$iframe.contents().find('head');
        this.$html = this.$iframe.contents().find('html');
        this.iframe_document = this.$iframe.get(0).contentWindow.document;

        this.$oel.trigger('froala.doInit');
      }
    }, this);

    // Initialize on a TEXTAREA.
    var initOnTextarea = $.proxy(function () {
      this.$box = $('<div>');
      this.$oel.before(this.$box).hide();

      this._original_html = this.$oel.val();

      // Before submit textarea do a sync.
      this.$oel.parents('form').on('submit.' + this.id, $.proxy(function () {
        this.events.trigger('form.submit');
      }, this));

      this.$oel.parents('form').on('reset.' + this.id, $.proxy(function () {
        this.events.trigger('form.reset');
      }, this));

      initOnDefault();
    }, this);

    // Initialize on a Link.
    var initOnA = $.proxy(function () {
      this.$el = this.$oel;
      this.$el.attr('contenteditable', true).css('outline', 'none').css('display', 'inline-block');
      this.opts.multiLine = false;
      this.opts.toolbarInline = false;

      this.$oel.trigger('froala.doInit');
    }, this)

    // Initialize on an Image.
    var initOnImg = $.proxy(function () {
      this.$el = this.$oel;
      this.opts.toolbarInline = false;

      this.$oel.trigger('froala.doInit');
    }, this)

    var editInPopup = $.proxy(function () {
      this.$el = this.$oel;
      this.opts.toolbarInline = false;

      this.$oel.on('click.popup', function (e) {
        e.preventDefault();
      })
      this.$oel.trigger('froala.doInit');
    }, this);

    // Check on what element it was initialized.
    if (this.opts.editInPopup) editInPopup();
    else if (tag_name == 'TEXTAREA') initOnTextarea();
    else if (tag_name == 'A') initOnA();
    else if (tag_name == 'IMG') initOnImg();
    else if (tag_name == 'BUTTON' || tag_name == 'INPUT') {
      this.opts.editInPopup = true;
      this.opts.toolbarInline = false;
      editInPopup();
    }
    else {
      initOnDefault();
    }
  }

  FE.prototype.load = function (module_list) {
    // Bind modules to the current instance and tear them up.
    for (var m_name in module_list) {
      if (module_list.hasOwnProperty(m_name)) {
        if (this[m_name]) continue;

        // Do not include plugin.
        if ($.FE.PLUGINS[m_name] && this.opts.pluginsEnabled.indexOf(m_name) < 0) continue;

        this[m_name] = new module_list[m_name](this);
        if (this[m_name]._init) {
          this[m_name]._init();
          if (this.opts.initOnClick && m_name == 'core') {
            return false;
          }
        }
      }
    }
  }

  // Do destroy.
  FE.prototype.destroy = function () {
    this.shared.count--;

    this.events.$off();

    // HTML.
    var html = this.html.get();

    this.events.trigger('destroy', [], true);
    this.events.trigger('shared.destroy', undefined, true);

    // Remove shared.
    if (this.shared.count === 0) {
      for (var k in this.shared) {
        if (this.shared.hasOwnProperty(k)) {
          this.shared[k] == null;
          $.FE.SHARED[this.sid][k] = null;
        }
      }

      $.FE.SHARED[this.sid] = {};
    }

    this.$oel.parents('form').off('.' + this.id);
    this.$oel.off('click.popup');
    this.$oel.removeData('froala.editor');

    // Destroy editor basic elements.
    this.core.destroy(html);

    $.FE.INSTANCES.splice($.FE.INSTANCES.indexOf(this), 1);
  }

  // FROALA EDITOR PLUGIN DEFINITION
  // ==========================
  $.fn.froalaEditor = function (option) {
    var arg_list = [];
    for (var i = 0; i < arguments.length; i++) {
      arg_list.push(arguments[i]);
    }

    if (typeof option == 'string') {
      var returns = [];

      this.each(function () {
        var $this = $(this);
        var editor = $this.data('froala.editor');

        if (!editor) {
          return console.warn('Editor should be initialized before calling the ' + option + ' method.');
        }

        var context;
        var nm;

        // Might do a module call.
        if (option.indexOf('.') > 0 && editor[option.split('.')[0]]) {
          if (editor[option.split('.')[0]]) {
            context = editor[option.split('.')[0]];
          }
          nm = option.split('.')[1];
        }
        else {
          context = editor;
          nm = option.split('.')[0]
        }

        if (context[nm]) {
          var returned_value = context[nm].apply(editor, arg_list.slice(1));
          if (returned_value === undefined) {
            returns.push(this);
          } else if (returns.length === 0) {
            returns.push(returned_value);
          }
        }
        else {
          return $.error('Method ' +  option + ' does not exist in Froala Editor.');
        }
      });

      return (returns.length == 1) ? returns[0] : returns;
    }
    else if (typeof option === 'object' || !option) {
      return this.each(function () {
        var editor = $(this).data('froala.editor');

        if (!editor) {
          var that = this;
          new FE(that, option);
        }
      });
    }
  }

  $.fn.froalaEditor.Constructor = FE;
  $.FroalaEditor = FE;
  $.FE = FE;


  $.FE.MODULES.node = function (editor) {
    function getContents(node) {
      if (!node || node.tagName == 'IFRAME') return [];
      return $(node).contents();
    }

    /**
     * Determine if the node is a block tag.
     */
    function isBlock (node) {
      if (!node) return false;
      if (node.nodeType != Node.ELEMENT_NODE) return false;

      return $.FE.BLOCK_TAGS.indexOf(node.tagName.toLowerCase()) >= 0;
    }

    /**
     * Check if a DOM element is empty.
     */
    function isEmpty (el, ignore_markers) {
      if ($(el).find('table').length > 0) return false;

      // Look for void nodes.
      if (el.querySelectorAll($.FE.VOID_ELEMENTS.join(',')).length - el.querySelectorAll('br').length) return false;

      // Look for empty allowed tags.
      if (el.querySelectorAll(editor.opts.htmlAllowedEmptyTags.join(':not(.fr-marker),') + ':not(.fr-marker)').length) return false;

      // Look for block tags.
      if (el.querySelectorAll($.FE.BLOCK_TAGS.join(',')).length > 1) return false;

      // Look for do not wrap tags.
      if (el.querySelectorAll(editor.opts.htmlDoNotWrapTags.join(':not(.fr-marker),') + ':not(.fr-marker)').length) return false;

      // Get element contents.
      var contents = getContents(el);

      // Check if there is a block tag.
      if (contents.length == 1 && isBlock(contents[0])) {
        contents = getContents(contents[0]);
      }

      var has_br = false;
      for (var i = 0; i < contents.length; i++) {
        var node = contents[i];

        if (ignore_markers && $(node).hasClass('fr-marker')) continue;

        if (node.nodeType == Node.TEXT_NODE && node.textContent.length == 0) continue;

        if (node.tagName != 'BR' && (node.textContent || '').replace(/\u200B/gi, '').replace(/\n/g, '').length > 0) return false;

        if (has_br) {
          return false;
        }
        else if (node.tagName == 'BR') {
          has_br = true;
        }
      }

      return true;
    }

    /**
     * Get the block parent.
     */
    function blockParent (node) {
      while (node && node.parentNode !== editor.$el.get(0) && !(node.parentNode && $(node.parentNode).hasClass('fr-inner'))) {
        node = node.parentNode;
        if (isBlock(node)) {
          return node;
        }
      }

      return null;
    }

    /**
     * Get deepest parent till the element.
     */
    function deepestParent (node, until, simple_enter) {
      if (typeof until == 'undefined') until = [];
      if (typeof simple_enter == 'undefined') simple_enter = true;
      until.push(editor.$el.get(0));

      if (until.indexOf(node.parentNode) >= 0 || (node.parentNode && $(node.parentNode).hasClass('fr-inner')) || (node.parentNode && $.FE.SIMPLE_ENTER_TAGS.indexOf(node.parentNode.tagName) >= 0 && simple_enter)) {
        return null;
      }

      // 1. Before until.
      // 2. Parent node doesn't has class fr-inner.
      // 3. Parent node is not a simple enter tag or quote.
      // 4. Parent node is not a block tag
      while (until.indexOf(node.parentNode) < 0 && node.parentNode && !$(node.parentNode).hasClass('fr-inner') && ($.FE.SIMPLE_ENTER_TAGS.indexOf(node.parentNode.tagName) < 0 || !simple_enter) && (!(isBlock(node) && isBlock(node.parentNode)) || !simple_enter)) {
        node = node.parentNode;
      }

      return node;
    }

    function rawAttributes (node) {
      var attrs = {};

      var atts = node.attributes;
      if (atts) {
        for (var i = 0; i < atts.length; i++) {
          var att = atts[i];
          attrs[att.nodeName] = att.value;
        }
      }

      return attrs;
    }

    /**
     * Get attributes for a node as a string.
     */
    function attributes (node) {
      var str = '';
      var atts = rawAttributes(node);

      var keys = Object.keys(atts).sort();
      for (var i = 0; i < keys.length; i++) {
        var nodeName = keys[i];
        var value = atts[nodeName];

        // Make sure we don't break any HTML.
        if (value.indexOf('"') < 0) {
          str += ' ' + nodeName + '="' + value + '"';
        }
        else {
          str += ' ' + nodeName + '=\'' + value + '\'';
        }
      }

      return str;
    }

    function clearAttributes (node) {
      var atts = node.attributes;
      for (var i = 0; i < atts.length; i++) {
        var att = atts[i];
        node.removeAttribute(att.nodeName);
      }
    }

    /**
     * Open string for a node.
     */
    function openTagString (node) {
      return '<' + node.tagName.toLowerCase() + attributes(node) + '>';
    }

    /**
     * Close string for a node.
     */
    function closeTagString (node) {
      return '</' + node.tagName.toLowerCase() + '>';
    }

    /**
     * Determine if the node has any left sibling.
     */
    function isFirstSibling (node, ignore_markers) {
      if (typeof ignore_markers == 'undefined') ignore_markers = true;
      var sibling = node.previousSibling;

      while (sibling && ignore_markers && $(sibling).hasClass('fr-marker')) {
        sibling = sibling.previousSibling;
      }

      if (!sibling) return true;
      if (sibling.nodeType == Node.TEXT_NODE && sibling.textContent === '') return isFirstSibling(sibling);
      return false;
    }

    /**
     * Determine if the node has any right sibling.
     */
    function isLastSibling (node, ignore_markers) {
      if (typeof ignore_markers == 'undefined') ignore_markers = true;
      var sibling = node.nextSibling;

      while (sibling && ignore_markers && $(sibling).hasClass('fr-marker')) {
        sibling = sibling.nextSibling;
      }

      if (!sibling) return true;
      if (sibling.nodeType == Node.TEXT_NODE && sibling.textContent === '') return isLastSibling(sibling);
      return false;
    }

    function isVoid(node) {
      return node && node.nodeType == Node.ELEMENT_NODE && $.FE.VOID_ELEMENTS.indexOf((node.tagName || '').toLowerCase()) >= 0
    }

    /**
     * Check if the node is a list.
     */
    function isList (node) {
      if (!node) return false;
      return ['UL', 'OL'].indexOf(node.tagName) >= 0;
    }

    /**
     * Check if the node is the editable element.
     */
    function isElement (node) {
      return node === editor.$el.get(0);
    }

    /**
     * Check if the node is the editable element.
     */
    function isDeletable (node) {
      return node && node.className && (node.className || '').indexOf('fr-deletable') >= 0;
    }

    /**
     * Check if the node has focus.
     */
    function hasFocus (node) {
      return node === editor.doc.activeElement && (!editor.doc.hasFocus || editor.doc.hasFocus()) && !!(isElement(node) || node.type || node.href || ~node.tabIndex);;
    }

    function isEditable (node) {
      return (!node.getAttribute || node.getAttribute('contenteditable') != 'false')
                && ['STYLE', 'SCRIPT'].indexOf(node.tagName) < 0;
    }

    return {
      isBlock: isBlock,
      isEmpty: isEmpty,
      blockParent: blockParent,
      deepestParent: deepestParent,
      rawAttributes: rawAttributes,
      attributes: attributes,
      clearAttributes: clearAttributes,
      openTagString: openTagString,
      closeTagString: closeTagString,
      isFirstSibling: isFirstSibling,
      isLastSibling: isLastSibling,
      isList: isList,
      isElement: isElement,
      contents: getContents,
      isVoid: isVoid,
      hasFocus: hasFocus,
      isEditable: isEditable,
      isDeletable: isDeletable
    }
  };


  // Extend defaults.
  $.extend($.FE.DEFAULTS, {
    // Tags that describe head from HEAD http://www.w3schools.com/html/html_head.asp.
    htmlAllowedTags: ['a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b', 'base', 'bdi', 'bdo', 'blockquote', 'br', 'button', 'canvas', 'caption', 'cite', 'code', 'col', 'colgroup', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'keygen', 'label', 'legend', 'li', 'link', 'main', 'map', 'mark', 'menu', 'menuitem', 'meter', 'nav', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'pre', 'progress', 'queue', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'style', 'section', 'select', 'small', 'source', 'span', 'strike', 'strong', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'u', 'ul', 'var', 'video', 'wbr'],
    htmlRemoveTags: ['script', 'style'],
    htmlAllowedAttrs: ['accept', 'accept-charset', 'accesskey', 'action', 'align', 'allowfullscreen', 'allowtransparency', 'alt', 'async', 'autocomplete', 'autofocus', 'autoplay', 'autosave', 'background', 'bgcolor', 'border', 'charset', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'color', 'cols', 'colspan', 'content', 'contenteditable', 'contextmenu', 'controls', 'coords', 'data', 'data-.*', 'datetime', 'default', 'defer', 'dir', 'dirname', 'disabled', 'download', 'draggable', 'dropzone', 'enctype', 'for', 'form', 'formaction', 'frameborder', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'http-equiv', 'icon', 'id', 'ismap', 'itemprop', 'keytype', 'kind', 'label', 'lang', 'language', 'list', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'mozallowfullscreen', 'multiple', 'name', 'novalidate', 'open', 'optimum', 'pattern', 'ping', 'placeholder', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'reversed', 'rows', 'rowspan', 'sandbox', 'scope', 'scoped', 'scrolling', 'seamless', 'selected', 'shape', 'size', 'sizes', 'span', 'src', 'srcdoc', 'srclang', 'srcset', 'start', 'step', 'summary', 'spellcheck', 'style', 'tabindex', 'target', 'title', 'type', 'translate', 'usemap', 'value', 'valign', 'webkitallowfullscreen', 'width', 'wrap'],
    htmlAllowComments: true,
    fullPage: false // Will also turn iframe on.
  });

  $.FE.HTML5Map = {
    'B': 'STRONG',
    'I': 'EM',
    'STRIKE': 'S'
  },

  $.FE.MODULES.clean = function (editor) {
    var $iframe, body;
    var allowedTagsRE, removeTagsRE, allowedAttrsRE;

    function _removeInvisible (node) {
      if (node.className && node.className.indexOf('fr-marker') >= 0) return false;

      // Get node contents.
      var contents = editor.node.contents(node);
      var markers = [];
      var i;

      // Loop through contents.
      for (i = 0; i < contents.length; i++) {
        // If node is not void.
        if (contents[i].nodeType == Node.ELEMENT_NODE && !editor.node.isVoid(contents[i])) {
          // There are invisible spaces.
          if (contents[i].textContent.replace(/\u200b/g, '').length != contents[i].textContent.length) {
            // Do remove invisible spaces.
            _removeInvisible(contents[i]);
          }
        }

        // If node is text node, replace invisible spaces.
        else if (contents[i].nodeType == Node.TEXT_NODE) {
          contents[i].textContent = contents[i].textContent.replace(/\u200b/g, '');
        }
      }

      // Reasess contents after cleaning invisible spaces.
      if (node.nodeType == Node.ELEMENT_NODE && !editor.node.isVoid(node)) {
        node.normalize();
        contents = editor.node.contents(node);
        markers = node.querySelectorAll('.fr-marker');

        // All we have left are markers.
        if (contents.length - markers.length == 0) {
          // Make sure contents are all markers.
          for (i = 0; i < contents.length; i++) {
            if ((contents[i].className || '').indexOf('fr-marker') < 0) {
              return false;
            }
          }

          for (i = 0; i < markers.length; i++) {
            node.parentNode.insertBefore(markers[i].cloneNode(true), node);
          }
          node.parentNode.removeChild(node);
          return false;
        }
      }
    }

    function _toHTML (el) {
      if (el.nodeType == Node.COMMENT_NODE) return '<!--' + el.nodeValue + '-->';
      if (el.nodeType == Node.TEXT_NODE) return el.textContent.replace(/\</g, '&lt;').replace(/\>/g, '&gt;').replace(/\u00A0/g, '&nbsp;');
      if (el.nodeType != Node.ELEMENT_NODE) return el.outerHTML;
      if (el.nodeType == Node.ELEMENT_NODE && ['STYLE', 'SCRIPT'].indexOf(el.tagName) >= 0) return el.outerHTML;
      if (el.tagName == 'IFRAME') return el.outerHTML;

      var contents = el.childNodes;

      if (contents.length === 0) return el.outerHTML;

      var str = '';
      for (var i = 0; i < contents.length; i++) {
        str += _toHTML(contents[i]);
      }

      return editor.node.openTagString(el) + str + editor.node.closeTagString(el);
    }

    var scripts = [];
    function _encode (dirty_html) {
      // Replace script tag with comments.
      scripts = [];
      dirty_html = dirty_html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, function (str) {
        scripts.push(str);
        return '[FROALA.EDITOR.SCRIPT ' + (scripts.length - 1) + ']';
      });

      dirty_html = dirty_html.replace(/<img((?:[\w\W]*?)) src="/g, '<img$1 data-fr-src="');

      return dirty_html;
    }

    function _decode (dirty_html) {
      // Replace script comments with the original script.
      dirty_html = dirty_html.replace(/\[FROALA\.EDITOR\.SCRIPT ([\d]*)\]/gi, function (str, a1) {
        if (editor.opts.htmlRemoveTags.indexOf('script') >= 0) {
          return '';
        }
        else {
          return scripts[parseInt(a1, 10)];
        }
      });

      dirty_html = dirty_html.replace(/<img((?:[\w\W]*?)) data-fr-src="/g, '<img$1 src="');

      return dirty_html;
    }

    function _cleanAttrs (attrs) {
      var nm;

      for (nm in attrs) {
        if (attrs.hasOwnProperty(nm)) {
          if (!nm.match(allowedAttrsRE)) {
            delete attrs[nm];
          }
        }
      }

      var str = '';
      var keys = Object.keys(attrs).sort();
      for (var i = 0; i < keys.length; i++) {
        nm = keys[i];

        // Make sure we don't break any HTML.
        if (attrs[nm].indexOf('"') < 0) {
          str += ' ' + nm + '="' + attrs[nm] + '"';
        }
        else {
          str += ' ' + nm + '=\'' + attrs[nm] + '\'';
        }
      }

      return str;
    }

    function _rebuild (body_html, head_html, original_html) {
      if (editor.opts.fullPage) {
        // Get DOCTYPE.
        var doctype = editor.html.extractDoctype(original_html);

        // Get HTML attributes.
        var html_attrs = _cleanAttrs(editor.html.extractNodeAttrs(original_html, 'html'));

        // Get HEAD data.
        head_html = head_html == null ? editor.html.extractNode(original_html, 'head') || '<title></title>' : head_html;
        var head_attrs = _cleanAttrs(editor.html.extractNodeAttrs(original_html, 'head'));

        // Get BODY attributes.
        var body_attrs = _cleanAttrs(editor.html.extractNodeAttrs(original_html, 'body'));

        return doctype + '<html' + html_attrs + '><head' + head_attrs + '>' + head_html + '</head><body' + body_attrs + '>' + body_html + '</body></html>';
      }

      return body_html;
    }

    function _process (html, func) {
      var $el = $('<div>' + html + '</div>');

      var new_html = '';
      if ($el) {
        var els = editor.node.contents($el.get(0));
        for (var i = 0; i < els.length; i++) {
          func(els[i]);
        }

        els = editor.node.contents($el.get(0));
        for (var i = 0; i < els.length; i++) {
          new_html += _toHTML(els[i]);
        }
      }

      return new_html;
    }

    function exec (html, func, parse_head) {
      html = _encode(html);

      var b_html = html;
      var h_html = null;
      if (editor.opts.fullPage) {
        // Get BODY data.
        var b_html = (editor.html.extractNode(html, 'body') || (html.indexOf('<body') >= 0 ? '' : html));

        if (parse_head) {
          h_html = (editor.html.extractNode(html, 'head') || '');
        }
      }

      b_html = _process(b_html, func);
      if (h_html) h_html = _process(h_html, func);

      var new_html = _rebuild(b_html, h_html, html);

      return _decode(new_html);
    }

    function invisibleSpaces (dirty_html) {
      if (dirty_html.replace(/\u200b/g, '').length == dirty_html.length) return dirty_html;

      return editor.clean.exec(dirty_html, _removeInvisible);
    }

    function toHTML5 () {
      var els = editor.$el.get(0).querySelectorAll(Object.keys($.FE.HTML5Map).join(','));
      if (els.length) {
        editor.selection.save();
        for (var i = 0; i < els.length; i++) {
          if (editor.node.attributes(els[i]) === '') {
             $(els[i]).replaceWith('<' + $.FE.HTML5Map[els[i].tagName] + '>' + els[i].innerHTML + '</' + $.FE.HTML5Map[els[i].tagName] + '>');
          }
        }
        editor.selection.restore();
      }
    }

    function _node (node) {
      // Skip when we're dealing with markers.
      if (node.tagName == 'SPAN' && (node.className || '').indexOf('fr-marker') >=0) return false;

      if (node.tagName == 'PRE') _cleanPre(node);

      if (node.nodeType == Node.ELEMENT_NODE) {
        if (node.getAttribute('data-fr-src')) node.setAttribute('data-fr-src', editor.helpers.sanitizeURL(node.getAttribute('data-fr-src')));
        if (node.getAttribute('href')) node.setAttribute('href', editor.helpers.sanitizeURL(node.getAttribute('href')));

        if (['TABLE', 'TBODY', 'TFOOT', 'TR'].indexOf(node.tagName) >= 0) {
          node.innerHTML = node.innerHTML.trim();
        }
      }

      // Remove local images if option they are not allowed.
      if (!editor.opts.pasteAllowLocalImages && node.nodeType == Node.ELEMENT_NODE && node.tagName == 'IMG' && node.getAttribute('data-fr-src') && node.getAttribute('data-fr-src').indexOf('file://') == 0) {
        node.parentNode.removeChild(node);
        return false;
      }

      if (node.nodeType == Node.ELEMENT_NODE && $.FE.HTML5Map[node.tagName] && editor.node.attributes(node) === '') {
        var tg = $.FE.HTML5Map[node.tagName];
        var new_node = '<' + tg + '>' + node.innerHTML + '</' + tg + '>';
        node.insertAdjacentHTML('beforebegin', new_node);
        node = node.previousSibling;
        node.parentNode.removeChild(node.nextSibling);
      }

      if (!editor.opts.htmlAllowComments && node.nodeType == Node.COMMENT_NODE) {
        // Do not remove FROALA.EDITOR comments.
        if (node.data.indexOf('[FROALA.EDITOR') !== 0) {
          node.parentNode.removeChild(node);
        }
      }

      // Remove completely tags in denied tags.
      else if (node.tagName && node.tagName.match(removeTagsRE)) {
        node.parentNode.removeChild(node);
      }

      // Unwrap tags not in allowed tags.
      else if (node.tagName && !node.tagName.match(allowedTagsRE)) {
        node.outerHTML = node.innerHTML;
      }

      // Check denied attributes.
      else {
        var attrs = node.attributes;
        if (attrs) {
          for (var i = attrs.length - 1; i >= 0; i--) {
            var attr = attrs[i];
            if (!attr.nodeName.match(allowedAttrsRE)) {
              node.removeAttribute(attr.nodeName);
            }
          }
        }
      }
    }

    function _run (node) {
      var contents = editor.node.contents(node);
      for (var i = 0; i < contents.length; i++) {
        if (contents[i].nodeType != Node.TEXT_NODE) {
          _run(contents[i]);
        }
      }

      _node(node);
    }

    /**
     * Clean pre.
     */
    function _cleanPre (pre) {
      var content = pre.innerHTML;
      if (content.indexOf('\n') >= 0) {
        pre.innerHTML = content.replace(/\n/g, '<br>');
      }
    }

    /**
     * Clean the html input.
     */
    var scripts = [];
    function html (dirty_html, denied_tags, denied_attrs, full_page) {
      if (typeof denied_tags == 'undefined') denied_tags = [];
      if (typeof denied_attrs == 'undefined') denied_attrs = [];
      if (typeof full_page == 'undefined') full_page = false;

      // Strip tabs.
      dirty_html = dirty_html.replace(/\u0009/g, '');

      // Build the allowed tags array.
      var allowed_tags = $.merge([], editor.opts.htmlAllowedTags);
      var i;
      for (i = 0; i < denied_tags.length; i++) {
        if (allowed_tags.indexOf(denied_tags[i]) >= 0) {
          allowed_tags.splice(allowed_tags.indexOf(denied_tags[i]), 1);
        }
      }

      // Build the allowed attrs array.
      var allowed_attrs = $.merge([], editor.opts.htmlAllowedAttrs);
      for (i = 0; i < denied_attrs.length; i++) {
        if (allowed_attrs.indexOf(denied_attrs[i]) >= 0) {
          allowed_attrs.splice(allowed_attrs.indexOf(denied_attrs[i]), 1);
        }
      }

      // We should allow data-fr.
      allowed_attrs.push('data-fr-.*');
      allowed_attrs.push('fr-.*');

      // Generate cleaning RegEx.
      allowedTagsRE = new RegExp('^' + allowed_tags.join('$|^') + '$', 'gi');
      allowedAttrsRE = new RegExp('^' + allowed_attrs.join('$|^') + '$', 'gi');
      removeTagsRE = new RegExp('^' + editor.opts.htmlRemoveTags.join('$|^') + '$', 'gi');

      dirty_html = exec(dirty_html, _run, true);

      return dirty_html;
    }

    /**
     * Clean quotes.
     */
    function quotes () {
      // Join quotes.
      var sibling_quotes = editor.$el.get(0).querySelectorAll('blockquote + blockquote');
      for (var k = 0; k < sibling_quotes.length; k++) {
        var quote = sibling_quotes[k];
        if (editor.node.attributes(quote) == editor.node.attributes(quote.previousSibling)) {
          $(quote).prev().append($(quote).html());
          $(quote).remove();
        }
      }
    }

    function _tablesWrapTHEAD () {
      var trs = editor.$el.get(0).querySelectorAll('tr');

      // Make sure the TH lives inside thead.
      for (var i = 0; i < trs.length; i++) {
        // Search for th inside tr.
        var children = trs[i].children;
        var ok = true;
        for (var j = 0; j < children.length; j++) {
          if (children[j].tagName != 'TH') {
            ok = false;
            break;
          }
        }

        // If there is something else than TH.
        if (ok == false || children.length == 0) continue;

        var tr = trs[i];

        while (tr && tr.tagName != 'TABLE' && tr.tagName != 'THEAD') {
          tr = tr.parentNode;
        }

        var thead = tr;
        if (thead.tagName != 'THEAD') {
          thead = editor.doc.createElement('THEAD');
          tr.insertBefore(thead, tr.firstChild);
        }

        thead.appendChild(trs[i]);
      }
    }

    function _tablesBRBefore () {
      // Make sure we have a br before tables.
      var tbls = editor.$el.get(0).querySelectorAll('table');
      for (var i = 0; i < tbls.length; i++) {
        var prev_node = tbls[i].previousSibling;

        // Get previous sibling.
        while (prev_node && prev_node.nodeType == Node.TEXT_NODE && prev_node.textContent.length == 0) {
          prev_node = prev_node.previousSibling;
        }

        if (prev_node && !editor.node.isBlock(prev_node) && prev_node.tagName != 'BR' && (prev_node.nodeType == Node.TEXT_NODE || prev_node.nodeType == Node.ELEMENT_NODE) && !$(prev_node).is(editor.opts.htmlDoNotWrapTags.join(','))) {
          tbls[i].parentNode.insertBefore(editor.doc.createElement('br'), tbls[i]);
        }
      }
    }

    function _tablesRemovePFromCell () {
      // Remove P from TH and TH.
      var default_tag = editor.html.defaultTag();
      if (default_tag) {
        var nodes = editor.$el.get(0).querySelectorAll('td > ' + default_tag + ', th > ' + default_tag);
        for (var i = 0; i < nodes.length; i++) {
          if (editor.node.attributes(nodes[i]) === '') {
            $(nodes[i]).replaceWith(nodes[i].innerHTML + '<br>');
          }
        }
      }
    }

    /**
     * Clean tables.
     */
    function tables () {
      _tablesWrapTHEAD();

      _tablesBRBefore();

      _tablesRemovePFromCell();
    }

    function _listsWrapMissplacedLI () {
      // Find missplaced list items.
      var lis = [];
      var filterListItem = function (li) {
        return !editor.node.isList(li.parentNode);
      };

      do {
        if (lis.length) {
          var li = lis[0];
          var ul = editor.doc.createElement('ul');
          li.parentNode.insertBefore(ul, li);
          do {
            var tmp = li;
            li = li.nextSibling;
            ul.appendChild(tmp);
          } while (li && li.tagName == 'LI');
        }

        lis = [];
        var li_sel = editor.$el.get(0).querySelectorAll('li');
        for (var i = 0; i < li_sel.length; i++) {
          if (filterListItem(li_sel[i])) lis.push(li_sel[i]);
        }
      } while (lis.length > 0);
    }

    function _listsJoinSiblings () {
      // Join lists.
      var sibling_lists = editor.$el.get(0).querySelectorAll('ol + ol, ul + ul');
      for (var k = 0; k < sibling_lists.length; k++) {
        var list = sibling_lists[k];
        if (editor.node.isList(list.previousSibling) && editor.node.openTagString(list) == editor.node.openTagString(list.previousSibling)) {
          var childs = editor.node.contents(list);
          for (var i = 0; i < childs.length; i++) {
            list.previousSibling.appendChild(childs[i]);
          }
          list.parentNode.removeChild(list);
        }
      }
    }

    function _listsRemoveEmpty () {
      // Remove empty lists.
      var do_remove;
      var removeEmptyList = function (lst) {
        if (lst.querySelectorAll('LI').length === 0) {
          do_remove = true;
          lst.parentNode.removeChild(lst);
        }
      };

      do {
        do_remove = false;

        // Remove empty li.
        var empty_lis = editor.$el.get(0).querySelectorAll('li:empty');
        for (var i = 0; i < empty_lis.length; i++) {
          empty_lis[i].parentNode.removeChild(empty_lis[i]);
        }

        // Remove empty ul and ol.
        var remaining_lists = editor.$el.get(0).querySelectorAll('ul, ol');
        for (var i = 0; i < remaining_lists.length; i++) {
          removeEmptyList(remaining_lists[i]);
        }
      } while (do_remove === true);
    }

    function _listsWrapLists () {
      // Do not allow list directly inside another list.
      var direct_lists = editor.$el.get(0).querySelectorAll('ul > ul, ol > ol, ul > ol, ol > ul');
      for (var i = 0; i < direct_lists.length; i++) {
        var list = direct_lists[i];
        var prev_li = list.previousSibling;
        if (prev_li) {
          if (prev_li.tagName == 'LI') {
            prev_li.appendChild(list);
          }
          else {
            $(list).wrap('<li></li>');
          }
        }
      }
    }

    function _listsNoTagAfterNested () {
      // Check if nested lists don't have HTML after them.
      var nested_lists = editor.$el.get(0).querySelectorAll('li > ul, li > ol');
      for (var i = 0; i < nested_lists.length; i++) {
        var lst = nested_lists[i];

        if (lst.nextSibling) {
          var node = lst.nextSibling;
          var $new_li = $('<li>');
          $(lst.parentNode).after($new_li);
          do {
            var tmp = node;
            node = node.nextSibling;
            $new_li.append(tmp);
          } while (node);
        }
      }
    }

    function _listsTypeInNested () {
      // Make sure we can type in nested list.
      var nested_lists = editor.$el.get(0).querySelectorAll('li > ul, li > ol');
      for (var i = 0; i < nested_lists.length; i++) {
        var lst = nested_lists[i];

        // List is the first in the LI.
        if (editor.node.isFirstSibling(lst)) {
          $(lst).before('<br/>');
        }

        // Make sure we don't leave BR before list.
        else if (lst.previousSibling && lst.previousSibling.tagName == 'BR') {
          var prev_node = lst.previousSibling.previousSibling;

          // Skip markers.
          while (prev_node && $(prev_node).hasClass('fr-marker')) {
            prev_node = prev_node.previousSibling;
          }

          // Remove BR only if there is something else than BR.
          if (prev_node && prev_node.tagName != 'BR') {
            $(lst.previousSibling).remove();
          }
        }
      }
    }

    function _listsRemoveEmptyLI () {
      // Remove empty li.
      var empty_lis = editor.$el.get(0).querySelectorAll('li:empty');
      for (var i = 0; i < empty_lis.length; i++) {
        $(empty_lis[i]).remove();
      }
    }

    function _listsFindMissplacedText () {
      var lists = editor.$el.get(0).querySelectorAll('ul, ol');
      for (var i = 0; i < lists.length; i++) {
        var contents = editor.node.contents(lists[i]);

        var $li = null;
        for (var j = contents.length - 1; j >=0 ; j--) {
          if (contents[j].tagName != 'LI') {
            if (!$li) {
              $li = $('<li>');
              $li.insertBefore(contents[j]);
            }

            $li.append(contents[j]);
          }
          else {
            $li = null;
          }
        }
      }
    }

    /**
     * Clean lists.
     */
    function lists () {
      _listsWrapMissplacedLI();

      _listsJoinSiblings();

      _listsRemoveEmpty();

      _listsWrapLists();

      _listsNoTagAfterNested();

      _listsTypeInNested();

      _listsFindMissplacedText();

      _listsRemoveEmptyLI();
    }

    /**
     * Initialize
     */
    function _init () {
      // If fullPage is on allow head and title.
      if (editor.opts.fullPage) {
        $.merge(editor.opts.htmlAllowedTags, ['head', 'title', 'style', 'link', 'base', 'body', 'html']);
      }
    }

    return {
      _init: _init,
      html: html,
      toHTML5: toHTML5,
      tables: tables,
      lists: lists,
      quotes: quotes,
      invisibleSpaces: invisibleSpaces,
      exec: exec
    }
  };


  $.FE.XS = 0;
  $.FE.SM = 1;
  $.FE.MD = 2;
  $.FE.LG = 3;

  $.FE.MODULES.helpers = function (editor) {
    /**
     * Get the IE version.
     */
    function _ieVersion () {
      /*global navigator */
      var rv = -1;
      var ua;
      var re;

      if (navigator.appName == 'Microsoft Internet Explorer') {
        ua = navigator.userAgent;
        re = new RegExp('MSIE ([0-9]{1,}[\\.0-9]{0,})');
        if (re.exec(ua) !== null)
          rv = parseFloat(RegExp.$1);
      } else if (navigator.appName == 'Netscape') {
        ua = navigator.userAgent;
        re = new RegExp('Trident/.*rv:([0-9]{1,}[\\.0-9]{0,})');
        if (re.exec(ua) !== null)
          rv = parseFloat(RegExp.$1);
      }
      return rv;
    }

    /**
     * Determine the browser.
     */
    function _browser () {
      var browser = {};

      var ie_version = _ieVersion();
      if (ie_version > 0) {
        browser.msie = true;
      } else {
        var ua = navigator.userAgent.toLowerCase();

        var match =
          /(edge)[ \/]([\w.]+)/.exec(ua) ||
          /(chrome)[ \/]([\w.]+)/.exec(ua) ||
          /(webkit)[ \/]([\w.]+)/.exec(ua) ||
          /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
          /(msie) ([\w.]+)/.exec(ua) ||
          ua.indexOf('compatible') < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
          [];

        var matched = {
          browser: match[1] || '',
          version: match[2] || '0'
        };

        if (match[1]) browser[matched.browser] = true;

        // Chrome is Webkit, but Webkit is also Safari.
        if (browser.chrome) {
          browser.webkit = true;
        } else if (browser.webkit) {
          browser.safari = true;
        }
      }

      if (browser.msie) browser.version = ie_version;

      return browser;
    }

    function isIOS () {
      return /(iPad|iPhone|iPod)/g.test(navigator.userAgent) && !isWindowsPhone();
    }

    function isAndroid () {
      return /(Android)/g.test(navigator.userAgent) && !isWindowsPhone();
    }

    function isBlackberry () {
      return /(Blackberry)/g.test(navigator.userAgent);
    }

    function isWindowsPhone () {
      return /(Windows Phone)/gi.test(navigator.userAgent);
    }

    function isMobile () {
      return isAndroid() || isIOS() || isBlackberry();
    }

    function requestAnimationFrame () {
      return window.requestAnimationFrame ||
              window.webkitRequestAnimationFrame ||
              window.mozRequestAnimationFrame    ||
              function (callback) {
                window.setTimeout(callback, 1000 / 60);
              };
    }

    function getPX (val) {
      return parseInt(val, 10) || 0;
    }

    function screenSize () {
      var $test = $('<div class="fr-visibility-helper"></div>').appendTo('body');
      var size = getPX($test.css('margin-left'));
      $test.remove();
      return size;
    }

    function isTouch () {
      return ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch;
    }

    function isURL (url) {
      if (!/^(https?:|ftps?:|)\/\//i.test(url)) return false;

      url = String(url)
              .replace(/</g, '%3C')
              .replace(/>/g, '%3E')
              .replace(/"/g, '%22')
              .replace(/ /g, '%20');


      var test_reg = /(http|ftp|https):\/\/[a-z\u00a1-\uffff0-9]+(\.[a-z\u00a1-\uffff0-9]*)*([a-z\u00a1-\uffff0-9.,@?^=%&amp;:\/~+#-]*[a-z\u00a1-\uffff0-9@?^=%&amp;\/~+#-])?/gi;

      return test_reg.test(url);
    }

    // Sanitize URL.
    function sanitizeURL (url) {
      if (/^(https?:|ftps?:|)\/\//i.test(url)) {
        if (!isURL(url) && !isURL('http:' + url)) {
          return '';
        }
      }
      else {
        url = encodeURIComponent(url)
                  .replace(/%23/g, '#')
                  .replace(/%2F/g, '/')
                  .replace(/%25/g, '%')
                  .replace(/mailto%3A/gi, 'mailto:')
                  .replace(/file%3A/gi, 'file:')
                  .replace(/sms%3A/gi, 'sms:')
                  .replace(/tel%3A/gi, 'tel:')
                  .replace(/notes%3A/gi, 'notes:')
                  .replace(/data%3Aimage/gi, 'data:image')
                  .replace(/blob%3A/gi, 'blob:')
                  .replace(/webkit-fake-url%3A/gi, 'webkit-fake-url:')
                  .replace(/%3F/g, '?')
                  .replace(/%3D/g, '=')
                  .replace(/%26/g, '&')
                  .replace(/&amp;/g, '&')
                  .replace(/%2C/g, ',')
                  .replace(/%3B/g, ';')
                  .replace(/%2B/g, '+')
                  .replace(/%40/g, '@')
                  .replace(/%5B/g, '[')
                  .replace(/%5D/g, ']')
                  .replace(/%7B/g, '{')
                  .replace(/%7D/g, '}');
      }

      return url;
    }

    function isArray (obj) {
      return obj && !(obj.propertyIsEnumerable('length')) &&
              typeof obj === 'object' && typeof obj.length === 'number';
    }

    /*
     * Transform RGB color to hex value.
     */
    function RGBToHex (rgb) {
      function hex(x) {
        return ('0' + parseInt(x, 10).toString(16)).slice(-2);
      }

      try {
        if (!rgb || rgb === 'transparent') return '';

        if (/^#[0-9A-F]{6}$/i.test(rgb)) return rgb;

        rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);

        return ('#' + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3])).toUpperCase();
      }
      catch (ex) {
        return null;
      }
    }

    function HEXtoRGB (hex) {
      // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
      var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
      hex = hex.replace(shorthandRegex, function (m, r, g, b) {
        return r + r + g + g + b + b;
      });

      var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
      return result ? 'rgb(' + parseInt(result[1], 16) + ', ' + parseInt(result[2], 16) + ', ' + parseInt(result[3], 16) + ')' : '';
    }

    /*
     * Get block alignment.
     */
    var default_alignment;
    function getAlignment ($block) {
      var alignment = ($block.css('text-align') || '').replace(/-(.*)-/g, '');

      // Detect rtl.
      if (['left', 'right', 'justify', 'center'].indexOf(alignment) < 0) {
        if (!default_alignment) {
          var $div = $('<div dir="auto" style="text-align: initial; position: fixed; left: -3000px;"><span id="s1">.</span><span id="s2">.</span></div>');
          $('body').append($div);

          var l1 = $div.find('#s1').get(0).getBoundingClientRect().left;
          var l2 = $div.find('#s2').get(0).getBoundingClientRect().left;

          $div.remove();

          default_alignment = l1 < l2 ? 'left' : 'right';
        }

        alignment = default_alignment;
      }

      return alignment;
    }

    /**
     * Check if is mac.
     */
    var is_mac = null;
    function isMac () {
      if (is_mac == null) {
        is_mac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;;
      }

      return is_mac;
    }

    /**
     * Tear up.
     */
    function _init () {
      editor.browser = _browser();
    }

    return {
      _init: _init,
      isIOS: isIOS,
      isMac: isMac,
      isAndroid: isAndroid,
      isBlackberry: isBlackberry,
      isWindowsPhone: isWindowsPhone,
      isMobile: isMobile,
      requestAnimationFrame: requestAnimationFrame,
      getPX: getPX,
      screenSize: screenSize,
      isTouch: isTouch,
      sanitizeURL: sanitizeURL,
      isArray: isArray,
      RGBToHex: RGBToHex,
      HEXtoRGB: HEXtoRGB,
      isURL: isURL,
      getAlignment: getAlignment
    }
  }


  $.FE.MODULES.events = function (editor) {
    var _events = {};
    var _do_blur;

    function _assignEvent($el, evs, handler) {
      $on($el, evs, handler);
    }

    function _forPaste () {
      _assignEvent(editor.$el, 'cut copy paste beforepaste', function (e) {
        trigger(e.type, [e]);
      });
    }

    function _forElement() {
      _assignEvent(editor.$el, 'click mouseup mousedown touchstart touchend dragenter dragover dragleave dragend drop dragstart', function (e) {
        trigger(e.type, [e]);
      });

      on('mousedown', function () {
        for (var i = 0; i < $.FE.INSTANCES.length; i++) {
          if ($.FE.INSTANCES[i] != editor && $.FE.INSTANCES[i].popups && $.FE.INSTANCES[i].popups.areVisible()) {
            $.FE.INSTANCES[i].$el.find('.fr-marker').remove();
          }
        }
      })
    }

    function _forKeys () {
      // Map events.
      _assignEvent(editor.$el, 'keydown keypress keyup input', function (e) {
        trigger(e.type, [e]);
      });
    }

    function _forWindow () {
      _assignEvent(editor.$win, editor._mousedown, function (e) {
        trigger('window.mousedown', [e]);
        enableBlur();
      });

      _assignEvent(editor.$win, editor._mouseup, function (e) {
        trigger('window.mouseup', [e]);
      });

      _assignEvent(editor.$win, 'cut copy keydown keyup touchmove touchend', function (e) {
        trigger('window.' + e.type, [e]);
      });
    }

    function _forDocument() {
      _assignEvent(editor.$doc, 'dragend drop', function (e) {
        trigger('document.' + e.type, [e]);
      })
    }

    function focus (do_focus) {
      if (typeof do_focus == 'undefined') do_focus = true;
      if (!editor.$wp) return false;

      // Focus the editor window.
      if (editor.helpers.isIOS()) {
        editor.$win.get(0).focus();
      }

      // If there is no focus, then force focus.
      if (!editor.core.hasFocus() && do_focus) {
        var st = editor.$win.scrollTop();
        editor.$el.focus();
        if (st != editor.$win.scrollTop()) {
          editor.$win.scrollTop(st);
        }
        return false;
      }

      // Don't go further if we haven't focused or there are markers.
      if (!editor.core.hasFocus() || editor.$el.find('.fr-marker').length > 0) {
        return false;
      }

      var info = editor.selection.info(editor.$el.get(0));

      if (info.atStart && editor.selection.isCollapsed()) {
        if (editor.html.defaultTag() != null) {
          var marker = editor.markers.insert();
          if (marker && !editor.node.blockParent(marker)) {
            $(marker).remove();

            var element = editor.$el.find(editor.html.blockTagsQuery()).get(0);
            if (element) {
              $(element).prepend($.FE.MARKERS);
              editor.selection.restore();
            }
          }
          else if (marker) {
            $(marker).remove();
          }
        }
      }
    }

    var focused = false;

    function _forFocus () {
      _assignEvent(editor.$el, 'focus', function (e) {
        if (blurActive()) {
          focus(false);
          if (focused === false) {
            trigger(e.type, [e]);
          }
        }
      });

      _assignEvent(editor.$el, 'blur', function (e) {
        if (blurActive() /* && document.activeElement != this */) {
          if (focused === true) {
            trigger(e.type, [e]);
            enableBlur();
          }
        }
      });

      on('focus', function () {
        focused = true;
      });

      on('blur', function () {
        focused = false;
      });
    }

    function _forMouse () {
      if (editor.helpers.isMobile()) {
        editor._mousedown = 'touchstart';
        editor._mouseup = 'touchend';
        editor._move = 'touchmove';
        editor._mousemove = 'touchmove';
      }
      else {
        editor._mousedown = 'mousedown';
        editor._mouseup = 'mouseup';
        editor._move = '';
        editor._mousemove = 'mousemove';
      }
    }

    function _buttonMouseDown (e) {
      var $btn = $(e.currentTarget);
      if (editor.edit.isDisabled() || $btn.hasClass('fr-disabled')) {
        e.preventDefault();
        return false;
      }

      // Not click button.
      if (e.type === 'mousedown' && e.which !== 1) return true;

      // Scroll in list.
      if (!editor.helpers.isMobile()) {
        e.preventDefault();
      }

      if ((editor.helpers.isAndroid() || editor.helpers.isWindowsPhone()) && $btn.parents('.fr-dropdown-menu').length === 0) {
        e.preventDefault();
        e.stopPropagation();
      }

      // Simulate click.
      $btn.addClass('fr-selected');

      editor.events.trigger('commands.mousedown', [$btn]);
    }

    function _buttonMouseUp (e, handler) {
      var $btn = $(e.currentTarget);

      if (editor.edit.isDisabled() || $btn.hasClass('fr-disabled')) {
        e.preventDefault();
        return false;
      }

      if (e.type === 'mouseup' && e.which !== 1) return true;
      if (!$btn.hasClass('fr-selected')) return true;

      if (e.type != 'touchmove') {
        e.stopPropagation();
        e.stopImmediatePropagation();
        e.preventDefault();

        // Simulate click.
        if (!$btn.hasClass('fr-selected')) {
          $('.fr-selected').removeClass('fr-selected');
          return false;
        }
        $('.fr-selected').removeClass('fr-selected');

        if ($btn.data('dragging') || $btn.attr('disabled')) {
          $btn.removeData('dragging');
          return false;
        }

        var timeout = $btn.data('timeout');
        if (timeout) {
          clearTimeout(timeout);
          $btn.removeData('timeout');
        }

        handler.apply(editor, [e]);
      }
      else {
        if (!$btn.data('timeout')) {
          $btn.data('timeout', setTimeout(function () {
            $btn.data('dragging', true);
          }, 100));
        }
      }
    }

    function enableBlur () {
      _do_blur = true;
    }

    function disableBlur () {
      _do_blur = false;
    }

    function blurActive () {
      return _do_blur;
    }

    /**
     * Bind click on an element.
     */
    function bindClick ($element, selector, handler) {
      $on($element, editor._mousedown, selector, function (e) {
        if (!editor.edit.isDisabled()) _buttonMouseDown(e);
      }, true);

      $on($element, editor._mouseup + ' ' + editor._move, selector, function (e) {
        if (!editor.edit.isDisabled()) _buttonMouseUp(e, handler);
      }, true);

      $on($element, 'mousedown click mouseup', selector, function (e) {
        if (!editor.edit.isDisabled()) e.stopPropagation();
      }, true);

      on('window.mouseup', function () {
        if (!editor.edit.isDisabled()) {
          $element.find(selector).removeClass('fr-selected');
          enableBlur();
        }
      });
    }

    /**
     * Add event.
     */
    function on (name, callback, first) {
      var names = name.split(' ');
      if (names.length > 1) {
        for (var i = 0; i < names.length; i++) {
          on(names[i], callback, first);
        }

        return true;
      }

      if (typeof first == 'undefined') first = false;

      var callbacks;
      if (name.indexOf('shared.') != 0) {
        callbacks = (_events[name] = _events[name] || []);
      }
      else {
        callbacks = (editor.shared._events[name] = editor.shared._events[name] || []);
      }

      if (first) {
        callbacks.unshift(callback);
      }
      else {
        callbacks.push(callback);
      }
    }

    var $_events = [];
    function $on ($el, evs, selector, callback, shared) {
      if (typeof selector == 'function') {
        shared = callback;
        callback = selector;
        selector = false;
      }

      var ary = (!shared ? $_events : editor.shared.$_events);
      var id = (!shared ? editor.id : editor.sid);

      if (!selector) {
        $el.on(evs.split(' ').join('.ed' + id + ' ') + '.ed' + id, callback);
      }
      else {
        $el.on(evs.split(' ').join('.ed' + id + ' ') + '.ed' + id, selector, callback);
      }

      if (ary.indexOf($el.get(0)) < 0) ary.push($el.get(0));
    }

    function _$off (evs, id) {
      for (var i = 0; i < evs.length; i++) {
        $(evs[i]).off('.ed' + id);
      }
    }

    function $off () {
      _$off($_events, editor.id);
      $_events = [];

      if (editor.shared.count == 0) {
        _$off(editor.shared.$_events, editor.sid);
        editor.shared.$_events = null;
      }
    }

    /**
     * Trigger an event.
     */
    function trigger (name, args, force) {
      if (!editor.edit.isDisabled() || force) {
        var callbacks;

        if (name.indexOf('shared.') != 0) {
          callbacks = _events[name];
        }
        else {
          if (editor.shared.count > 0) return false;
          callbacks = editor.shared._events[name];
        }

        var val;

        if (callbacks) {
          for (var i = 0; i < callbacks.length; i++) {
            val = callbacks[i].apply(editor, args);
            if (val === false) return false;
          }
        }

        // Trigger event outside.
        val = editor.$oel.triggerHandler('froalaEditor.' + name, $.merge([editor], (args || [])));
        if (val === false) return false;

        return val;
      }
    }

    function chainTrigger (name, param, force) {
      if (!editor.edit.isDisabled() || force) {
        var callbacks;

        if (name.indexOf('shared.') != 0) {
          callbacks = _events[name];
        }
        else {
          if (editor.shared.count > 0) return false;
          callbacks = editor.shared._events[name];
        }

        var resp;

        if (callbacks) {
          for (var i = 0; i < callbacks.length; i++) {
            // Get the callback response.
            resp = callbacks[i].apply(editor, [param]);

            // If callback response is defined then assign it to param.
            if (typeof resp !== 'undefined') param = resp;
          }
        }

        // Trigger event outside.
        resp = editor.$oel.triggerHandler('froalaEditor.' + name, $.merge([editor], [param]));

        // If callback response is defined then assign it to param.
        if (typeof resp !== 'undefined') param = resp;

        return param;
      }
    }

    /**
     * Destroy
     */
    function _destroy () {
      // Clear the events list.
      for (var k in _events) {
        if (_events.hasOwnProperty(k)) {
          delete _events[k];
        }
      }
    }

    function _sharedDestroy () {
      for (var k in editor.shared._events) {
        if (editor.shared._events.hasOwnProperty(k)) {
          delete editor.shared._events[k];
        }
      }
    }

    /**
     * Tear up.
     */
    function _init () {
      editor.shared.$_events = editor.shared.$_events || [];
      editor.shared._events = {};

      _forMouse();

      _forElement();
      _forWindow();
      _forDocument();
      _forKeys();

      _forFocus();
      enableBlur();

      _forPaste();

      on('destroy', _destroy);
      on('shared.destroy', _sharedDestroy);
    }

    return {
      _init: _init,
      on: on,
      trigger: trigger,
      bindClick: bindClick,
      disableBlur: disableBlur,
      enableBlur: enableBlur,
      blurActive: blurActive,
      focus: focus,
      chainTrigger: chainTrigger,
      $on: $on,
      $off: $off
    }
  };


  $.FE.INVISIBLE_SPACE = '&#8203;';
  $.FE.START_MARKER = '<span class="fr-marker" data-id="0" data-type="true" style="display: none; line-height: 0;">' + $.FE.INVISIBLE_SPACE + '</span>';
  $.FE.END_MARKER = '<span class="fr-marker" data-id="0" data-type="false" style="display: none; line-height: 0;">'  + $.FE.INVISIBLE_SPACE + '</span>';
  $.FE.MARKERS = $.FE.START_MARKER + $.FE.END_MARKER;

  $.FE.MODULES.markers = function (editor) {
    /**
     * Build marker element.
     */
    function _build (marker, id) {
      return $('<span class="fr-marker" data-id="' + id + '" data-type="' + marker + '" style="display: ' + (editor.browser.safari ? 'none' : 'inline-block') + '; line-height: 0;">' + $.FE.INVISIBLE_SPACE + '</span>', editor.doc)[0];
    }

    /**
     * Place marker.
     */
    function place (range, marker, id) {
      try {
        var boundary = range.cloneRange();
        boundary.collapse(marker);

        boundary.insertNode(_build(marker, id));

        if (marker === true && range.collapsed) {
          var mk = editor.$el.find('span.fr-marker[data-type="true"][data-id="' + id + '"]');
          var sibling = mk.get(0).nextSibling;
          while (sibling && sibling.nodeType === Node.TEXT_NODE && sibling.textContent.length === 0) {
            $(sibling).remove();
            sibling = mk.nextSibling;
          }
        }

        if (marker === true && !range.collapsed) {
          var mk = editor.$el.find('span.fr-marker[data-type="true"][data-id="' + id + '"]').get(0);
          var sibling = mk.nextSibling;
          if (sibling && sibling.nodeType === Node.ELEMENT_NODE && editor.node.isBlock(sibling)) {
            // Place the marker deep inside the block tags.
            var contents = [sibling];
            do {
              sibling = contents[0];
              contents = editor.node.contents(sibling);
            } while (contents[0] && editor.node.isBlock(contents[0]));

            $(sibling).prepend($(mk));
          }
        }

        if (marker === false && !range.collapsed) {
          var mk = editor.$el.find('span.fr-marker[data-type="false"][data-id="' + id + '"]').get(0);
          var sibling = mk.previousSibling;
          if (sibling && sibling.nodeType === Node.ELEMENT_NODE && editor.node.isBlock(sibling)) {
            // Place the marker deep inside the block tags.
            var contents = [sibling];
            do {
              sibling = contents[contents.length - 1];
              contents = editor.node.contents(sibling);
            } while (contents[contents.length - 1] && editor.node.isBlock(contents[contents.length - 1]));

            $(sibling).append($(mk));
          }

          // https://github.com/froala/wysiwyg-editor/issues/705
          if (mk.parentNode && ['TD', 'TH'].indexOf(mk.parentNode.tagName) >= 0) {
            if (mk.parentNode.previousSibling && !mk.previousSibling) {
              $(mk.parentNode.previousSibling).append(mk);
            }
          }
        }

        var dom_marker = editor.$el.find('span.fr-marker[data-type="' + marker + '"][data-id="' + id + '"]').get(0);

        // If image is at the top of the editor in an empty P
        // and floated to right, the text will be pushed down
        // when trying to insert an image.
        if (dom_marker) dom_marker.style.display = 'none';
        return dom_marker;
      }
      catch (ex) {
        return null;
      }
    }

    /**
     * Insert a single marker.
     */
    function insert () {
      if (!editor.$wp) return null;

      try {
        var range = editor.selection.ranges(0);
        var containter = range.commonAncestorContainer;

        // Check if selection is inside editor.
        if (containter != editor.$el.get(0) && editor.$el.find(containter).length == 0) return null;

        var boundary = range.cloneRange();
        var original_range = range.cloneRange();
        boundary.collapse(true);

        var mk = $('<span class="fr-marker" style="display: none; line-height: 0;">' + $.FE.INVISIBLE_SPACE + '</span>', editor.doc)[0];

        boundary.insertNode(mk);

        mk = editor.$el.find('span.fr-marker').get(0);

        if (mk) {
          var sibling = mk.nextSibling;
          while (sibling && sibling.nodeType === Node.TEXT_NODE && sibling.textContent.length === 0) {
            $(sibling).remove();
            sibling = editor.$el.find('span.fr-marker').get(0).nextSibling;
          }

          // Keep original selection.
          editor.selection.clear();
          editor.selection.get().addRange(original_range);

          return mk;
        }
        else {
          return null;
        }
      }
      catch (ex) {
        console.warn ('MARKER', ex)
      }
    }

    /**
     * Split HTML at the marker position.
     */
    function split () {
      if (!editor.selection.isCollapsed()) {
        editor.selection.remove();
      }

      var marker = editor.$el.find('.fr-marker').get(0);
      if (marker == null) marker = insert();
      if (marker == null) return null;

      var deep_parent;
      if ((deep_parent = editor.node.deepestParent(marker))) {
        if (editor.node.isBlock(deep_parent) && editor.node.isEmpty(deep_parent)) {
          $(deep_parent).replaceWith('<span class="fr-marker"></span>');
        }
        else {
          var node = marker;
          var close_str = '';
          var open_str = '';
          do {
            node = node.parentNode;
            close_str = close_str + editor.node.closeTagString(node);
            open_str = editor.node.openTagString(node) + open_str;
          } while (node != deep_parent);

          $(marker).replaceWith('<span id="fr-break"></span>');
          var h = editor.node.openTagString(deep_parent) + $(deep_parent).html() + editor.node.closeTagString(deep_parent);
          h = h.replace(/<span id="fr-break"><\/span>/g, close_str + '<span class="fr-marker"></span>' + open_str);

          $(deep_parent).replaceWith(h);
        }
      }

      return editor.$el.find('.fr-marker').get(0)
    }

    /**
     * Insert marker at point from event.
     *
     * http://stackoverflow.com/questions/11191136/set-a-selection-range-from-a-to-b-in-absolute-position
     * https://developer.mozilla.org/en-US/docs/Web/API/this.document.caretPositionFromPoint
     */
    function insertAtPoint (e) {
      var x = e.clientX;
      var y = e.clientY;

      // Clear markers.
      remove();

      var start;
      var range = null;

      // Default.
      if (typeof editor.doc.caretPositionFromPoint != 'undefined') {
        start = editor.doc.caretPositionFromPoint(x, y);
        range = editor.doc.createRange();

        range.setStart(start.offsetNode, start.offset);
        range.setEnd(start.offsetNode, start.offset);
      }

      // Webkit.
      else if (typeof editor.doc.caretRangeFromPoint != 'undefined') {
        start = editor.doc.caretRangeFromPoint(x, y);
        range = editor.doc.createRange();

        range.setStart(start.startContainer, start.startOffset);
        range.setEnd(start.startContainer, start.startOffset);
      }

      // Set ranges.
      if (range !== null && typeof editor.win.getSelection != 'undefined') {
        var sel = editor.win.getSelection();
        sel.removeAllRanges();
        sel.addRange(range);
      }

      // MSIE.
      else if (typeof editor.doc.body.createTextRange != 'undefined') {
        try {
          range = editor.doc.body.createTextRange();
          range.moveToPoint(x, y);
          var end_range = range.duplicate();
          end_range.moveToPoint(x, y);
          range.setEndPoint('EndToEnd', end_range);
          range.select();
        }
        catch (ex) {
          return false;
        }
      }

      insert();
    }

    /**
     * Remove markers.
     */
    function remove () {
      editor.$el.find('.fr-marker').remove();
    }

    return {
      place: place,
      insert: insert,
      split: split,
      insertAtPoint: insertAtPoint,
      remove: remove
    }
  };


  $.FE.MODULES.selection = function (editor) {
    /**
     * Get selection text.
     */
    function text () {
      var text = '';

      if (editor.win.getSelection) {
        text = editor.win.getSelection();
      } else if (editor.doc.getSelection) {
        text = editor.doc.getSelection();
      } else if (editor.doc.selection) {
        text = editor.doc.selection.createRange().text;
      }

      return text.toString();
    }

    /**
     * Get the selection object.
     */
    function get () {
      var selection = '';
      if (editor.win.getSelection) {
        selection = editor.win.getSelection();
      } else if (editor.doc.getSelection) {
        selection = editor.doc.getSelection();
      } else {
        selection = editor.doc.selection.createRange();
      }

      return selection;
    }

    /**
     * Get the selection ranges or a single range at a specified index.
    */
    function ranges (index) {
      var sel = get();
      var ranges = [];

      // Get ranges.
      if (sel && sel.getRangeAt && sel.rangeCount) {
        var ranges = [];
        for (var i = 0; i < sel.rangeCount; i++) {
          ranges.push(sel.getRangeAt(i));
        }
      }
      else {
        if (editor.doc.createRange) {
          ranges = [editor.doc.createRange()];
        } else {
          ranges = [];
        }
      }

      return (typeof index != 'undefined' ? ranges[index] : ranges);
    }

    /**
     * Clear selection.
     */
    function clear () {
      var sel = get();

      try {
        if (sel.removeAllRanges) {
          sel.removeAllRanges();
        } else if (sel.empty) {  // IE?
          sel.empty();
        } else if (sel.clear) {
          sel.clear();
        }
      }
      catch (ex) {}
    }

    /**
     * Selection element.
    */
    function element () {
      var sel = get();

      try {
        if (sel.rangeCount) {
          var range = ranges(0);
          var node = range.startContainer;

          // https://github.com/froala/wysiwyg-editor/issues/1399.
          if (node.nodeType == Node.TEXT_NODE && range.startOffset == (node.textContent || '').length && node.nextSibling) {
            node = node.nextSibling;
          }

          // Get parrent if node type is not DOM.
          if (node.nodeType == Node.ELEMENT_NODE) {
            var node_found = false;

            // Search for node deeper.
            if (node.childNodes.length > 0 && node.childNodes[range.startOffset]) {
              var child = node.childNodes[range.startOffset];
              while (child && child.nodeType == Node.TEXT_NODE && child.textContent.length == 0) {
                child = child.nextSibling;
              }

              if (child && child.textContent.replace(/\u200B/g, '') === text().replace(/\u200B/g, '')) {
                node = child;
                node_found = true;
              }
            }
            // Selection starts just at the end of the node.
            else if (!range.collapsed && node.nextSibling && node.nextSibling.nodeType == Node.ELEMENT_NODE) {
              var child = node.nextSibling;

              if (child && child.textContent.replace(/\u200B/g, '') === text().replace(/\u200B/g, '')) {
                node = child;
                node_found = true;
              }
            }

            if (!node_found && node.childNodes.length > 0 && $(node.childNodes[0]).text().replace(/\u200B/g, '') === text().replace(/\u200B/g, '') && ['BR', 'IMG', 'HR'].indexOf(node.childNodes[0].tagName) < 0) {
              node = node.childNodes[0];
            }
          }

          while (node.nodeType != Node.ELEMENT_NODE && node.parentNode) {
            node = node.parentNode;
          }

          // Make sure the node is in editor.
          var p = node;
          while (p && p.tagName != 'HTML') {
            if (p == editor.$el.get(0)) {
              return node;
            }

            p = $(p).parent()[0];
          }
        }
      }
      catch (ex) {

      }

      return editor.$el.get(0);
    }

    /**
     * Selection element.
    */
    function endElement () {
      var sel = get();

      try {
        if (sel.rangeCount) {
          var range = ranges(0);
          var node = range.endContainer;

          // Get parrent if node type is not DOM.
          if (node.nodeType == Node.ELEMENT_NODE) {
            var node_found = false;

            // Search for node deeper.
            if (node.childNodes.length > 0 && node.childNodes[range.endOffset] && $(node.childNodes[range.endOffset]).text() === text()) {
              node = node.childNodes[range.endOffset];
              node_found = true;
            }
            // Selection starts just at the end of the node.
            else if (!range.collapsed && node.previousSibling && node.previousSibling.nodeType == Node.ELEMENT_NODE) {
              var child = node.previousSibling;

              if (child && child.textContent.replace(/\u200B/g, '') === text().replace(/\u200B/g, '')) {
                node = child;
                node_found = true;
              }
            }
            // Browser sees selection at the beginning of the next node.
            else if (!range.collapsed && node.childNodes.length > 0 && node.childNodes[range.endOffset]) {
              var child = node.childNodes[range.endOffset].previousSibling;

              if (child.nodeType == Node.ELEMENT_NODE) {
                if (child && child.textContent.replace(/\u200B/g, '') === text().replace(/\u200B/g, '')) {
                  node = child;
                  node_found = true;
                }
              }
            }

            if (!node_found && node.childNodes.length > 0 && $(node.childNodes[node.childNodes.length - 1]).text() === text() && ['BR', 'IMG', 'HR'].indexOf(node.childNodes[node.childNodes.length - 1].tagName) < 0) {
              node = node.childNodes[node.childNodes.length - 1];
            }
          }

          if (node.nodeType == Node.TEXT_NODE && range.endOffset == 0 && node.previousSibling && node.previousSibling.nodeType == Node.ELEMENT_NODE) {
            node = node.previousSibling;
          }

          while (node.nodeType != Node.ELEMENT_NODE && node.parentNode) {
            node = node.parentNode;
          }

          // Make sure the node is in editor.
          var p = node;
          while (p && p.tagName != 'HTML') {
            if (p == editor.$el.get(0)) {
              return node;
            }

            p = $(p).parent()[0];
          }
        }
      }
      catch (ex) {
      }

      return editor.$el.get(0);
    }

    /**
     * Get the ELEMENTS node where the selection starts.
     * By default TEXT node might be selected.
     */
    function rangeElement(rangeContainer, offset) {
      var node = rangeContainer;
      if (node.nodeType == Node.ELEMENT_NODE) {
        // Search for node deeper.
        if (node.childNodes.length > 0 && node.childNodes[offset]) {
          node = node.childNodes[offset];
        }
      }

      if (node.nodeType == Node.TEXT_NODE) {
        node = node.parentNode;
      }

      return node;
    }

    /**
     * Search for the current selected blocks.
     */
    function blocks () {
      var blks = [];

      var sel = get();

      // Selection must be inside editor.
      if (inEditor() && sel.rangeCount) {

        // Loop through ranges.
        var rngs = ranges();
        for (var i = 0; i < rngs.length; i++) {
          var range = rngs[i];

          // Get start node and end node for range.
          var start_node = rangeElement(range.startContainer, range.startOffset);
          var end_node  = rangeElement(range.endContainer, range.endOffset);

          // Add the start node.
          if (editor.node.isBlock(start_node) && blks.indexOf(start_node) < 0) blks.push(start_node);

          // Check for the parent node of the start node.
          var block_parent = editor.node.blockParent(start_node);
          if (block_parent && blks.indexOf(block_parent) < 0) {
            blks.push(block_parent);
          }

          // Do not add nodes where we've been once.
          var was_into = [];

          // Loop until we reach end.
          var next_node = start_node;
          while (next_node !== end_node && next_node !== editor.$el.get(0)) {
            // Get deeper into the current node.
            if (was_into.indexOf(next_node) < 0 && next_node.children && next_node.children.length) {
              was_into.push(next_node);
              next_node = next_node.children[0];
            }

            // Get next sibling.
            else if (next_node.nextSibling) {
              next_node = next_node.nextSibling;
            }

            // Get parent node.
            else if (next_node.parentNode) {
              next_node = next_node.parentNode;
              was_into.push(next_node);
            }

            // Add node to the list.
            if (editor.node.isBlock(next_node) && was_into.indexOf(next_node) < 0 && blks.indexOf(next_node) < 0) {
              if (next_node !== end_node || range.endOffset > 0) {
                blks.push(next_node);
              }
            }
          }

          // Add the end node.
          if (editor.node.isBlock(end_node) && blks.indexOf(end_node) < 0 && range.endOffset > 0) blks.push(end_node);

          // Check for the parent node of the end node.
          var block_parent = editor.node.blockParent(end_node);
          if (block_parent && blks.indexOf(block_parent) < 0) {
            blks.push(block_parent);
          }
        }
      }

      // Remove blocks that we don't need.
      for (var i = blks.length - 1; i > 0; i--) {
        // Nodes that contain another node. Don't do it for LI.
        if ($(blks[i]).find(blks).length && blks[i].tagName != 'LI') blks.splice(i, 1);
      }

      return blks;
    }

    /**
     * Save selection.
     */
    function save () {
      if (editor.$wp) {
        editor.markers.remove();

        var rgs = ranges();
        var new_ranges = [];

        for (var i = 0; i < rgs.length; i++) {
          if (rgs[i].startContainer !== editor.doc) {
            var range = rgs[i];
            var collapsed = range.collapsed;
            var start_m = editor.markers.place(range, true, i); // Start.
            var end_m = editor.markers.place(range, false, i); // End.

            // https://github.com/froala/wysiwyg-editor/issues/1398.
            editor.$el.get(0).normalize();

            if (editor.browser.safari && !collapsed) {
              var range = editor.doc.createRange();
              range.setStartAfter(start_m);
              range.setEndBefore(end_m);
              new_ranges.push(range);
            }
          }
        }

        if (editor.browser.safari && new_ranges.length) {
          editor.selection.clear();
          for (var i = 0; i < new_ranges.length; i++) {
            editor.selection.get().addRange(new_ranges[i]);
          }
        }
      }
    }

    /**
     * Restore selection.
     */
    function restore () {
      // Get markers.
      var markers = editor.$el.get(0).querySelectorAll('.fr-marker[data-type="true"]');

      if (!editor.$wp) {
        editor.markers.remove();
        return false;
      }

      // No markers.
      if (markers.length === 0) {
        return false;
      }

      if (editor.browser.msie || editor.browser.edge) {
        for (var i = 0; i < markers.length; i++) {
          markers[i].style.display = 'inline-block';
        }
      }

      // Focus.
      if (!editor.core.hasFocus() && !editor.browser.msie && !editor.browser.webkit) {
        editor.$el.focus();
      }

      clear();
      var sel = get();

      var parents = [];

      // Add ranges.
      for (var i = 0; i < markers.length; i++) {
        var id = $(markers[i]).data('id');
        var start_marker = markers[i];
        var range = editor.doc.createRange();
        var end_marker = editor.$el.find('.fr-marker[data-type="false"][data-id="' + id + '"]');

        if (editor.browser.msie || editor.browser.edge) end_marker.css('display', 'inline-block');

        var ghost = null;

        // Make sure there is an start marker.
        if (end_marker.length > 0) {
          end_marker = end_marker[0];

          try {
            // If we have markers one next to each other inside text, then we should normalize text by joining it.
            var special_case = false;

            // Clear empty text nodes.
            var s_node = start_marker.nextSibling;
            while (s_node && s_node.nodeType == Node.TEXT_NODE && s_node.textContent.length == 0) {
              var tmp = s_node;
              s_node = s_node.nextSibling;
              $(tmp).remove();
            }

            var e_node = end_marker.nextSibling;
            while (e_node && e_node.nodeType == Node.TEXT_NODE && e_node.textContent.length == 0) {
              var tmp = e_node;
              e_node = e_node.nextSibling;
              $(tmp).remove();
            }

            if (start_marker.nextSibling == end_marker || end_marker.nextSibling == start_marker) {
              // Decide which is first and which is last between markers.
              var first_node = (start_marker.nextSibling == end_marker) ? start_marker : end_marker;
              var last_node = (first_node == start_marker) ? end_marker : start_marker;

              // Previous node.
              var prev_node = first_node.previousSibling;
              while (prev_node && prev_node.nodeType == Node.TEXT_NODE && prev_node.length == 0) {
                var tmp = prev_node;
                prev_node = prev_node.previousSibling;
                $(tmp).remove();
              }

              // Normalize text before.
              if (prev_node && prev_node.nodeType == Node.TEXT_NODE) {
                while (prev_node && prev_node.previousSibling && prev_node.previousSibling.nodeType == Node.TEXT_NODE) {
                  prev_node.previousSibling.textContent = prev_node.previousSibling.textContent + prev_node.textContent;
                  prev_node = prev_node.previousSibling;
                  $(prev_node.nextSibling).remove();
                }
              }

              // Next node.
              var next_node = last_node.nextSibling;
              while (next_node && next_node.nodeType == Node.TEXT_NODE && next_node.length == 0) {
                var tmp = next_node;
                next_node = next_node.nextSibling;
                $(tmp).remove();
              }

              // Normalize text after.
              if (next_node && next_node.nodeType == Node.TEXT_NODE) {
                while (next_node && next_node.nextSibling && next_node.nextSibling.nodeType == Node.TEXT_NODE) {
                  next_node.nextSibling.textContent =  next_node.textContent + next_node.nextSibling.textContent;
                  next_node = next_node.nextSibling;
                  $(next_node.previousSibling).remove();
                }
              }

              if (prev_node && (editor.node.isVoid(prev_node) || editor.node.isBlock(prev_node))) prev_node = null;
              if (next_node && (editor.node.isVoid(next_node) || editor.node.isBlock(next_node))) next_node = null;

              // Previous node and next node are both text.
              if (prev_node && next_node && prev_node.nodeType == Node.TEXT_NODE && next_node.nodeType == Node.TEXT_NODE) {
                // Remove markers.
                $(start_marker).remove();
                $(end_marker).remove();

                // Save cursor position.
                var len = prev_node.textContent.length;
                prev_node.textContent = prev_node.textContent + next_node.textContent;
                $(next_node).remove();

                // Normalize spaces.
                editor.spaces.normalize(prev_node);

                // Restore position.
                range.setStart(prev_node, len);
                range.setEnd(prev_node, len);

                special_case = true;
              }
              else if (!prev_node && next_node && next_node.nodeType == Node.TEXT_NODE) {
                // Remove markers.
                $(start_marker).remove();
                $(end_marker).remove();

                // Normalize spaces.
                editor.spaces.normalize(next_node);

                ghost = $(editor.doc.createTextNode('\u200B'));
                $(next_node).before(ghost);

                // Restore position.
                range.setStart(next_node, 0);
                range.setEnd(next_node, 0);
                special_case = true;
              }
              else if (!next_node && prev_node && prev_node.nodeType == Node.TEXT_NODE) {
                // Remove markers.
                $(start_marker).remove();
                $(end_marker).remove();

                // Normalize spaces.
                editor.spaces.normalize(prev_node);

                ghost = $(editor.doc.createTextNode('\u200B'));
                $(prev_node).after(ghost);

                // Restore position.
                range.setStart(prev_node, prev_node.textContent.length);
                range.setEnd(prev_node, prev_node.textContent.length);

                special_case = true;
              }
            }

            if (!special_case) {
              var x, y;
              // DO NOT TOUCH THIS OR IT WILL BREAK!!!
              if (editor.browser.chrome && start_marker.nextSibling == end_marker) {
                x = _normalizedMarker(end_marker, range, true) || range.setStartAfter(end_marker);
                y = _normalizedMarker(start_marker, range, false) || range.setEndBefore(start_marker);
              }
              else {
                if (start_marker.previousSibling == end_marker) {
                  start_marker = end_marker;
                  end_marker = start_marker.nextSibling;
                }

                // https://github.com/froala/wysiwyg-editor/issues/759
                if (!(end_marker.nextSibling && end_marker.nextSibling.tagName === 'BR') &&
                    !(!end_marker.nextSibling && editor.node.isBlock(start_marker.previousSibling)) &&
                    !(start_marker.previousSibling && start_marker.previousSibling.tagName == 'BR')) {
                  start_marker.style.display = 'inline';
                  end_marker.style.display = 'inline';
                  ghost = $(editor.doc.createTextNode('\u200B'));
                }

                // https://github.com/froala/wysiwyg-editor/issues/1120
                var p_node = start_marker.previousSibling;
                if (p_node && p_node.style && editor.win.getComputedStyle(p_node).display == 'block' && !editor.opts.enter == $.FE.ENTER_BR) {
                  range.setEndAfter(p_node);
                  range.setStartAfter(p_node);
                }
                else {
                  x = _normalizedMarker(start_marker, range, true) || ($(start_marker).before(ghost) && range.setStartBefore(start_marker));
                  y = _normalizedMarker(end_marker, range, false) || ($(end_marker).after(ghost) && range.setEndAfter(end_marker));
                }
              }

              if (typeof x == 'function') x();
              if (typeof y == 'function') y();
            }
          } catch (ex) {
            console.warn ('RESTORE RANGE', ex);
          }
        }

        if (ghost) {
          ghost.remove();
        }

        try {
          sel.addRange(range);
        }
        catch (ex) {
          console.warn ('ADD RANGE', ex);
        }
      }

      // Remove used markers.
      editor.markers.remove();
    }

    /**
     * Normalize marker when restoring selection.
     */
    function _normalizedMarker(marker, range, start) {
      var prev_node = marker.previousSibling;
      var next_node = marker.nextSibling;

      // Prev and next node are both text nodes.
      if (prev_node && next_node && prev_node.nodeType == Node.TEXT_NODE && next_node.nodeType == Node.TEXT_NODE) {
        var len = prev_node.textContent.length;

        if (start) {
         next_node.textContent = prev_node.textContent + next_node.textContent;
         $(prev_node).remove();
         $(marker).remove();

         editor.spaces.normalize(next_node);

         return function () {
           range.setStart(next_node, len);
         }
        }
        else {
          prev_node.textContent = prev_node.textContent + next_node.textContent;
          $(next_node).remove();
          $(marker).remove();

          editor.spaces.normalize(prev_node);

          return function () {
            range.setEnd(prev_node, len);
          }
        }
      }
      // Prev node is text node.
      else if (prev_node && !next_node && prev_node.nodeType == Node.TEXT_NODE) {
        var len = prev_node.textContent.length;

        if (start) {
          editor.spaces.normalize(prev_node);
          return function () {
            range.setStart(prev_node, len);
          }
        }
        else {
          editor.spaces.normalize(prev_node);
          return function () {
            range.setEnd(prev_node, len);
          }
        }
      }

      // Next node is text node.
      else if (next_node && !prev_node && next_node.nodeType == Node.TEXT_NODE) {
        if (start) {
          editor.spaces.normalize(next_node);
          return function () {
            range.setStart(next_node, 0);
          }
        }
        else {
          editor.spaces.normalize(next_node);
          return function () {
            range.setEnd(next_node, 0);
          }
        }
      }

      return false;
    }

    /**
     * Determine if we can do delete.
     */
    function _canDelete () {
      return true;
    }

    /**
     * Check if selection is collapsed.
     */
    function isCollapsed () {
      var rgs = ranges();
      for (var i = 0; i < rgs.length; i++) {
        if (!rgs[i].collapsed) return false;
      }

      return true;
    }

    // From: http://www.coderexception.com/0B1B33z1NyQxUQSJ/contenteditable-div-how-can-i-determine-if-the-cursor-is-at-the-start-or-end-of-the-content
    function info (el) {
      var atStart = false;
      var atEnd = false;
      var selRange;
      var testRange;

      if (editor.win.getSelection) {
        var sel = editor.win.getSelection();
        if (sel.rangeCount) {
          selRange = sel.getRangeAt(0);
          testRange = selRange.cloneRange();

          testRange.selectNodeContents(el);
          testRange.setEnd(selRange.startContainer, selRange.startOffset);
          atStart = (testRange.toString() === '');

          testRange.selectNodeContents(el);
          testRange.setStart(selRange.endContainer, selRange.endOffset);
          atEnd = (testRange.toString() === '');
        }
      } else if (editor.doc.selection && editor.doc.selection.type != 'Control') {
        selRange = editor.doc.selection.createRange();
        testRange = selRange.duplicate();

        testRange.moveToElementText(el);
        testRange.setEndPoint('EndToStart', selRange);
        atStart = (testRange.text === '');

        testRange.moveToElementText(el);
        testRange.setEndPoint('StartToEnd', selRange);
        atEnd = (testRange.text === '');
      }

      return { atStart: atStart, atEnd: atEnd };
    }

    /**
     * Check if everything is selected inside the editor.
     */
    function isFull () {
      if (isCollapsed()) return false;

      // https://github.com/froala/wysiwyg-editor/issues/710
      editor.$el.find('td').prepend('<span class="fr-mk">' + $.FE.INVISIBLE_SPACE + '</span>');
      editor.$el.find('img').append('<span class="fr-mk">' + $.FE.INVISIBLE_SPACE + '</span>');

      var full = false;
      var inf = info(editor.$el.get(0));
      if (inf.atStart && inf.atEnd) full = true;

      // https://github.com/froala/wysiwyg-editor/issues/710
      editor.$el.find('.fr-mk').remove();

      return full;
    }

    /**
     * Remove HTML from inner nodes when we deal with keepFormatOnDelete option.
     */
    function _emptyInnerNodes (node, first) {
      if (typeof first == 'undefined') first = true;

      // Remove invisible spaces.
      var h = $(node).html();
      if (h && h.replace(/\u200b/g, '').length != h.length) $(node).html(h.replace(/\u200b/g, ''));

      // Loop contents.
      var contents = editor.node.contents(node);
      for (var j = 0; j < contents.length; j++) {
        // Remove text nodes.
        if (contents[j].nodeType != Node.ELEMENT_NODE) {
          $(contents[j]).remove();
        }

        // Empty inner nodes further.
        else {
          // j == 0 determines if the node is the first one and we should keep format.
          _emptyInnerNodes(contents[j], j == 0);

          // There are inner nodes, ignore the current one.
          if (j == 0) first = false;
        }
      }

      // First node is a text node, so replace it with a span.
      if (node.nodeType == Node.TEXT_NODE) {
        $(node).replaceWith('<span data-first="true" data-text="true"></span>');
      }

      // Add the first node marker so that we add selection in it later on.
      else if (first) {
        $(node).attr('data-first', true);
      }
    }

    /**
     * Process deleting nodes.
     */
    function _processNodeDelete ($node, should_delete) {
      var contents = editor.node.contents($node.get(0));

      // Node is TD or TH.
      if (['TD', 'TH'].indexOf($node.get(0).tagName) >= 0 && $node.find('.fr-marker').length == 1 && $(contents[0]).hasClass('fr-marker')) {
        $node.attr('data-del-cell', true);
      }

      for (var i = 0; i < contents.length; i++) {
        var node = contents[i];

        // We found a marker.
        if ($(node).hasClass('fr-marker')) {
          should_delete = (should_delete + 1) % 2;
        }
        else if (should_delete) {
          // Check if we have a marker inside it.
          if ($(node).find('.fr-marker').length > 0) {
            should_delete = _processNodeDelete($(node), should_delete);
          }
          else {
            // TD, TH or inner, then go further.
            if (['TD', 'TH'].indexOf(node.tagName) < 0 && !$(node).hasClass('fr-inner')) {
              if (!editor.opts.keepFormatOnDelete || editor.$el.find('[data-first]').length > 0) {
                $(node).remove();
              }
              else {
                _emptyInnerNodes(node);
              }
            }
            else if ($(node).hasClass('fr-inner')) {
              if ($(node).find('.fr-inner').length == 0) {
                $(node).html('<br>');
              }
              else {
                $(node).find('.fr-inner').filter(function () {
                  return $(this).find('fr-inner').length == 0;
                }).html('<br>');
              }
            }
            else {
              $(node).empty();
              $(node).attr('data-del-cell', true);
            }
          }
        }
        else {
          if ($(node).find('.fr-marker').length > 0) {
            should_delete = _processNodeDelete($(node), should_delete);
          }
        }
      }

      return should_delete;
    }

    /**
     * Determine if selection is inside the editor.
     */
    function inEditor () {
      try {
        if (!editor.$wp) return false;

        var range = ranges(0);
        var container = range.commonAncestorContainer;

        while (container && !editor.node.isElement(container)) {
          container = container.parentNode;
        }

        if (editor.node.isElement(container)) return true;
        return false;
      }
      catch (ex) {
        return false;
      }
    }

    /**
     * Remove the current selection html.
     */
    function remove () {
      if (isCollapsed()) return true;

      save();

      // Get the previous sibling normalized.
      var _prevSibling = function (node) {
        var prev_node = node.previousSibling;
        while (prev_node && prev_node.nodeType == Node.TEXT_NODE && prev_node.textContent.length == 0) {
          var tmp = prev_node;
          var prev_node = prev_node.previousSibling;
          $(tmp).remove();
        }

        return prev_node;
      }

      // Get the next sibling normalized.
      var _nextSibling = function (node) {
        var next_node = node.nextSibling;
        while (next_node && next_node.nodeType == Node.TEXT_NODE && next_node.textContent.length == 0) {
          var tmp = next_node;
          var next_node = next_node.nextSibling;
          $(tmp).remove();
        }

        return next_node;
      }

      // Normalize start markers.
      var start_markers = editor.$el.find('.fr-marker[data-type="true"]');
      for (var i = 0; i < start_markers.length; i++) {
        var sm = start_markers[i];
        while (!_prevSibling(sm) && !editor.node.isBlock(sm.parentNode) && !editor.$el.is(sm.parentNode)) {
          $(sm.parentNode).before(sm);
        }
      }

      // Normalize end markers.
      var end_markers = editor.$el.find('.fr-marker[data-type="false"]');
      for (var i = 0; i < end_markers.length; i++) {
        var em = end_markers[i];
        while (!_nextSibling(em) && !editor.node.isBlock(em.parentNode) && !editor.$el.is(em.parentNode)) {
          $(em.parentNode).after(em);
        }

        // Last node is empty and has a BR in it.
        if (em.parentNode && editor.node.isBlock(em.parentNode) && editor.node.isEmpty(em.parentNode) && !editor.$el.is(em.parentNode)) {
          $(em.parentNode).after(em);
        }
      }

      // Check if selection can be deleted.
      if (_canDelete()) {
        _processNodeDelete(editor.$el, 0);

        // Look for selection marker.
        var $first_node = editor.$el.find('[data-first="true"]');
        if ($first_node.length) {
          // Remove markers.
          editor.$el.find('.fr-marker').remove();

          // Add markers in the node that we marked as the first one.
          $first_node
            .append($.FE.INVISIBLE_SPACE + $.FE.MARKERS)
            .removeAttr('data-first');

          // Remove span with data-text if there is one.
          if ($first_node.attr('data-text')) {
            $first_node.replaceWith($first_node.html());
          }
        }
        else {
          // Remove tables.
          editor.$el.find('table').filter(function () {
            var ok = $(this).find('[data-del-cell]').length > 0 && $(this).find('[data-del-cell]').length == $(this).find('td, th').length;

            return ok;
          }).remove();
          editor.$el.find('[data-del-cell]').removeAttr('data-del-cell');

          // Merge contents between markers.
          var start_markers = editor.$el.find('.fr-marker[data-type="true"]');
          for (var i = 0; i < start_markers.length; i++) {
            // Get start marker.
            var start_marker = start_markers[i];

            // Get the next node after start marker.
            var next_node = start_marker.nextSibling;

            // Get the end node.
            var end_marker = editor.$el.find('.fr-marker[data-type="false"][data-id="' + $(start_marker).data('id') + '"]').get(0);

            if (end_marker) {
              // Markers are next to other.
              if (next_node && next_node == end_marker) {
                // Do nothing.
              }
              else if (start_marker) {
                // Get the parents of the nodes.
                var start_parent = editor.node.blockParent(start_marker);
                var end_parent = editor.node.blockParent(end_marker);

                // https://github.com/froala/wysiwyg-editor/issues/1233
                var list_start = false;
                var list_end = false;
                if (start_parent && ['UL', 'OL'].indexOf(start_parent.tagName) >= 0) {
                  start_parent = null;
                  list_start = true;
                }
                if (end_parent && ['UL', 'OL'].indexOf(end_parent.tagName) >= 0) {
                  end_parent = null;
                  list_end = true;
                }

                // Move end marker next to start marker.
                $(start_marker).after(end_marker);

                // We're in the same parent. Moving marker is enough.
                if (start_parent == end_parent) {
                }

                // The block parent of the start marker is the element itself.
                else if (start_parent == null && !list_start) {
                  var deep_parent = editor.node.deepestParent(start_marker);

                  // There is a parent for the marker. Move the end html to it.
                  if (deep_parent) {
                    $(deep_parent).after($(end_parent).html());
                    $(end_parent).remove();
                  }

                  // There is no parent for the marker.
                  else if ($(end_parent).parentsUntil(editor.$el, 'table').length == 0) {
                    $(start_marker).next().after($(end_parent).html());
                    $(end_parent).remove();
                  }
                }

                // End marker is inside element. We don't merge in table.
                else if (end_parent == null && !list_end && $(start_parent).parentsUntil(editor.$el, 'table').length == 0) {
                  // Get the node that has a next sibling.
                  var next_node = start_parent;
                  while (!next_node.nextSibling && next_node.parentNode != editor.$el.get(0)) {
                    next_node = next_node.parentNode;
                  }
                  next_node = next_node.nextSibling;

                  // Join HTML inside the start node.
                  while (next_node && next_node.tagName != 'BR') {
                    var tmp_node = next_node.nextSibling;
                    $(start_parent).append(next_node);
                    next_node = tmp_node;
                  }

                  if (next_node && next_node.tagName == 'BR') {
                    $(next_node).remove();
                  }
                }

                // Join end block with start block.
                else if (start_parent && end_parent && $(start_parent).parentsUntil(editor.$el, 'table').length == 0 && $(end_parent).parentsUntil(editor.$el, 'table').length == 0) {
                  $(start_parent).append($(end_parent).html());
                  $(end_parent).remove();
                }
              }
            }

            else {
              end_marker = $(start_marker).clone().attr('data-type', false);
              $(start_marker).after(end_marker);
            }
          }
        }
      }

      if (!editor.opts.keepFormatOnDelete) {
        editor.html.fillEmptyBlocks();
      }

      editor.html.cleanEmptyTags(true);
      editor.clean.lists();

      editor.spaces.normalize();

      // https://github.com/froala/wysiwyg-editor/issues/1379
      var last_marker = editor.$el.find('.fr-marker:last').get(0);
      var first_marker = editor.$el.find('.fr-marker:first').get(0);
      if (!last_marker.nextSibling && first_marker.previousSibling && first_marker.previousSibling.tagName == 'BR' && editor.node.isElement(last_marker.parentNode) && editor.node.isElement(first_marker.parentNode)) {
        editor.$el.append('<br>');
      }

      restore();
    }

    function setAtStart (node) {
      if ($(node).find('.fr-marker').length > 0) return false;

      var contents = editor.node.contents(node);
      while (contents.length && editor.node.isBlock(contents[0])) {
        node = contents[0];
        contents = editor.node.contents(node);
      }

      $(node).prepend($.FE.MARKERS);
    }

    function setAtEnd (node) {
      if ($(node).find('.fr-marker').length > 0) return false;

      var contents = editor.node.contents(node);
      while (contents.length && editor.node.isBlock(contents[contents.length - 1])) {
        node = contents[contents.length - 1];
        contents = editor.node.contents(node);
      }

      $(node).append($.FE.MARKERS);
    }

    function setBefore (node) {
      var prev_node = node.previousSibling;
      while (prev_node && prev_node.nodeType == Node.TEXT_NODE && prev_node.textContent.length == 0) {
        prev_node = prev_node.previousSibling;
      }

      if (prev_node) {
        if (editor.node.isBlock(prev_node)) {
          setAtEnd(prev_node);
        }
        else if (prev_node.tagName == 'BR') {
          $(prev_node).before($.FE.MARKERS);
        }
        else {
          $(prev_node).after($.FE.MARKERS);
        }

        return true;
      }
      else {
        return false;
      }
    }

    function setAfter (node) {
      var next_node = node.nextSibling;
      while (next_node && next_node.nodeType == Node.TEXT_NODE && next_node.textContent.length == 0) {
        next_node = next_node.nextSibling;
      }

      if (next_node) {
        if (editor.node.isBlock(next_node)) {
          setAtStart(next_node);
        }
        else {
          $(next_node).before($.FE.MARKERS);
        }

        return true;
      }
      else {
        return false;
      }
    }

    return {
      text: text,
      get: get,
      ranges: ranges,
      clear: clear,
      element: element,
      endElement: endElement,
      save: save,
      restore: restore,
      isCollapsed: isCollapsed,
      isFull: isFull,
      inEditor: inEditor,
      remove: remove,
      blocks: blocks,
      info: info,
      setAtEnd: setAtEnd,
      setAtStart: setAtStart,
      setBefore: setBefore,
      setAfter: setAfter,
      rangeElement: rangeElement
    }
  };


  $.FE.MODULES.spaces = function (editor) {
    function _remove (node) {
      var next = node.nextSibling || node.parentNode;

      node.parentNode.removeChild(node);

      return next;
    }

    function _next (prev, current) {
      if ((prev && prev.parentNode === current) || current.nodeName === 'PRE') {
        return current.nextSibling || current.parentNode;
      }

      return current.firstChild || current.nextSibling || current.parentNode;
    }

    // Adaptation of MIT https://github.com/lucthev/collapse-whitespace.
    function collapse (elem) {
      if (!elem.firstChild || elem.nodeName === 'PRE' || ['STYLE', 'SCRIPT'].indexOf(elem.tagName) >= 0) return;

      var prevText = null;

      var prev = null;
      var node = _next(prev, elem);

      // Go deep while current node is not elem.
      while (node !== elem && node.nodeName !== 'PRE' && ['STYLE', 'SCRIPT'].indexOf(node.tagName) < 0) {
        // Current node is text node.
        if (node.nodeType === Node.TEXT_NODE) {
          // Collapse spaces.
          var text = node.data.replace(/[ \r\n\t]+/g, ' ');

          // No previous text or previous text ends with space.
          // No previous voide.
          // Current text starts with space.
          if ((!prevText || / $/.test(prevText.data)) && text[0] === ' ' && (!node.previousSibling || !editor.node.isVoid(node.previousSibling) || node.previousSibling.tagName === 'BR')) {
            // Remove starting space.
            text = text.substr(1);
          }

          // `text` might be empty at this point.
          if (!text || text.length == 0) {
            node = _remove(node);
            continue;
          }

          // Set new text.
          node.data = text;

          // Set previous text node as current node.
          prevText = node;
        }

        // Current node is element node.
        else if (node.nodeType === Node.ELEMENT_NODE) {
          // Block or BR.
          if (editor.node.isBlock(node) || editor.node.isVoid(node)) {
            // If there was a previous text collapse ending spaces;
            if (prevText && prevText.data && (editor.node.isBlock(node) || node.tagName === 'BR')) {
              prevText.data = prevText.data.replace(/ $/, '');
            }

            prevText = null;
          }
          else if (node.textContent.length == 0) {
            prevText = node;
          }
        }

        var nextNode = _next(prev, node);
        prev = node;
        node = nextNode;
      }

      // There is previous text.
      if (prevText && prevText.data) {
        // Remove ending.
        prevText.data = prevText.data.replace(/ $/, '')

        // If text is left empty, remove it.
        if (!prevText.data) {
          _remove(prevText);
        }
      }
    }

    function normalize (node, browser_way) {
      if (typeof node == 'undefined' || !node) node = editor.$el.get(0);
      if (typeof browser_way == 'undefined') browser_way = false;

      if (browser_way) {
        collapse(node);
      }

      // Ignore contenteditable.
      if (node.getAttribute && node.getAttribute('contenteditable') == 'false') return;

      if (node.nodeType == Node.ELEMENT_NODE && ['STYLE', 'SCRIPT', 'HEAD'].indexOf(node.tagName) < 0) {
        var contents = editor.node.contents(node);
        for (var i = contents.length - 1; i >= 0 ; i--) {
          if (contents[i].tagName != Node.ELEMENT_NODE || (contents[i].className || '').indexOf('fr-marker') < 0) {
            normalize(contents[i]);
          }
        }
      }
      else if (node.nodeType == Node.TEXT_NODE && node.textContent.length > 0) {
        var prev_node = node.previousSibling;
        var next_node = node.nextSibling;
        var txt = node.textContent;

        // Convert all non breaking to breaking spaces.
        txt = txt.replace(new RegExp($.FE.UNICODE_NBSP, 'g'), ' ');

        var new_text = '';
        for (var t = 0; t < txt.length; t++) {
          if (txt.charCodeAt(t) == 32 && (t === 0 || new_text.charCodeAt(t - 1) == 32)) {
            new_text += $.FE.UNICODE_NBSP;
          }
          else {
            new_text += txt[t];
          }
        }

        // Ending spaces should be NBSP or spaces before block tags.
        if (!node.nextSibling || editor.node.isBlock(node.nextSibling) || (node.nextSibling.nodeType == Node.ELEMENT_NODE && editor.win.getComputedStyle(node.nextSibling) && editor.win.getComputedStyle(node.nextSibling).display == 'block')) {
          new_text = new_text.replace(/ $/, $.FE.UNICODE_NBSP);
        }

        // Previous sibling is not void or block.
        if (node.previousSibling && !editor.node.isVoid(node.previousSibling) && !editor.node.isBlock(node.previousSibling)) {
          new_text = new_text.replace(/^\u00A0([^ $])/, ' $1');

          // https://github.com/froala/wysiwyg-editor/issues/1355.
          if (new_text.length === 1 && new_text.charCodeAt(0) === 160 && node.nextSibling && !editor.node.isVoid(node.nextSibling) && !editor.node.isBlock(node.nextSibling)) {
            new_text = ' ';
          }
        }

        // Convert middle nbsp to spaces.
        new_text = new_text.replace(/([^ \u00A0])\u00A0([^ \u00A0])/g, '$1 $2');

        if (node.textContent != new_text) {
          node.textContent = new_text;
        }
      }
    }

    return {
      normalize: normalize
    }
  };


  $.FE.UNICODE_NBSP = String.fromCharCode(160);

  // Void Elements http://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements
  $.FE.VOID_ELEMENTS = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'];

  $.FE.BLOCK_TAGS = ['address', 'article', 'aside', 'audio', 'blockquote', 'canvas', 'dd', 'div', 'dl', 'dt', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'li', 'main', 'nav', 'noscript', 'ol', 'output', 'p', 'pre', 'section', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'ul', 'video'];

  // Extend defaults.
  $.extend($.FE.DEFAULTS, {
    htmlAllowedEmptyTags: ['textarea', 'a', 'iframe', 'object', 'video', 'style', 'script', '.fa', '.fr-emoticon'],
    htmlDoNotWrapTags: ['script', 'style'],
    htmlSimpleAmpersand: false
  });

  $.FE.MODULES.html = function (editor) {
    /**
     * Determine the default block tag.
     */
    function defaultTag () {
      if (editor.opts.enter == $.FE.ENTER_P) return 'p';
      if (editor.opts.enter == $.FE.ENTER_DIV) return 'div';
      if (editor.opts.enter == $.FE.ENTER_BR) return null;
    }

    /**
     * Get the empty blocs.
     */
    function emptyBlocks () {
      var empty_blocks = [];

      // Block tag elements.
      var els = editor.$el.get(0).querySelectorAll(blockTagsQuery());

      // Check if there are empty block tags with markers.
      for (var i = 0; i < els.length; i++) {
        // There are void elements tags.
        if (els[i].querySelectorAll($.FE.VOID_ELEMENTS.join(',')).length > 0) continue;

        // There are other empty elements.
        if (els[i].querySelectorAll(editor.opts.htmlAllowedEmptyTags.join(':not(.fr-marker),') + ':not(.fr-marker)').length > 0) continue;

        // There are other block tags.
        if (els[i].querySelectorAll(blockTagsQuery()).length > 0) continue;

        // We're checking text from here on.
        var contents = editor.node.contents(els[i]);

        var found = false;
        for (var j = 0; j < contents.length; j++) {
          if (contents[j].nodeType == Node.COMMENT_NODE) continue;

          // Text node that is not empty.
          if (contents[j].textContent && contents[j].textContent.replace(/\u200B/g, '').replace(/\n/g, '').length > 0) {
            found = true;
            break;
          }
        }

        // Make sure we don't add TABLE and TD at the same time for instance.
        if (!found) empty_blocks.push(els[i]);
      }

      return empty_blocks;
    }

    /**
     * Create jQuery query for empty block tags.
     */
    function emptyBlockTagsQuery () {
      return $.FE.BLOCK_TAGS.join(':empty, ') + ':empty';
    }

    /**
     * Create jQuery query for selecting block tags.
     */
    function blockTagsQuery () {
      return $.FE.BLOCK_TAGS.join(', ');
    }

    /**
     * Remove empty elements that are not VOID elements.
     */
    function cleanEmptyTags (remove_blocks) {
      var els = $.merge([], $.FE.VOID_ELEMENTS);
      els = $.merge(els, editor.opts.htmlAllowedEmptyTags);

      if (typeof remove_blocks == 'undefined') els = $.merge(els, $.FE.BLOCK_TAGS);

      var elms;
      var ok;
      do {
        ok = false;
        elms = editor.$el.get(0).querySelectorAll('*:empty:not(' + els.join('):not(') + '):not(.fr-marker)');

        // Remove those elements that have no attributes.
        for (var i = 0; i < elms.length; i++) {
          if (elms[i].attributes.length === 0 || typeof elms[i].getAttribute('href') !== 'undefined') {
            $(elms[i]).remove();
            ok = true;
          }
        }

        elms = editor.$el.get(0).querySelectorAll('*:empty:not(' + els.join('):not(') + '):not(.fr-marker)');
      } while (elms.length && ok);

    }

    /**
     * Wrap the content inside the element passed as argument.
     */
    function _wrapElement($el, temp) {
      var default_tag = defaultTag();
      if (temp) default_tag = 'div class="fr-temp-div"';

      if (default_tag) {
        var contents = editor.node.contents($el.get(0));
        var $anchor = null;

        // Loop through contents.
        for (var i = 0; i < contents.length; i++) {

          // Current node.
          var node = contents[i];

          // Current node is a block node.
          if (node.nodeType == Node.ELEMENT_NODE && (editor.node.isBlock(node) || ($(node).is(editor.opts.htmlDoNotWrapTags.join(',')) && !$(node).hasClass('fr-marker')))) {
            $anchor = null;
          }

          // Other node types than element and text.
          else if (node.nodeType != Node.ELEMENT_NODE && node.nodeType != Node.TEXT_NODE) {
            $anchor = null;
          }

          // Current node is BR.
          else if (node.nodeType == Node.ELEMENT_NODE && node.tagName == 'BR') {
            // There is no anchor.
            if ($anchor == null) {
              if (temp) {
                $(node).replaceWith('<' + default_tag + ' data-empty="true"><br></div>');
              }
              else {
                $(node).replaceWith('<' + default_tag + '><br></' + default_tag + '>');
              }
            }

            // There is anchor. Just remove BR.
            else {
              $(node).remove();

              // Check if in anchor we have something else than markers.
              var cnts = editor.node.contents($anchor);
              var found = false;
              for (var j = 0; j < cnts.length; j++) {
                if (!$(cnts[j]).hasClass('fr-marker') && !(cnts[j].nodeType == Node.TEXT_NODE && cnts[j].textContent.replace(/ /g, '').length === 0)) {
                  found = true;
                  break;
                }
              }

              if (found === false) {
                $anchor.append('<br>');
                $anchor.data('empty', true);
              }

              $anchor = null;
            }
          }

          // Text node or other node type.
          else {
            if (node.nodeType == Node.TEXT_NODE && $(node).text().trim().length == 0) {
              $(node).remove();
            }
            else {
              if ($anchor == null) {
                $anchor = $('<' + default_tag + '>');
                $(node).before($anchor);
              }

              if (node.nodeType == Node.TEXT_NODE && $(node).text().trim().length > 0) {
                $anchor.append($(node).clone());
                $(node).remove();
              }
              else {
                $anchor.append($(node));
              }
            }
          }
        }
      }
    }

    /**
     * Wrap the direct content inside the default block tag.
     */
    function _wrap (temp, tables, blockquote, inner) {
      if (!editor.$wp) return false;

      if (typeof temp == 'undefined') temp = false;
      if (typeof tables == 'undefined') tables = false;
      if (typeof blockquote == 'undefined') blockquote = false;
      if (typeof inner == 'undefined') inner = false;

      // Wrap element.
      _wrapElement(editor.$el, temp);

      if (inner) {
        editor.$el.find('.fr-inner').each (function () {
          _wrapElement($(this), temp);
        })
      }

      // Wrap table contents.
      if (tables) {
        editor.$el.find('td, th').each (function () {
          _wrapElement($(this), temp);
        })
      }

      // Wrap table contents.
      if (blockquote) {
        editor.$el.find('blockquote').each (function () {
          _wrapElement($(this), temp);
        })
      }
    }

    /**
     * Unwrap temporary divs.
     */
    function unwrap () {
      editor.$el.find('div.fr-temp-div').each(function () {
        if ($(this).data('empty') || this.parentNode.tagName == 'LI') {
          $(this).replaceWith($(this).html());
        }
        else {
          $(this).replaceWith($(this).html() + '<br>');
        }
      });

      // Remove temp class from other blocks.
      editor.$el.find('.fr-temp-div').removeClass('fr-temp-div').filter(function () {
        return $(this).attr('class') == '';
      }).removeAttr('class');
    }

    /**
     * Add BR inside empty elements.
     */
    function fillEmptyBlocks () {
      var blocks = emptyBlocks();
      for (var i = 0; i < blocks.length; i++) {
        var block = blocks[i];
        if (block.getAttribute('contenteditable') != "false" &&
            block.querySelectorAll(editor.opts.htmlAllowedEmptyTags.join(':not(.fr-marker),') + ':not(.fr-marker)').length == 0 &&
            !editor.node.isVoid(block)) {
          if (block.tagName != 'TABLE') block.appendChild(editor.doc.createElement('br'));
        }
      }

      // Fix for https://github.com/froala/wysiwyg-editor/issues/1166#issuecomment-204549406.
      if (editor.browser.msie && editor.opts.enter == $.FE.ENTER_BR) {
        var contents = editor.node.contents(editor.$el.get(0));
        if (contents.length && contents[contents.length - 1].nodeType == Node.TEXT_NODE) {
          editor.$el.append('<br>');
        }
      }
    }

    /**
     * Get the blocks inside the editable area.
     */
    function blocks () {
      return editor.$el.find(blockTagsQuery());
    }

    /**
     * Clean the blank spaces between the block tags.
     */
    function cleanBlankSpaces (node) {
      if (typeof node == 'undefined') node = editor.$el.get(0);
      if (node && ['SCRIPT', 'STYLE', 'PRE'].indexOf(node.tagName) >= 0) return false;

      var contents = editor.node.contents(node);

      // Loop contents.
      for (var i = contents.length - 1; i >= 0; i--) {
        // Content is text and node is block.
        if (contents[i].nodeType == Node.TEXT_NODE) {
          var len = -1;

          // Remove middle spaces.
          contents[i].textContent = contents[i].textContent.replace(/(?!^)( ){2,}(?!$)/g, ' ');

          // Replace new lines with spaces.
          contents[i].textContent = contents[i].textContent.replace(/\n/g, ' ');

          // Replace begin/end spaces.
          contents[i].textContent = contents[i].textContent.replace(/^[ ]{2,}/g, ' ');
          contents[i].textContent = contents[i].textContent.replace(/[ ]{2,}$/g, ' ');

          if (editor.node.isBlock(node) || editor.node.isElement(node)) {
            // No previous siblings.
            if (!contents[i].previousSibling) {
              contents[i].textContent = contents[i].textContent.replace(/^ */,'');
            }

            // No next siblings.
            if (!contents[i].nextSibling) {
              contents[i].textContent = contents[i].textContent.replace(/ *$/,'');
            }

            if (contents[i].previousSibling && contents[i].nextSibling && contents[i].textContent == ' ') {
              if (contents[i].previousSibling && contents[i].nextSibling && editor.node.isBlock(contents[i].previousSibling) && editor.node.isBlock(contents[i].nextSibling)) {
                contents[i].textContent = '';
              }
              else {
                contents[i].textContent = "\n";
              }
            }
          }
        }
        else {
          cleanBlankSpaces(contents[i]);
        }
      }
    }

    function _isBlock (node) {
      return node && (editor.node.isBlock(node) || ['STYLE', 'SCRIPT', 'HEAD', 'BR', 'HR'].indexOf(node.tagName) >= 0 || node.nodeType == Node.COMMENT_NODE);
    }

    function doNormalize (node) {
      if (typeof node == 'undefined') node = editor.$el.get(0);

      if (node.nodeType == Node.ELEMENT_NODE && ['STYLE', 'SCRIPT', 'HEAD'].indexOf(node.tagName) < 0) {
        var contents = editor.node.contents(node);
        for (var i = contents.length - 1; i >= 0 ; i--) {
          if (!$(contents[i]).hasClass('fr-marker')) {
            var r = doNormalize(contents[i]);
            if (r == true) return true;
          }
        }
      }
      else if (node.nodeType == Node.TEXT_NODE && node.textContent.length > 0) {
        var prev_node = node.previousSibling;
        var next_node = node.nextSibling;

        if (_isBlock(prev_node) && _isBlock(next_node) && node.textContent.trim().length === 0) {
          return true;
        }
        else {
          var txt = node.textContent;
          txt = txt.replace(new RegExp($.FE.UNICODE_NBSP, 'g'), ' ');

          var new_text = ''
          for (var t = 0; t < txt.length; t++) {
            if (txt.charCodeAt(t) == 32 && (t === 0 || new_text.charCodeAt(t - 1) == 32)) {
              new_text += $.FE.UNICODE_NBSP;
            }
            else {
              new_text += txt[t];
            }
          }

          if (!node.nextSibling) new_text = new_text.replace(/ $/, $.FE.UNICODE_NBSP);
          if (node.previousSibling && !editor.node.isVoid(node.previousSibling)) new_text = new_text.replace(/^\u00A0([^ $])/, ' $1');

          new_text = new_text.replace(/([^ \u00A0])\u00A0([^ \u00A0])/g, '$1 $2');

          if (node.textContent != new_text) {
            return true;
          }
        }
      }

      return false;
    }

    /**
     * Extract a specific match for a RegEx.
     */
    function _extractMatch (html, re, id) {
      var reg_exp = new RegExp(re, 'gi');
      var matches = reg_exp.exec(html);

      if (matches) {
        return matches[id];
      }

      return null;
    }

    /**
     * Create new doctype.
     */
    function _newDoctype (string, doc) {
      var matches = string.match(/<!DOCTYPE ?([^ ]*) ?([^ ]*) ?"?([^"]*)"? ?"?([^"]*)"?>/i);
      if (matches) {
        return doc.implementation.createDocumentType(
          matches[1],
          matches[3],
          matches[4]
        )
      }
      else {
        return doc.implementation.createDocumentType('html');
      }
    }

    /**
     * Get string doctype of a document.
     */
    function getDoctype (doc) {
      var node = doc.doctype;
      var doctype = '<!DOCTYPE html>';
      if (node) {
        doctype = '<!DOCTYPE '
                  + node.name
                  + (node.publicId ? ' PUBLIC "' + node.publicId + '"' : '')
                  + (!node.publicId && node.systemId ? ' SYSTEM' : '')
                  + (node.systemId ? ' "' + node.systemId + '"' : '')
                  + '>';
      }

      return doctype;
    }

    /**
     * Normalize.
     */
    function _normalize () {
      // Wrap possible text.
      _wrap();

      // Clean blank spaces.
      cleanBlankSpaces();

      // Remove empty tags.
      cleanEmptyTags();

      // Normalize spaces.
      editor.spaces.normalize(null, true);

      // Add BR tag where it is necessary.
      editor.html.fillEmptyBlocks();

      // Clean quotes.
      editor.clean.quotes();

      // Clean lists.
      editor.clean.lists();

      // Clean tables.
      editor.clean.tables();

      // Convert to HTML5.
      editor.clean.toHTML5();

      // Restore selection.
      editor.selection.restore();

      // Check if editor is empty and add placeholder.
      checkIfEmpty();

      // Refresh placeholder.
      editor.placeholder.refresh();
    }

    function checkIfEmpty () {
      if (editor.core.isEmpty()) {
        if (defaultTag() != null) {
          // There is no block tag inside the editor.
          if (editor.$el.get(0).querySelectorAll(blockTagsQuery()).length === 0 &&
               editor.$el.get(0).querySelectorAll(editor.opts.htmlDoNotWrapTags.join(':not(.fr-marker),') + ':not(.fr-marker)').length === 0) {
            if (editor.core.hasFocus()) {
              editor.$el.html('<' + defaultTag() + '>' + $.FE.MARKERS + '<br/></' + defaultTag() + '>');
              editor.selection.restore();
            }
            else {
              editor.$el.html('<' + defaultTag() + '>' + '<br/></' + defaultTag() + '>');
            }
          }
        }
        else {
          // There is nothing in the editor.
          if (editor.$el.get(0).querySelectorAll('*:not(.fr-marker):not(br)').length === 0) {
            if (editor.core.hasFocus()) {
              editor.$el.html($.FE.MARKERS + '<br/>');
              editor.selection.restore();
            }
            else {
              editor.$el.html('<br/>');
            }
          }
        }
      }
    }

    function extractNode (html, tag) {
      return _extractMatch(html, '<' + tag +'[^>]*?>([\\w\\W]*)<\/' + tag + '>', 1);
    }

    function extractNodeAttrs (html, tag) {
      var $dv = $('<div ' + (_extractMatch(html, '<' + tag + '([^>]*?)>', 1) || '') + '>');
      return editor.node.rawAttributes($dv.get(0));
    }

    function extractDoctype (html) {
      return _extractMatch(html, '<!DOCTYPE([^>]*?)>', 0) || '<!DOCTYPE html>';
    }

    /**
     * Set HTML.
     */
    function set (html) {
      var clean_html = editor.clean.html(html || '', [], [], editor.opts.fullPage);

      if (!editor.opts.fullPage) {
        editor.$el.html(clean_html);
      }
      else {
        // Get BODY data.
        var body_html = (extractNode(clean_html, 'body') || (clean_html.indexOf('<body') >= 0 ? '' : clean_html));
        var body_attrs = extractNodeAttrs(clean_html, 'body');

        // Get HEAD data.
        var head_html = extractNode(clean_html, 'head') || '<title></title>';
        var head_attrs = extractNodeAttrs(clean_html, 'head');

        // Get HTML that might be in <head> other than meta tags.
        // https://github.com/froala/wysiwyg-editor/issues/1208
        var head_bad_html = $('<div>')
                              .append(head_html)
                              .find('base, link, meta, noscript, script, style, template, title')
                              .remove().end()
                              .html().trim();

        // Filter and keep only meta tags in <head>.
        // https://html.spec.whatwg.org/multipage/dom.html#metadata-content-2
        head_html = $('<div>')
                              .append(head_html)
                              .find('base, link, meta, noscript, script, style, template, title')
                              .map(function () {
                                return this.outerHTML;
                              }).toArray().join('');

        // Get DOCTYPE.
        var doctype = extractDoctype(clean_html);

        // Get HTML attributes.
        var html_attrs = extractNodeAttrs(clean_html, 'html');

        editor.$el.html(head_bad_html + '\n' + body_html);
        editor.node.clearAttributes(editor.$el.get(0));
        editor.$el.attr(body_attrs);
        editor.$el.addClass('fr-view');
        editor.$el.attr('spellcheck', editor.opts.spellcheck);
        editor.$el.attr('dir', editor.opts.direction);

        editor.$head.html(head_html);
        editor.node.clearAttributes(editor.$head.get(0));
        editor.$head.attr(head_attrs);

        editor.node.clearAttributes(editor.$html.get(0));

        editor.$html.attr(html_attrs);

        editor.iframe_document.doctype.parentNode.replaceChild(
          _newDoctype(doctype, editor.iframe_document),
          editor.iframe_document.doctype
        );
      }

      // Make sure the content is editable.
      var disabled = editor.edit.isDisabled();
      editor.edit.on();

      editor.core.injectStyle(editor.opts.iframeStyle);

      _normalize();

      if (!editor.opts.useClasses) {
        // Restore orignal attributes if present.
        editor.$el.find('[fr-original-class]').each (function () {
          this.setAttribute('class', this.getAttribute('fr-original-class'));
          this.removeAttribute('fr-original-class');
        });

        editor.$el.find('[fr-original-style]').each (function () {
          this.setAttribute('style', this.getAttribute('fr-original-style'));
          this.removeAttribute('fr-original-style');
        });
      }

      if (disabled) editor.edit.off();

      editor.events.trigger('html.set');
    }

    /**
     * Get HTML.
     */
    function get (keep_markers, keep_classes) {
      if (!editor.$wp) {
        return editor.$oel.clone()
                .removeClass('fr-view')
                .removeAttr('contenteditable')
                .get(0).outerHTML;
      }

      var html = '';

      editor.events.trigger('html.beforeGet');

      var specifity = function (selector) {
        var idRegex = /(#[^\s\+>~\.\[:]+)/g;
        var attributeRegex = /(\[[^\]]+\])/g;
        var classRegex = /(\.[^\s\+>~\.\[:]+)/g;
        var pseudoElementRegex = /(::[^\s\+>~\.\[:]+|:first-line|:first-letter|:before|:after)/gi;
        var pseudoClassWithBracketsRegex = /(:[\w-]+\([^\)]*\))/gi;
        // A regex for other pseudo classes, which don't have brackets
        var pseudoClassRegex = /(:[^\s\+>~\.\[:]+)/g;
        var elementRegex = /([^\s\+>~\.\[:]+)/g;

        // Remove the negation psuedo-class (:not) but leave its argument because specificity is calculated on its argument
        (function() {
          var regex = /:not\(([^\)]*)\)/g;
          if (regex.test(selector)) {
            selector = selector.replace(regex, '     $1 ');
          }
        }());

        var s = (selector.match(idRegex) || []).length * 100 +
                (selector.match(attributeRegex) || []).length * 10 +
                (selector.match(classRegex) || []).length * 10 +
                (selector.match(pseudoClassWithBracketsRegex) || []).length * 10 +
                (selector.match(pseudoClassRegex) || []).length * 10 +
                (selector.match(pseudoElementRegex) || []).length;

        // Remove universal selector and separator characters
        selector = selector.replace(/[\*\s\+>~]/g, ' ');

        // Remove any stray dots or hashes which aren't attached to words
        // These may be present if the user is live-editing this selector
        selector = selector.replace(/[#\.]/g, ' ');

        s += (selector.match(elementRegex) || []).length;

        return s;
      }

      // Convert STYLE from CSS files to inline style.
      var updated_elms = [];
      var elms_info = {};
      var i;
      if (!editor.opts.useClasses && !keep_classes) {
        for (i = 0; i < editor.doc.styleSheets.length; i++) {
          var rules;
          var head_style = 0;
          try {
            rules = editor.doc.styleSheets[i].cssRules;
            if (editor.doc.styleSheets[i].ownerNode && editor.doc.styleSheets[i].ownerNode.nodeType == 'STYLE') {
              head_style = 1;
            }
          }
          catch (ex) {
          }

          if (rules) {
            for (var idx = 0, len = rules.length; idx < len; idx++) {
              if (rules[idx].selectorText) {
                if (rules[idx].style.cssText.length > 0) {
                  var selector = rules[idx].selectorText.replace(/body |\.fr-view /g, '').replace(/::/g, ':');
                  var elms;
                  try {
                    elms = editor.$el.get(0).querySelectorAll(selector);
                  }
                  catch (ex) {
                    elms = [];
                  }

                  for (var j = 0; j < elms.length; j++) {
                    // Save original style.
                    if (!elms[j].getAttribute('fr-original-style') && elms[j].getAttribute('style')) {
                      elms[j].setAttribute('fr-original-style', elms[j].getAttribute('style'));
                      updated_elms.push(elms[j]);
                    }
                    else if (!elms[j].getAttribute('fr-original-style')) {
                      updated_elms.push(elms[j]);
                    }

                    if (!elms_info[elms[j]]) {
                      elms_info[elms[j]] = {};
                    }

                    // Compute specification.
                    var spec = head_style * 1000 + specifity(rules[idx].selectorText);

                    // Get CSS text of the rule.
                    var css_text = rules[idx].style.cssText.split(';');

                    // Get each rule.
                    for (var k = 0; k < css_text.length; k++) {
                      // Rule.
                      var rule = css_text[k].trim().split(':')[0];
                      if (!elms_info[elms[j]][rule]) {
                        elms_info[elms[j]][rule] = 0;

                        if ((elms[j].getAttribute('fr-original-style') || '').indexOf(rule + ':') >= 0) {
                          elms_info[elms[j]][rule] = 10000;
                        }
                      }

                      // Current spec is higher than the existing one.
                      if (spec >= elms_info[elms[j]][rule]) {
                        elms_info[elms[j]][rule] = spec;

                        if (css_text[k].trim().length) {
                          elms[j].style[rule.trim()] = css_text[k].trim().split(':')[1].trim();
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }

        // Save original class.
        for (i = 0; i < updated_elms.length; i++) {
          if (updated_elms[i].getAttribute('class')) {
            updated_elms[i].setAttribute('fr-original-class', updated_elms[i].getAttribute('class'));
            updated_elms[i].removeAttribute('class');
          }

          // Make sure that we have the inline style first.
          if ((updated_elms[i].getAttribute('fr-original-style') || '').trim().length > 0) {
            var original_rules = updated_elms[i].getAttribute('fr-original-style').split(';');
            for (var j = 0; j < original_rules.length; j++) {
              if (original_rules[j].indexOf(':') > 0) {
                updated_elms[i].style[original_rules[j].split(':')[0].trim()] = original_rules[j].split(':')[1].trim();
              }
            }
          }
        }
      }

      // If editor is not empty.
      if (!editor.core.isEmpty()) {
        if (typeof keep_markers == 'undefined') keep_markers = false;

        if (!editor.opts.fullPage) {
          html = editor.$el.html();
        }
        else {
          html = getDoctype(editor.iframe_document);
          editor.$el.removeClass('fr-view');
          html += '<html' + editor.node.attributes(editor.$html.get(0)) + '>' + editor.$html.html() + '</html>';
          editor.$el.addClass('fr-view');
        }
      }
      else if (editor.opts.fullPage) {
        html = getDoctype(editor.iframe_document);
        html += '<html' + editor.node.attributes(editor.$html.get(0)) + '>' + editor.$html.find('head').get(0).outerHTML + '<body></body></html>';
      }

      // Remove unwanted attributes.
      if (!editor.opts.useClasses && !keep_classes) {
        for (i = 0; i < updated_elms.length; i++) {
          if (updated_elms[i].getAttribute('fr-original-class')) {
            updated_elms[i].setAttribute('class', updated_elms[i].getAttribute('fr-original-class'));
            updated_elms[i].removeAttribute('fr-original-class');
          }

          if (updated_elms[i].getAttribute('fr-original-style')) {
            updated_elms[i].setAttribute('style', updated_elms[i].getAttribute('fr-original-style'));
            updated_elms[i].removeAttribute('fr-original-style');
          }
          else {
            updated_elms[i].removeAttribute('style');
          }
        }
      }

      // Clean helpers.
      if (editor.opts.fullPage) {
        html = html.replace(/<style data-fr-style="true">(?:[\w\W]*?)<\/style>/g, '');
        html = html.replace(/<link(?:[\w\W]*?)data-fr-style="true"(?:[\w\W]*?)>/g, '');
        html = html.replace(/<style(?:[\w\W]*?)class="firebugResetStyles"(?:[\w\W]*?)>(?:[\w\W]*?)<\/style>/g, '');
        html = html.replace(/<body((?:[\w\W]*?)) spellcheck="true"((?:[\w\W]*?))>((?:[\w\W]*?))<\/body>/g, '<body$1$2>$3</body>');
        html = html.replace(/<body((?:[\w\W]*?)) contenteditable="(true|false)"((?:[\w\W]*?))>((?:[\w\W]*?))<\/body>/g, '<body$1$3>$4</body>');

        html = html.replace(/<body((?:[\w\W]*?)) dir="([\w]*)"((?:[\w\W]*?))>((?:[\w\W]*?))<\/body>/g, '<body$1$3>$4</body>');
        html = html.replace(/<body((?:[\w\W]*?))class="([\w\W]*?)(fr-rtl|fr-ltr)([\w\W]*?)"((?:[\w\W]*?))>((?:[\w\W]*?))<\/body>/g, '<body$1class="$2$4"$5>$6</body>');
        html = html.replace(/<body((?:[\w\W]*?)) class=""((?:[\w\W]*?))>((?:[\w\W]*?))<\/body>/g, '<body$1$2>$3</body>');
      }

      // Ampersand fix.
      if (editor.opts.htmlSimpleAmpersand) {
        html = html.replace(/\&amp;/gi, '&');
      }

      editor.events.trigger('html.afterGet');

      // Remove markers.
      if (!keep_markers) {
        html = html.replace(/<span[^>]*? class\s*=\s*["']?fr-marker["']?[^>]+>\u200b<\/span>/gi, '');
      }

      html = editor.clean.invisibleSpaces(html);

      var new_html = editor.events.chainTrigger('html.get', html);
      if (typeof new_html == 'string') {
        html = new_html;
      }

      // Deal with pre.
      html = html.replace(/<pre(?:[\w\W]*?)>(?:[\w\W]*?)<\/pre>/g, function (str) {
        return str.replace(/<br>/g, '\n');
      });

      return html;
    }

    /**
     * Get selected HTML.
     */
    function getSelected () {
      var wrapSelection = function (container, node) {
        while (node && (node.nodeType == Node.TEXT_NODE || !editor.node.isBlock(node)) && !editor.node.isElement(node)) {
          if (node && node.nodeType != Node.TEXT_NODE) {
            $(container).wrapInner(editor.node.openTagString(node) + editor.node.closeTagString(node));
          }

          node = node.parentNode;
        }

        if (node && container.innerHTML == node.innerHTML) {
          container.innerHTML = node.outerHTML;
        }
      }

      var selectionParent = function () {
        var parent = null;
        var sel;

        if (editor.win.getSelection) {
          sel = editor.win.getSelection();
          if (sel && sel.rangeCount) {
            parent = sel.getRangeAt(0).commonAncestorContainer;
            if (parent.nodeType != Node.ELEMENT_NODE) {
              parent = parent.parentNode;
            }
          }
        } else if ((sel = editor.doc.selection) && sel.type != 'Control') {
          parent = sel.createRange().parentElement();
        }

        if (parent != null && ($.inArray(editor.$el.get(0), $(parent).parents()) >= 0 || parent == editor.$el.get(0))) {
          return parent;
        }
        else {
          return null;
        }
      }

      var html = '';
      if (typeof editor.win.getSelection != 'undefined') {

        // Multiple ranges hack.
        if (editor.browser.mozilla) {
          editor.selection.save();
          if (editor.$el.find('.fr-marker[data-type="false"]').length > 1) {
            editor.$el.find('.fr-marker[data-type="false"][data-id="0"]').remove();
            editor.$el.find('.fr-marker[data-type="false"]:last').attr('data-id', '0');
            editor.$el.find('.fr-marker').not('[data-id="0"]').remove();
          }
          editor.selection.restore();
        }

        var ranges = editor.selection.ranges();
        for (var i = 0; i < ranges.length; i++) {
          var container = document.createElement('div');
          container.appendChild(ranges[i].cloneContents());
          wrapSelection(container, selectionParent());

          // Fix for https://github.com/froala/wysiwyg-editor/issues/1010.
          if ($(container).find('.fr-element').length > 0) {
            container = editor.$el.get(0);
          }

          html += container.innerHTML;
        }
      }

      else if (typeof editor.doc.selection != 'undefined') {
        if (editor.doc.selection.type == 'Text') {
          html = editor.doc.selection.createRange().htmlText;
        }
      }
      return html;
    }

    function _hasBlockTags (html) {
      var $tmp = $('<div>').html(html);
      return $tmp.find(blockTagsQuery()).length > 0;
    }

    function _setCursorAtEnd (html) {
      var tmp = editor.doc.createElement('div');
      tmp.innerHTML = html;

      editor.selection.setAtEnd(tmp);

      return tmp.innerHTML;
    }

    function escapeEntities (str) {
      return str.replace(/</gi, '&lt;')
                .replace(/>/gi, '&gt;')
                .replace(/"/gi, '&quot;')
                .replace(/'/gi, '&apos;')
    }

    /**
     * Insert HTML.
     */
    function insert (dirty_html, clean, do_split) {
      // There is no selection.
      if (!editor.selection.isCollapsed()) {
        editor.selection.remove();
      }

      var clean_html;
      if (!clean) {
        clean_html = editor.clean.html(dirty_html);
      }
      else {
        clean_html = dirty_html;
      }

      clean_html = clean_html.replace(/\r|\n/g, ' ');

      if (dirty_html.indexOf('class="fr-marker"') < 0) {
        clean_html = _setCursorAtEnd(clean_html);
      }

      if (editor.core.isEmpty()) {
        editor.$el.html(clean_html);
      }
      else {
        // Insert a marker.
        var marker = editor.markers.insert();

        if (!marker) {
          editor.$el.append(clean_html);
        }
        else {
          // Check if HTML contains block tags and if so then break the current HTML.
          var deep_parent;
          if ((_hasBlockTags(clean_html) || do_split) && (deep_parent = editor.node.deepestParent(marker))) {
            var marker = editor.markers.split();
            if (!marker) return false;
            $(marker).replaceWith(clean_html);
          }
          else {
            $(marker).replaceWith(clean_html);
          }
        }
      }

      _normalize();

      editor.events.trigger('html.inserted');
    }

    /**
     * Clean those tags that have an invisible space inside.
     */
    function cleanWhiteTags (ignore_selection) {
      var current_el = null;
      if (typeof ignore_selection == 'undefined') {
        current_el = editor.selection.element();
      }

      var possible_elements;
      var removed;
      do {
        removed = false;
        possible_elements = editor.$el.get(0).querySelectorAll('*:not(.fr-marker)');
        for (var i = 0; i < possible_elements.length; i++) {
          var el = possible_elements[i];
          if (current_el == el) continue;

          var text = el.textContent;
          if (el.children.length === 0 && text.length === 1 && text.charCodeAt(0) == 8203) {
            $(el).remove();
            removed = true;
          }
        }
      } while (removed);
    }

    /**
     * Initialization.
     */
    function _init () {
      var cleanTags = function () {
        cleanWhiteTags();

        if (editor.placeholder) editor.placeholder.refresh();
      }

      editor.events.on('mouseup', cleanTags);
      editor.events.on('keydown', cleanTags);
      editor.events.on('contentChanged', checkIfEmpty);
    }

    return {
      defaultTag: defaultTag,
      emptyBlocks: emptyBlocks,
      emptyBlockTagsQuery: emptyBlockTagsQuery,
      blockTagsQuery: blockTagsQuery,
      fillEmptyBlocks: fillEmptyBlocks,
      cleanEmptyTags: cleanEmptyTags,
      cleanWhiteTags: cleanWhiteTags,
      doNormalize: doNormalize,
      cleanBlankSpaces: cleanBlankSpaces,
      blocks: blocks,
      getDoctype: getDoctype,
      set: set,
      get: get,
      getSelected: getSelected,
      insert: insert,
      wrap: _wrap,
      unwrap: unwrap,
      escapeEntities: escapeEntities,
      checkIfEmpty: checkIfEmpty,
      extractNode: extractNode,
      extractNodeAttrs: extractNodeAttrs,
      extractDoctype: extractDoctype,
      _init: _init
    }
  }


  // Extend defaults.
  $.extend($.FE.DEFAULTS, {
    height: null,
    heightMax: null,
    heightMin: null,
    width: null
  });

  $.FE.MODULES.size = function (editor) {
    function syncIframe () {
      refresh();

      if (editor.opts.height) {
        editor.$el.css('minHeight', editor.opts.height - editor.helpers.getPX(editor.$el.css('padding-top')) - editor.helpers.getPX(editor.$el.css('padding-bottom')));
      }

      editor.$iframe.height(editor.$el.outerHeight(true));
    }

    function refresh () {
      if (editor.opts.heightMin) {
        editor.$el.css('minHeight', editor.opts.heightMin);
      }
      else {
        editor.$el.css('minHeight', '');
      }

      if (editor.opts.heightMax) {
        editor.$wp.css('maxHeight', editor.opts.heightMax);
        editor.$wp.css('overflow', 'auto');
      }
      else {
        editor.$wp.css('maxHeight', '');
        editor.$wp.css('overflow', '');
      }

      // Set height.
      if (editor.opts.height) {
        editor.$wp.height(editor.opts.height);
        editor.$el.css('minHeight', editor.opts.height - editor.helpers.getPX(editor.$el.css('padding-top')) - editor.helpers.getPX(editor.$el.css('padding-bottom')));
        editor.$wp.css('overflow', 'auto');
      }
      else {
        editor.$wp.css('height', '');
        if (!editor.opts.heightMin) editor.$el.css('minHeight', '');
        if (!editor.opts.heightMax) editor.$wp.css('overflow', '');
      }

      if (editor.opts.width) editor.$box.width(editor.opts.width);
    }

    function _init () {
      if (!editor.$wp) return false;

      refresh();

      // Sync iframe height.
      if (editor.$iframe) {
        editor.events.on('keyup', syncIframe);
        editor.events.on('commands.after', syncIframe);
        editor.events.on('html.set', syncIframe);
        editor.events.on('init', syncIframe);
        editor.events.on('initialized', syncIframe);
      }
    }

    return {
      _init: _init,
      syncIframe: syncIframe,
      refresh: refresh
    }
  };


  // Extend defaults.
  $.extend($.FE.DEFAULTS, {
    language: null
  });

  $.FE.LANGUAGE = {};

  $.FE.MODULES.language = function (editor) {
    var lang;

    /**
     * Translate.
     */
    function translate (str) {
      if (lang && lang.translation[str]) {
        return lang.translation[str];
      }
      else {
        return str;
      }
    }

    /* Initialize */
    function _init () {
      // Load lang.
      if ($.FE.LANGUAGE) {
        lang = $.FE.LANGUAGE[editor.opts.language];
      }

      // Set direction.
      if (lang && lang.direction) {
        editor.opts.direction = lang.direction;
      }
    }

    return {
      _init: _init,
      translate: translate
    }
  };



  // Extend defaults.
  $.extend($.FE.DEFAULTS, {
    placeholderText: 'Type something'
  });

  $.FE.MODULES.placeholder = function (editor) {
    /* Show placeholder. */
    function show () {
      if (!editor.$placeholder) _add();

      // Determine the placeholder position based on the first element inside editor.
      var margin_top = 0;
      var margin_left = 0;
      var padding_top = 0;
      var padding_left = 0;
      var contents = editor.node.contents(editor.$el.get(0));
      if (contents.length && contents[0].nodeType == Node.ELEMENT_NODE) {
        var $first_node = $(contents[0]);
        if (!editor.opts.toolbarInline) {
          margin_top = editor.helpers.getPX($first_node.css('margin-top'));
          padding_top = editor.helpers.getPX($first_node.css('padding-top'));
          margin_left = editor.helpers.getPX($first_node.css('margin-left'));
          padding_left = editor.helpers.getPX($first_node.css('padding-left'));
        }

        editor.$placeholder.css('font-size', $first_node.css('font-size'));
        editor.$placeholder.css('line-height', $first_node.css('line-height'));
      }
      else {
        editor.$placeholder.css('font-size', editor.$el.css('font-size'));
        editor.$placeholder.css('line-height', editor.$el.css('line-height'));
      }

      editor.$wp.addClass('show-placeholder');
      editor.$placeholder
        .css({
            marginTop: Math.max(editor.helpers.getPX(editor.$el.css('margin-top')), margin_top),
            paddingTop: Math.max(editor.helpers.getPX(editor.$el.css('padding-top')), padding_top),
            paddingLeft: Math.max(editor.helpers.getPX(editor.$el.css('padding-left')), padding_left),
            marginLeft: Math.max(editor.helpers.getPX(editor.$el.css('margin-left')), margin_left)
          })
        .text(editor.language.translate(editor.opts.placeholderText || editor.$oel.attr('placeholder') || ''));

      editor.$placeholder.html(editor.$placeholder.text().replace(/\n/g, '<br>'));
    }

    /* Hide placeholder. */
    function hide () {
      editor.$wp.removeClass('show-placeholder');
    }

    /* Check if placeholder is visible */
    function isVisible () {
      return !editor.$wp ? true : editor.$wp.hasClass('show-placeholder');
    }

    /* Refresh placeholder. */
    function refresh () {
      if (!editor.$wp) return false;

      if (editor.core.isEmpty()) {
        show();
      }
      else {
        hide();
      }
    }

    function _add () {
      editor.$placeholder = $('<span class="fr-placeholder"></span>');
      editor.$wp.append(editor.$placeholder);
    }

    /* Initialize. */
    function _init () {
      if (!editor.$wp) return false;

      editor.events.on('init input keydown keyup contentChanged initialized', refresh);
    }

    return {
      _init: _init,
      show: show,
      hide: hide,
      refresh: refresh,
      isVisible: isVisible
    }
  };


  $.FE.MODULES.edit = function (editor) {
    /**
     * Disable editing design.
     */
    function disableDesign () {
      if (editor.browser.mozilla) {
        try {
          editor.doc.execCommand('enableObjectResizing', false, 'false');
          editor.doc.execCommand('enableInlineTableEditing', false, 'false');
        }
        catch (ex) {

        }
      }

      if (editor.browser.msie) {
        try {
          editor.doc.body.addEventListener('mscontrolselect', function (e) {
            e.preventDefault();
            return false;
          });
        }
        catch (ex) {

        }
      }
    }

    var disabled = false;

    /**
     * Add contneteditable attribute.
     */
    function on () {
      if (editor.$wp) {
        editor.$el.attr('contenteditable', true);
        editor.$el.removeClass('fr-disabled');
        if (editor.$tb) editor.$tb.removeClass('fr-disabled');
        disableDesign();
      }
      else if (editor.$el.is('a')) {
        editor.$el.attr('contenteditable', true);
      }

      disabled = false;
    }

    /**
     * Remove contenteditable attribute.
     */
    function off () {
      if (editor.$wp) {
        editor.$el.attr('contenteditable', false);
        editor.$el.addClass('fr-disabled');
        if (editor.$tb) editor.$tb.addClass('fr-disabled');
      }
      else if (editor.$el.is('a')) {
        editor.$el.attr('contenteditable', false);
      }

      disabled = true;
    }

    function isDisabled () {
      return disabled;
    }

    return {
      on: on,
      off: off,
      disableDesign: disableDesign,
      isDisabled: isDisabled
    }
  };



  // Extend defaults.
  $.extend($.FE.DEFAULTS, {
    editorClass: null,
    typingTimer: 500,
    iframe: false,
    requestWithCORS: true,
    requestHeaders: {},
    useClasses: true,
    spellcheck: true,
    iframeStyle: 'html{margin: 0px;}body{padding:10px;background:transparent;color:#000000;position:relative;z-index: 2;-webkit-user-select:auto;margin:0px;overflow:hidden;min-height:20px;}body:after{content:"";display:block;clear:both;}',
    iframeStyleFiles: [],
    direction: 'auto',
    zIndex: 1,
    disableRightClick: false,
    scrollableContainer: 'body',
    keepFormatOnDelete: false,
    theme: null
  })

  $.FE.MODULES.core = function(editor) {
    function injectStyle(style) {
      if (editor.opts.iframe) {
        editor.$head.find('style[data-fr-style], link[data-fr-style]').remove();
        editor.$head.append('<style data-fr-style="true">' + style + '</style>');

        for (var i = 0; i < editor.opts.iframeStyleFiles.length; i++) {
          editor.$head.append('<link data-fr-style="true" rel="stylesheet" href="' + editor.opts.iframeStyleFiles[i] + '">');
        }
      }
    }

    function _initElementStyle() {
      if (!editor.opts.iframe) {
        editor.$el.addClass('fr-element fr-view');
      }
    }

    /**
     * Init the editor style.
     */

    function _initStyle() {
      editor.$box.addClass('fr-box' + (editor.opts.editorClass ? ' ' + editor.opts.editorClass : ''));
      editor.$wp.addClass('fr-wrapper');

      _initElementStyle();

      if (editor.opts.iframe) {
        editor.$iframe.addClass('fr-iframe');
        editor.$el.addClass('fr-view');

        for (var i = 0; i < editor.o_doc.styleSheets.length; i++) {
          var rules;
          try {
            rules = editor.o_doc.styleSheets[i].cssRules;
          }
          catch (ex) {

          }

          if (rules) {
            for (var idx = 0, len = rules.length; idx < len; idx++) {
              if (rules[idx].selectorText && (rules[idx].selectorText.indexOf('.fr-view') === 0 || rules[idx].selectorText.indexOf('.fr-element') === 0)) {
                if (rules[idx].style.cssText.length > 0) {
                  if (rules[idx].selectorText.indexOf('.fr-view') === 0) {
                    editor.opts.iframeStyle += rules[idx].selectorText.replace(/\.fr-view/g, 'body') + '{' + rules[idx].style.cssText + '}';
                  }
                  else {
                    editor.opts.iframeStyle += rules[idx].selectorText.replace(/\.fr-element/g, 'body') + '{' + rules[idx].style.cssText + '}';
                  }
                }
              }
            }
          }
        }
      }

      if (editor.opts.direction != 'auto') {
        editor.$box.removeClass('fr-ltr fr-rtl').addClass('fr-' + editor.opts.direction);
      }
      editor.$el.attr('dir', editor.opts.direction);
      editor.$wp.attr('dir', editor.opts.direction);

      if (editor.opts.zIndex > 1) {
        editor.$box.css('z-index', editor.opts.zIndex);
      }

      if (editor.opts.theme) {
        editor.$box.addClass(editor.opts.theme + '-theme');
      }
    }

    /**
     * Determine if the editor is empty.
     */

    function isEmpty() {
      return editor.node.isEmpty(editor.$el.get(0));
    }

    /**
     * Check if the browser allows drag and init it.
     */

    function _initDrag() {
      // Drag and drop support.
      editor.drag_support = {
        filereader: typeof FileReader != 'undefined',
        formdata: !! editor.win.FormData,
        progress: 'upload' in new XMLHttpRequest()
      };
    }

    /**
     * Return an XHR object.
     */

    function getXHR(url, method) {
      var xhr = new XMLHttpRequest();

      // Make it async.
      xhr.open(method, url, true);

      // Set with credentials.
      if (editor.opts.requestWithCORS) {
        xhr.withCredentials = true;
      }

      // Set headers.
      for (var header in editor.opts.requestHeaders) {
        if (editor.opts.requestHeaders.hasOwnProperty(header)) {
          xhr.setRequestHeader(header, editor.opts.requestHeaders[header]);
        }
      }

      return xhr;
    }

    function _destroy (html) {
      if (editor.$oel.get(0).tagName == 'TEXTAREA') {
        editor.$oel.val(html);
      }

      if (editor.$wp) {
        if (editor.$oel.get(0).tagName == 'TEXTAREA') {
          editor.$el.html('');
          editor.$wp.html('');
          editor.$box.replaceWith(editor.$oel);
          editor.$oel.show();
        } else {
          editor.$wp.replaceWith(html);
          editor.$el.html('');
          editor.$box.removeClass('fr-view fr-ltr fr-box ' + (editor.opts.editorClass || ''));

          if (editor.opts.theme) {
            editor.$box.addClass(editor.opts.theme + '-theme');
          }
        }
      }

      this.$wp = null;
      this.$el = null;
      this.$box = null;
    }

    function hasFocus() {
      if (editor.browser.mozilla && editor.helpers.isMobile()) return editor.selection.inEditor();
      return editor.node.hasFocus(editor.$el.get(0)) || editor.$el.find('*:focus').length > 0;
    }

    function sameInstance ($obj) {
      if (!$obj) return false;

      var inst = $obj.data('instance');

      return (inst ? inst.id == editor.id : false);
    }

    /**
     * Tear up.
     */

    function _init () {
      $.FE.INSTANCES.push(editor);

      _initDrag();

      // Call initialization methods.
      if (editor.$wp) {
        _initStyle();
        editor.html.set(editor._original_html);

        // Set spellcheck.
        editor.$el.attr('spellcheck', editor.opts.spellcheck);

        // Disable autocomplete.
        if (editor.helpers.isMobile()) {
          editor.$el.attr('autocomplete', editor.opts.spellcheck ? 'on' : 'off');
          editor.$el.attr('autocorrect', editor.opts.spellcheck ? 'on' : 'off');
          editor.$el.attr('autocapitalize', editor.opts.spellcheck ? 'on' : 'off');
        }

        // Disable right click.
        if (editor.opts.disableRightClick) {
          editor.events.$on(editor.$el, 'contextmenu', function(e) {
            if (e.button == 2) {
              return false;
            }
          });
        }

        try {
          editor.doc.execCommand('styleWithCSS', false, false);
        } catch (ex) {

        }
      }

      // Do not allow drop inside the editor.
      editor.events.on('drop', function (e) {
        e.preventDefault();
        e.stopPropagation();
      });

      if (editor.$oel.get(0).tagName == 'TEXTAREA') {
        // Sync on contentChanged.
        editor.events.on('contentChanged', function() {
          editor.$oel.val(editor.html.get());
        });

        // Set HTML on form submit.
        editor.events.on('form.submit', function() {
          editor.$oel.val(editor.html.get());
        });

        editor.events.on('form.reset', function () {
          editor.html.set(editor._original_html);
        })

        editor.$oel.val(editor.html.get());
      }

      // iOS focus fix.
      if (editor.helpers.isIOS()) {
        editor.events.$on(editor.$doc, 'selectionchange', function () {
          if (!editor.$doc.get(0).hasFocus()) {
            editor.$win.get(0).focus();
          }
        });
      }

      editor.events.trigger('init');
    }

    return {
      _init: _init,
      destroy: _destroy,
      isEmpty: isEmpty,
      getXHR: getXHR,
      injectStyle: injectStyle,
      hasFocus: hasFocus,
      sameInstance: sameInstance
    }
  }


  'use strict';

  $.FE.MODULES.format = function (editor) {
    /**
     * Create open tag string.
     */
    function _openTag (tag, attrs) {
      var str = '<' + tag;

      for (var key in attrs) {
        if (attrs.hasOwnProperty(key)) {
          str += ' ' + key + '="' + attrs[key] + '"';
        }
      }

      str += '>';

      return str;
    }

    /**
     * Create close tag string.
     */
    function _closeTag (tag) {
      return '</' + tag + '>';
    }

    /**
     * Create query for the current format.
     */
    function _query (tag, attrs) {
      var selector = tag;

      for (var key in attrs) {
        if (attrs.hasOwnProperty(key)) {
          if (key == 'id') tag += '#' + attrs[key];
          else if (key == 'class') tag += '.' + attrs[key];
          else tag += '[' + key + '="' + attrs[key] + '"]';
        }
      }

      return selector;
    }

    /**
     * Test matching element.
     */
    function _matches (el, selector) {
      if (!el || el.nodeType != Node.ELEMENT_NODE) return false;

      return (el.matches || el.matchesSelector || el.msMatchesSelector || el.mozMatchesSelector || el.webkitMatchesSelector || el.oMatchesSelector).call(el, selector);
    }

    /**
     * Apply format to the current node till we find a marker.
     */
    function _processNodeFormat (start_node, tag, attrs) {
      // No start node.
      if (!start_node) return;

      // If we are in a block process starting with the first child.
      if (editor.node.isBlock(start_node)) {
        _processNodeFormat(start_node.firstChild, tag, attrs);
        return false;
      }

      // Create new element.
      var $span = $(_openTag(tag, attrs)).insertBefore(start_node);

      // Start with the next sibling of the current node.
      var node = start_node;

      // Search while there is a next node.
      // Next node is not marker.
      // Next node does not contain marker.
      while (node && !$(node).is('.fr-marker') && $(node).find('.fr-marker').length == 0) {
        var tmp = node;
        node = node.nextSibling;
        $span.append(tmp);
      }

      // If there is no node left at the right look at parent siblings.
      if (!node) {
        var p_node = $span.get(0).parentNode;
        while (p_node && !p_node.nextSibling && !editor.node.isElement(p_node)) {
          p_node = p_node.parentNode;
        }

        if (p_node) {
          var sibling = p_node.nextSibling;
          if (sibling) {
            // Parent sibling is block then look next.
            if (!editor.node.isBlock(sibling)) {
              _processNodeFormat(sibling, tag, attrs);
            }
            else {
              _processNodeFormat(sibling.firstChild, tag, attrs);
            }
          }
        }
      }
      // Start processing child nodes if there is a marker.
      else if ($(node).find('.fr-marker').length) {
        _processNodeFormat(node.firstChild, tag, attrs);
      }

      if ($span.is(':empty')) {
        $span.remove();
      }
    }

    /**
     * Apply tag format.
     */
    function apply (tag, attrs) {
      if (typeof attrs == 'undefined') attrs = {};
      if (attrs.style) {
        delete attrs.style;
      }

      // Selection is collapsed.
      if (editor.selection.isCollapsed()) {
        editor.markers.insert();
        var $marker = editor.$el.find('.fr-marker');
        $marker.replaceWith(_openTag(tag, attrs) + $.FE.INVISIBLE_SPACE + $.FE.MARKERS + _closeTag(tag));
        editor.selection.restore();
      }

      // Selection is not collapsed.
      else {
        editor.selection.save();

        // Check if selection can be deleted.
        var start_marker = editor.$el.find('.fr-marker[data-type="true"]').get(0).nextSibling;
        _processNodeFormat(start_marker, tag, attrs);

        // Clean inner spans.
        var inner_spans;
        do {
          inner_spans = editor.$el.find(_query(tag, attrs) + ' > ' + _query(tag, attrs));
          inner_spans.each (function () {
            $(this).replaceWith(this.innerHTML);
          });
        } while (inner_spans.length);

        editor.$el.get(0).normalize();

        // Have markers inside the new tag.
        var markers = editor.$el.get(0).querySelectorAll('.fr-marker');
        for (var i = 0; i < markers.length; i++) {
          var $mk = $(markers[i]);
          if ($mk.data('type') == true) {
            if (_matches($mk.get(0).nextSibling, _query(tag, attrs))) {
              $mk.next().prepend($mk);
            }
          }
          else {
            if (_matches($mk.get(0).previousSibling, _query(tag, attrs))) {
              $mk.prev().append($mk);
            }
          }
        }

        editor.selection.restore();
      }
    }

    /**
     * Split at current node the parents with tag.
     */
    function _split ($node, tag, attrs, collapsed) {
      if (!collapsed) {
        var changed = false;
        if ($node.data('type') === true) {
          while (editor.node.isFirstSibling($node.get(0)) && !$node.parent().is(editor.$el)) {
            $node.parent().before($node);
            changed = true;
          }
        }
        else if ($node.data('type') === false) {
          while (editor.node.isLastSibling($node.get(0)) && !$node.parent().is(editor.$el)) {
            $node.parent().after($node);
            changed = true;
          }
        }

        if (changed) return true;
      }

      // Check if current node has parents which match our tag.
      if ($node.parents(tag).length || typeof tag == 'undefined') {
        var close_str = '';
        var open_str = '';
        var $p_node = $node.parent();

        // Do not split when parent is block.
        if ($p_node.is(editor.$el) || editor.node.isBlock($p_node.get(0))) return false;

        // Check undefined so that we
        while ((typeof tag == 'undefined' && !editor.node.isBlock($p_node.parent().get(0))) || (typeof tag != 'undefined' && !_matches($p_node.get(0), _query(tag, attrs)))) {
          close_str = close_str + editor.node.closeTagString($p_node.get(0));
          open_str = editor.node.openTagString($p_node.get(0)) + open_str;
          $p_node = $p_node.parent();
        }

        // Node STR.
        var node_str = $node.get(0).outerHTML;

        // Replace node with marker.
        $node.replaceWith('<span id="mark"></span>');

        // Rebuild the HTML for the node.
        var p_html = $p_node.html().replace(/<span id="mark"><\/span>/, close_str + editor.node.closeTagString($p_node.get(0)) + open_str + node_str + close_str + editor.node.openTagString($p_node.get(0)) + open_str);
        $p_node.replaceWith(editor.node.openTagString($p_node.get(0)) + p_html + editor.node.closeTagString($p_node.get(0)));

        return true;
      }

      return false;
    }

    /**
     * Process node remove.
     */
    function _processNodeRemove ($node, should_remove, tag, attrs) {
      // Get contents.
      var contents = editor.node.contents($node.get(0));

      // Loop contents.
      for (var i = 0; i < contents.length; i++) {
        var node = contents[i];

        // We found a marker => change should_remove flag.
        if ($(node).hasClass('fr-marker')) {
          should_remove = (should_remove + 1) % 2;
        }
        // We should remove.
        else if (should_remove) {
          // Check if we have a marker inside it.
          if ($(node).find('.fr-marker').length > 0) {
            should_remove = _processNodeRemove($(node), should_remove, tag, attrs);
          }
          // Remove everything starting with the most inner nodes.
          else {
            $($(node).find(tag || '*').get().reverse()).each(function() {
              if (!editor.node.isBlock(this) && !editor.node.isVoid(this)) {
                $(this).replaceWith(this.innerHTML);
              }
            });

            // Check inner nodes.
            if ((typeof tag == 'undefined' && node.nodeType == Node.ELEMENT_NODE && !editor.node.isVoid(node) && !editor.node.isBlock(node)) || _matches(node, _query(tag, attrs))) {
              $(node).replaceWith(node.innerHTML);
            }
          }
        }
        else {
          // There is a marker.
          if ($(node).find('.fr-marker').length > 0) {
            should_remove = _processNodeRemove($(node), should_remove, tag, attrs);
          }
        }
      }

      return should_remove;
    }

    /**
     * Remove tag.
     */
    function remove (tag, attrs) {
      if (typeof attrs == 'undefined') attrs = {};
      if (attrs.style) {
        delete attrs.style;
      }

      var collapsed = editor.selection.isCollapsed();
      editor.selection.save();

      // Split at start and end marker.
      var reassess = true;
      while (reassess) {
        reassess = false;
        var markers = editor.$el.find('.fr-marker');
        for (var i = 0; i < markers.length; i++) {
          if (_split($(markers[i]), tag, attrs, collapsed)) {
            reassess = true;
            break;
          }
        }
      }

      // Remove format between markers.
      _processNodeRemove(editor.$el, 0, tag, attrs);

      // Selection is collapsed => add invisible spaces.
      if (collapsed) {
        editor.$el.find('.fr-marker').before($.FE.INVISIBLE_SPACE).after($.FE.INVISIBLE_SPACE);
      }

      editor.html.cleanEmptyTags();

      editor.$el.get(0).normalize();
      editor.selection.restore();
    }

    /**
     * Toggle format.
     */
    function toggle (tag, attrs) {
      if (is(tag, attrs)) {
        remove(tag, attrs);
      }
      else {
        apply(tag, attrs);
      }
    }

    /**
     * Clean format.
     */
    function _cleanFormat (elem, prop) {
      var $elem = $(elem);
      $elem.css(prop, '');

      if ($elem.attr('style') === '') {
        $elem.replaceWith($elem.html());
      }
    }

    /**
     * Filter spans with specific property.
     */
    function _filterSpans (elem, prop) {
      return $(elem).attr('style').indexOf(prop + ':') === 0 || $(elem).attr('style').indexOf(';' + prop + ':') >= 0 || $(elem).attr('style').indexOf('; ' + prop + ':') >= 0;
    };

    /**
     * Apply inline style.
     */
    function applyStyle (prop, val) {
      // Selection is collapsed.
      if (editor.selection.isCollapsed()) {
        editor.markers.insert();
        var $marker = editor.$el.find('.fr-marker');
        var $parent = $marker.parent();

        // https://github.com/froala/wysiwyg-editor/issues/1084
        if (editor.node.openTagString($parent.get(0)) == '<span style="' + prop + ': ' + $parent.css(prop) + ';">' && editor.node.isEmpty($parent.get(0))) {
          $parent.replaceWith('<span style="' + prop + ': ' + val + ';">' + $.FE.INVISIBLE_SPACE + $.FE.MARKERS + '</span>');
        }
        else if (editor.node.isEmpty($parent.get(0)) && $parent.is('span')) {
          $marker.replaceWith($.FE.MARKERS);
          $parent.css(prop, val);
        }
        else {
          $marker.replaceWith('<span style="' + prop + ': ' + val + ';">' + $.FE.INVISIBLE_SPACE + $.FE.MARKERS + '</span>');
        }

        editor.selection.restore();
      }
      else {
        editor.selection.save();

        // When removing selection we should make sure we have selection outside of the first/last parent node.
        if (val === null) {
          var markers = editor.$el.find('.fr-marker');
          for (var i = 0; i < markers.length; i++) {
            var $marker = $(markers[i]);
            if ($marker.data('type') === true) {
              while (editor.node.isFirstSibling($marker.get(0)) && !$marker.parent().is(editor.$el)) {
                $marker.parent().before($marker);
              }
            }
            else {
              while (editor.node.isLastSibling($marker.get(0)) && !$marker.parent().is(editor.$el)) {
                $marker.parent().after($marker);
              }
            }
          }
        }

        // Check if selection can be deleted.
        var start_marker = editor.$el.find('.fr-marker[data-type="true"]').get(0).nextSibling;

        var attrs = { 'class': 'fr-unprocessed' };
        if (val) attrs.style = prop + ': ' + val + ';'
        _processNodeFormat(start_marker, 'span', attrs);

        editor.$el.find('.fr-marker + .fr-unprocessed').each(function () {
          $(this).prepend($(this).prev());
        });

        editor.$el.find('.fr-unprocessed + .fr-marker').each(function () {
          $(this).prev().append(this);
        });

        while (editor.$el.find('span.fr-unprocessed').length > 0) {
          var $span = editor.$el.find('span.fr-unprocessed:first').removeClass('fr-unprocessed');

          // Look at parent node to see if we can merge with it.
          $span.parent().get(0).normalize();
          if ($span.parent().is('span') && $span.parent().get(0).childNodes.length == 1) {
            $span.parent().css(prop, val);
            var $child = $span;
            $span = $span.parent();
            $child.replaceWith($child.html());
          }

          // Replace in reverse order to take care of the inner spans first.
          var inner_spans = $span.find('span');
          for (var i = inner_spans.length - 1; i >= 0; i--) {
            _cleanFormat(inner_spans[i], prop);
          }

          // Look at parents with the same property.
          var $outer_span = $span.parentsUntil(editor.$el, 'span[style]').filter(function() {
            return _filterSpans(this, prop);
          });

          if ($outer_span.length) {
            var c_str = '';
            var o_str = '';
            var ic_str = '';
            var io_str = '';
            var c_node = $span.get(0);

            do {
              c_node = c_node.parentNode;

              c_str = c_str + editor.node.closeTagString(c_node);
              o_str = editor.node.openTagString($(c_node).clone().addClass('fr-split').get(0)) + o_str;

              // Inner close and open.
              if ($outer_span.get(0) != c_node) {
                ic_str = ic_str + editor.node.closeTagString(c_node);
                io_str = editor.node.openTagString($(c_node).clone().addClass('fr-split').get(0)) + io_str;
              }
            } while ($outer_span.get(0) != c_node);

            // Build breaking string.
            var str = c_str + editor.node.openTagString($($outer_span.get(0)).clone().css(prop, val || '').get(0)) + io_str + $span.css(prop, '').get(0).outerHTML + ic_str + '</span>' + o_str;
            $span.replaceWith('<span id="fr-break"></span>');
            var html = $outer_span.get(0).outerHTML;

            // Replace the outer node.
            $($outer_span.get(0)).replaceWith(html.replace(/<span id="fr-break"><\/span>/g, str));
          }
        }

        while (editor.$el.find('.fr-split:empty').length > 0) {
          editor.$el.find('.fr-split:empty').remove();
        }

        editor.$el.find('.fr-split').removeClass('fr-split');

        editor.$el.find('span[style=""]').removeAttr('style');
        editor.$el.find('span[class=""]').removeAttr('class');

        editor.html.cleanEmptyTags();

        $(editor.$el.find('span').get().reverse()).each(function () {
          if (!this.attributes || this.attributes.length == 0) {
            $(this).replaceWith(this.innerHTML);
          }
        });

        editor.$el.get(0).normalize();

        // Join current spans together if they are one next to each other.
        var just_spans = editor.$el.find('span[style] + span[style]');
        for (i = 0; i < just_spans.length; i++) {
          var $x = $(just_spans[i]);
          var $p = $(just_spans[i]).prev();

          if ($x.get(0).previousSibling == $p.get(0) && editor.node.openTagString($x.get(0)) == editor.node.openTagString($p.get(0))) {
            $x.prepend($p.html());
            $p.remove();
          }
        }

        editor.$el.get(0).normalize();
        editor.selection.restore();
      }
    }

    /**
     * Remove inline style.
     */
    function removeStyle (prop) {
      applyStyle(prop, null);
    }

    /**
     * Get the current state.
     */
    function is (tag, attrs) {
      if (typeof attrs == 'undefined') attrs = {};
      if (attrs.style) {
        delete attrs.style;
      }

      var range = editor.selection.ranges(0);
      var el = range.startContainer;
      if (el.nodeType == Node.ELEMENT_NODE) {
        // Search for node deeper.
        if (el.childNodes.length > 0 && el.childNodes[range.startOffset]) {
          el = el.childNodes[range.startOffset];
        }
      }

      // Check first childs.
      var f_child = el;
      while (f_child && f_child.nodeType == Node.ELEMENT_NODE && !_matches(f_child, _query(tag, attrs))) {
        f_child = f_child.firstChild;
      }

      if (f_child && f_child.nodeType == Node.ELEMENT_NODE && _matches(f_child, _query(tag, attrs))) return true;

      // Check parents.
      var p_node = el;
      if (p_node && p_node.nodeType != Node.ELEMENT_NODE) p_node = p_node.parentNode;
      while (p_node && p_node.nodeType == Node.ELEMENT_NODE && p_node != editor.$el.get(0) && !_matches(p_node, _query(tag, attrs))) {
        p_node = p_node.parentNode;
      }

      if (p_node && p_node.nodeType == Node.ELEMENT_NODE && p_node != editor.$el.get(0) && _matches(p_node, _query(tag, attrs))) return true;

      return false;
    }

    return {
      is: is,
      toggle: toggle,
      apply: apply,
      remove: remove,
      applyStyle: applyStyle,
      removeStyle: removeStyle
    }
  }



  $.FE.COMMANDS = {
    bold: {
      title: 'Bold',
      refresh: function ($btn) {
        $btn.toggleClass('fr-active', this.format.is('strong'));
      }
    },
    italic: {
      title: 'Italic',
      refresh: function ($btn) {
        $btn.toggleClass('fr-active', this.format.is('em'));
      }
    },
    underline: {
      title: 'Underline',
      refresh: function ($btn) {
        $btn.toggleClass('fr-active', this.format.is('u'));
      }
    },
    strikeThrough: {
      title: 'Strikethrough',
      refresh: function ($btn) {
        $btn.toggleClass('fr-active', this.format.is('s'));
      }
    },
    subscript: {
      title: 'Subscript',
      refresh: function ($btn) {
        $btn.toggleClass('fr-active', this.format.is('sub'));
      }
    },
    superscript: {
      title: 'Superscript',
      refresh: function ($btn) {
        $btn.toggleClass('fr-active', this.format.is('sup'));
      }
    },
    outdent: {
      title: 'Decrease Indent'
    },
    indent: {
      title: 'Increase Indent'
    },
    undo: {
      title: 'Undo',
      undo: false,
      forcedRefresh: true,
      disabled: true
    },
    redo: {
      title: 'Redo',
      undo: false,
      forcedRefresh: true,
      disabled: true
    },
    insertHR: {
      title: 'Insert Horizontal Line'
    },
    clearFormatting: {
      title: 'Clear Formatting'
    },
    selectAll: {
      title: 'Select All',
      undo: false
    }
  };

  $.FE.RegisterCommand = function (name, info) {
    $.FE.COMMANDS[name] = info;
  }

  $.FE.MODULES.commands = function (editor) {
    var mapping = {
      bold: function () {
        _execCommand('bold', 'strong');
      },

      subscript: function () {
        _execCommand('subscript', 'sub');
      },

      superscript: function () {
        _execCommand('superscript', 'sup');
      },

      italic: function () {
        _execCommand('italic', 'em');
      },

      strikeThrough: function () {
        _execCommand('strikeThrough', 's');
      },

      underline: function () {
        _execCommand('underline', 'u');
      },

      undo: function () {
        editor.undo.run();
      },

      redo: function () {
        editor.undo.redo();
      },

      indent: function () {
        _processIndent(1);
      },

      outdent: function () {
        _processIndent(-1);
      },

      show: function () {
        if (editor.opts.toolbarInline) {
          editor.toolbar.showInline(null, true);
        }
      },

      insertHR: function () {
        editor.selection.remove();

        var empty = '';
        if (editor.core.isEmpty()) {
          empty = '<br>';
          if (editor.html.defaultTag()) {
            empty = '<' + editor.html.defaultTag() + '>' + empty + '</' + editor.html.defaultTag() + '>';
          }
        }

        editor.html.insert('<hr id="fr-just">' + empty);

        var $hr = editor.$el.find('hr#fr-just');
        $hr.removeAttr('id');

        editor.selection.setAfter($hr.get(0)) || editor.selection.setBefore($hr.get(0));

        editor.selection.restore();
      },

      clearFormatting: function () {
        editor.format.remove();
      },

      selectAll: function () {
        editor.doc.execCommand('selectAll', false, false);
      }
    }

    /**
     * Exec command.
     */
    function exec (cmd, params) {
      // Trigger before command to see if to execute the default callback.
      if (editor.events.trigger('commands.before', $.merge([cmd], params || [])) !== false) {
        // Get the callback.
        var callback = ($.FE.COMMANDS[cmd] && $.FE.COMMANDS[cmd].callback) || mapping[cmd];

        var focus = true;
        if ($.FE.COMMANDS[cmd] && typeof $.FE.COMMANDS[cmd].focus != 'undefined') {
          focus = $.FE.COMMANDS[cmd].focus;
        }

        // Make sure we have focus.
        if (!editor.core.hasFocus() && focus && !editor.popups.areVisible()) {
          // Focus in the editor at any position.
          editor.events.focus(true);
        }

        // Callback.
        // Save undo step.
        if ($.FE.COMMANDS[cmd] && $.FE.COMMANDS[cmd].undo !== false) {
          editor.undo.saveStep();
        }

        if (callback) {
          callback.apply(editor, $.merge([cmd], params || []));
        }

        // Trigger after command.
        editor.events.trigger('commands.after', $.merge([cmd], params || []));

        // Save undo step again.
        if ($.FE.COMMANDS[cmd] && $.FE.COMMANDS[cmd].undo !== false) editor.undo.saveStep();
      }
    }

    /**
     * Exex default.
     */
    function _execCommand(cmd, tag) {
      editor.format.toggle(tag);
    }

    function _processIndent(indent) {
      editor.selection.save();
      editor.html.wrap(true, true, true, true);
      editor.selection.restore();

      var blocks = editor.selection.blocks();

      for (var i = 0; i < blocks.length; i++) {
        if (blocks[i].tagName != 'LI' && blocks[i].parentNode.tagName != 'LI') {
          var $block = $(blocks[i]);

          var prop = (editor.opts.direction == 'rtl' || $block.css('direction') == 'rtl') ? 'margin-right' : 'margin-left';

          var margin_left = editor.helpers.getPX($block.css(prop));

          $block.css(prop, Math.max(margin_left + indent * 20, 0) || '');
          $block.removeClass('fr-temp-div');
        }
      }

      editor.selection.save();
      editor.html.unwrap();
      editor.selection.restore();
    }

    function callExec (k) {
      return function () {
        exec(k);
      }
    }

    var resp = {};
    for (var k in mapping) {
      if (mapping.hasOwnProperty(k)) {
        resp[k] = callExec(k);
      }
    }

    function _init () {
      // Prevent typing in HR.
      editor.events.on('keydown', function (e) {
        var el = editor.selection.element();
        if (el && el.tagName == 'HR') {
          e.preventDefault();
          return false;
        }
      });

      // Do not allow mousedown on HR.
      editor.events.on('mousedown', function (e) {
        if (e.target && e.target.tagName == 'HR') {
          e.preventDefault();
          e.stopPropagation();
          return false;
        }
      });

      // If somehow focus gets in HR remove it.
      editor.events.on('mouseup', function (e) {
        var s_el = editor.selection.element();
        var e_el = editor.selection.endElement();

        if (s_el == e_el && s_el && s_el.tagName == 'HR') {
          if (s_el.nextSibling) {
            if (!editor.node.isBlock(s_el.nextSibling)) {
              $(s_el).after($.FE.MARKERS);
            }
            else {
              editor.selection.setAtStart(s_el.nextSibling);
            }
          }

          editor.selection.restore();
        }
      })
    }

    return $.extend(resp, {
      exec: exec,
      _init: _init
    });
  };


  $.FE.MODULES.cursorLists = function (editor) {
    /**
     * Find the first li parent.
     */
    function _firstParentLI (node) {
      var p_node = node;
      while (p_node.tagName != 'LI') {
        p_node = p_node.parentNode;
      }

      return p_node;
    }

    /**
     * Find the first list parent.
     */
    function _firstParentList (node) {
      var p_node = node;
      while (!editor.node.isList(p_node)) {
        p_node = p_node.parentNode;
      }

      return p_node;
    }


    /**
     * Do enter at the beginning of a list item.
     */
    function _startEnter (marker) {
      var li = _firstParentLI(marker);

      // Get previous and next siblings.
      var next_li = li.nextSibling;
      var prev_li = li.previousSibling;
      var default_tag = editor.html.defaultTag();

      var ul;

      // We are in a list item at the middle of the list or an list item that is not empty.
      if (editor.node.isEmpty(li, true) && next_li) {
        var o_str = '';
        var c_str = ''
        var p_node = marker.parentNode;

        // Create open / close string.
        while (!editor.node.isList(p_node) && p_node.parentNode && p_node.parentNode.tagName !== 'LI') {
          o_str = editor.node.openTagString(p_node) + o_str;
          c_str = c_str + editor.node.closeTagString(p_node);
          p_node = p_node.parentNode;
        }

        o_str = editor.node.openTagString(p_node) + o_str;
        c_str = c_str + editor.node.closeTagString(p_node);

        var str = ''
        if (p_node.parentNode && p_node.parentNode.tagName == 'LI') {
          str = c_str + '<li>' + $.FE.MARKERS + '<br>' + o_str;
        }
        else {
          if (default_tag) {
            str = c_str + '<' + default_tag + '>' + $.FE.MARKERS + '<br>' + '</' + default_tag + '>' + o_str;
          }
          else {
            str = c_str + $.FE.MARKERS + '<br>' + o_str;
          }
        }

        $(li).html('<span id="fr-break"></span>');

        while (['UL', 'OL'].indexOf(p_node.tagName) < 0 || (p_node.parentNode && p_node.parentNode.tagName === 'LI')) {
          p_node = p_node.parentNode;
        }
        var html = editor.node.openTagString(p_node) + $(p_node).html() + editor.node.closeTagString(p_node);
        html = html.replace(/<span id="fr-break"><\/span>/g, str);

        $(p_node).replaceWith(html);

        editor.$el.find('li:empty').remove();
      }
      else if ((prev_li && next_li) || !editor.node.isEmpty(li, true)) {
        $(li).before('<li><br></li>');
        $(marker).remove();
      }

      // There is no previous list item so transform the current list item to an empty line.
      else if (!prev_li) {
        ul = _firstParentList(li);

        // We are in a nested list so add a new li before it.
        if (ul.parentNode && ul.parentNode.tagName == 'LI') {
          $(ul.parentNode).before('<li>' + $.FE.MARKERS + '<br></li>');
        }

        // We are in a normal list. Add a new line before.
        else {
          if (default_tag) {
            $(ul).before('<' + default_tag + '>' + $.FE.MARKERS + '<br></' + default_tag + '>');
          }
          else {
            $(ul).before($.FE.MARKERS  + '<br>');
          }
        }

        // Remove the current li.
        $(li).remove();
      }

      // There is no next_li item so transform the current list item to an empty line.
      else {
        ul = _firstParentList(li);

        // We are in a nested lists so add a new li after it.
        if (ul.parentNode && ul.parentNode.tagName == 'LI') {
          $(ul.parentNode).after('<li>' + $.FE.MARKERS + '<br></li>');
        }

        // We are in a normal list. Add a new line after.
        else {
          if (default_tag) {
            $(ul).after('<' + default_tag + '>' + $.FE.MARKERS + '<br></' + default_tag + '>');
          }
          else {
            $(ul).after($.FE.MARKERS  + '<br>');
          }
        }

        // Remove the current li.
        $(li).remove();
      }
    }

    /**
     * Enter at the middle of a list.
     */
    function _middleEnter (marker) {
      var li = _firstParentLI(marker);

      // Build the closing / opening list item string.
      var str = '';
      var node = marker;
      var o_str = '';
      var c_str = '';
      while (node != li) {
        node = node.parentNode;

        var cls = (node.tagName == 'A' && editor.cursor.isAtEnd(marker, node)) ? 'fr-to-remove' : '';

        o_str = editor.node.openTagString($(node).clone().addClass(cls).get(0)) + o_str;
        c_str = editor.node.closeTagString(node) + c_str;
      }

      // Add markers.
      str = c_str + str + o_str + $.FE.MARKERS;

      // Build HTML.
      $(marker).replaceWith('<span id="fr-break"></span>');
      var html = editor.node.openTagString(li) + $(li).html() + editor.node.closeTagString(li);
      html = html.replace(/<span id="fr-break"><\/span>/g, str);

      // Replace the current list item.
      $(li).replaceWith(html);
    }

    /**
     * Enter at the end of a list item.
     */
    function _endEnter (marker) {
      var li = _firstParentLI(marker);

      var end_str = $.FE.MARKERS;
      var start_str = '';
      var node = marker;

      var add_invisible = false;
      while (node != li) {
        node = node.parentNode;

        var cls = (node.tagName == 'A' && editor.cursor.isAtEnd(marker, node)) ? 'fr-to-remove' : '';

        if (!add_invisible && node != li && !editor.node.isBlock(node)) {
          add_invisible = true;
          start_str = start_str + $.FE.INVISIBLE_SPACE;
        }

        start_str = editor.node.openTagString($(node).clone().addClass(cls).get(0)) + start_str;
        end_str = end_str + editor.node.closeTagString(node);
      }

      var str = start_str + end_str;

      $(marker).remove();
      $(li).after(str);
    }

    /**
     * Do backspace on a list item. This method is called only when wer are at the beginning of a LI.
     */
    function _backspace (marker) {
      var li = _firstParentLI(marker);

      // Get previous sibling.
      var prev_li = li.previousSibling;

      // There is a previous li.
      if (prev_li) {
        // Get the li inside a nested list or inner block tags.
        prev_li = $(prev_li).find(editor.html.blockTagsQuery()).get(-1) || prev_li;

        // Add markers.
        $(marker).replaceWith($.FE.MARKERS);

        // Remove possible BR at the end of the previous list.
        var contents = editor.node.contents(prev_li);
        if (contents.length && contents[contents.length - 1].tagName == 'BR') {
          $(contents[contents.length - 1]).remove();
        }

        // Remove any nodes that might be wrapped.
        $(li).find(editor.html.blockTagsQuery()).not('ol, ul, table').each (function () {
          if (this.parentNode == li) {
            $(this).replaceWith($(this).html() + (editor.node.isEmpty(this) ? '' : '<br>'));
          }
        })

        // Append the current list item content to the previous one.
        var node = editor.node.contents(li)[0];
        var tmp;
        while (node && !editor.node.isList(node)) {
          tmp = node.nextSibling;
          $(prev_li).append(node);
          node = tmp;
        }

        prev_li = li.previousSibling;
        while (node) {
          tmp = node.nextSibling;
          $(prev_li).append(node);
          node = tmp;
        }

        // Remove the current LI.
        $(li).remove();
      }

      // No previous li.
      else {
        var ul = _firstParentList(li);

        // Add markers.
        $(marker).replaceWith($.FE.MARKERS);

        // Nested lists.
        if (ul.parentNode && ul.parentNode.tagName == 'LI') {
          var prev_node = ul.previousSibling;

          // Previous node is block.
          if (editor.node.isBlock(prev_node)) {
            // Remove any nodes that might be wrapped.
            $(li).find(editor.html.blockTagsQuery()).not('ol, ul, table').each (function () {
              if (this.parentNode == li) {
                $(this).replaceWith($(this).html() + (editor.node.isEmpty(this) ? '' : '<br>'));
              }
            });

            $(prev_node).append($(li).html());
          }

          // Text right in li.
          else {
            $(ul).before($(li).html());
          }
        }

        // Normal lists. Add an empty li instead.
        else {
          var default_tag = editor.html.defaultTag();
          if (default_tag && $(li).find(editor.html.blockTagsQuery()).length === 0) {
            $(ul).before('<' + default_tag + '>' + $(li).html() + '</' + default_tag + '>');
          }
          else {
            $(ul).before($(li).html());
          }
        }

        // Remove the current li.
        $(li).remove();

        // Remove the ul if it is empty.
        if ($(ul).find('li').length === 0) $(ul).remove();
      }
    }

    /**
     * Delete at the end of list item.
     */
    function _del (marker) {
      var li = _firstParentLI(marker);
      var next_li = li.nextSibling;
      var contents;

      // There is a next li.
      if (next_li) {
        // Remove possible BR at the beginning of the next LI.
        contents = editor.node.contents(next_li);
        if (contents.length && contents[0].tagName == 'BR') {
          $(contents[0]).remove();
        }

        // Unwrap content from the next node.
        $(next_li).find(editor.html.blockTagsQuery()).not('ol, ul, table').each (function () {
          if (this.parentNode == next_li) {
            $(this).replaceWith($(this).html() + (editor.node.isEmpty(this) ? '' : '<br>'));
          }
        });

        // Append the next LI to the current LI.
        var last_node = marker;
        var node = editor.node.contents(next_li)[0];
        var tmp;
        while (node && !editor.node.isList(node)) {
          tmp = node.nextSibling;
          $(last_node).after(node);
          last_node = node;
          node = tmp;
        }

        // Append nested lists.
        while (node) {
          tmp = node.nextSibling;
          $(li).append(node);
          node = tmp;
        }

        // Replace marker with markers.
        $(marker).replaceWith($.FE.MARKERS);

        // Remove next li.
        $(next_li).remove();
      }

      // No next li.
      else {
        // Search the next sibling in parents.
        var next_node = li;
        while (!next_node.nextSibling && next_node != editor.$el.get(0)) {
          next_node = next_node.parentNode;
        }

        // We're right at the end.
        if (next_node == editor.$el.get(0)) return false;

        // Get the next sibling.
        next_node = next_node.nextSibling;

        // Next sibling is a block tag.
        if (editor.node.isBlock(next_node)) {
          // Check if we can do delete in it.
          if ($.FE.NO_DELETE_TAGS.indexOf(next_node.tagName) < 0) {

            // Add markers.
            $(marker).replaceWith($.FE.MARKERS);

            // Remove any possible BR at the end of the LI.
            contents = editor.node.contents(li);
            if (contents.length && contents[contents.length - 1].tagName == 'BR') {
              $(contents[contents.length - 1]).remove();
            }

            // Append next node.
            $(li).append($(next_node).html());

            // Remove the next node.
            $(next_node).remove();
          }
        }

        // Append everything till the next block tag or BR.
        else {
          // Remove any possible BR at the end of the LI.
          contents = editor.node.contents(li);
          if (contents.length && contents[contents.length - 1].tagName == 'BR') {
            $(contents[contents.length - 1]).remove();
          }

          // var next_node = next_li;
          $(marker).replaceWith($.FE.MARKERS);
          while (next_node && !editor.node.isBlock(next_node) && next_node.tagName != 'BR') {
            $(li).append($(next_node));
            next_node = next_node.nextSibling;
          }
        }
      }
    }

    return {
      _startEnter: _startEnter,
      _middleEnter: _middleEnter,
      _endEnter: _endEnter,
      _backspace: _backspace,
      _del: _del
    }
  };


  // Do not merge with the previous one.
  $.FE.NO_DELETE_TAGS = ['TH', 'TD', 'TABLE', 'FORM'];

  // Do simple enter.
  $.FE.SIMPLE_ENTER_TAGS = ['TH', 'TD', 'LI', 'DL', 'DT', 'FORM'];

  $.FE.MODULES.cursor = function (editor) {
    /**
     * Check if node is at the end of a block tag.
     */
    function _atEnd (node) {
      if (!node) return false;
      if (editor.node.isBlock(node)) return true;
      if (node.nextSibling) return false;

      return _atEnd(node.parentNode);
    }

    /**
     * Check if node is at the start of a block tag.
     */
    function _atStart (node) {
      if (!node) return false;
      if (editor.node.isBlock(node)) return true;
      if (node.previousSibling) return false;

      return _atStart(node.parentNode);
    }

    /**
     * Check if node is a the start of the container.
     */
    function _isAtStart (node, container) {
      if (!node) return false;
      if (node == editor.$wp.get(0)) return false;
      if (node.previousSibling) return false;
      if (node.parentNode == container) return true;

      return _isAtStart(node.parentNode, container);
    }

    /**
     * Check if node is a the start of the container.
     */
    function _isAtEnd (node, container) {
      if (!node) return false;
      if (node == editor.$wp.get(0)) return false;
      if (node.nextSibling) return false;
      if (node.parentNode == container) return true;

      return _isAtEnd(node.parentNode, container);
    }

    /**
     * Check if the node is inside a LI.
     */
    function _inLi (node) {
      return $(node).parentsUntil(editor.$el, 'LI').length > 0 && $(node).parentsUntil('LI', 'TABLE').length === 0;
    }

    /**
     * Do backspace at the start of a block tag.
     */
    function _startBackspace (marker) {
      var quote = $(marker).parentsUntil(editor.$el, 'BLOCKQUOTE').length > 0;
      var deep_parent = editor.node.deepestParent(marker, [], !quote);

      if (deep_parent && deep_parent.tagName == 'BLOCKQUOTE') {
        var m_parent = editor.node.deepestParent(marker, [$(marker).parentsUntil(editor.$el, 'BLOCKQUOTE').get(0)]);
        if (m_parent && m_parent.previousSibling) {
          deep_parent = m_parent;
        }
      }

      // Deepest parent is not the main element.
      if (deep_parent !== null) {
        var prev_node = deep_parent.previousSibling;
        var contents;

        // We are inside a block tag.
        if (editor.node.isBlock(deep_parent) && editor.node.isEditable(deep_parent)) {
          // There is a previous node.
          if (prev_node && $.FE.NO_DELETE_TAGS.indexOf(prev_node.tagName) < 0) {
            if (editor.node.isDeletable(prev_node)) {
              $(prev_node).remove();
              $(marker).replaceWith($.FE.MARKERS);
            }
            else {
              // Previous node is a block tag.
              if (editor.node.isEditable(prev_node)) {
                if (editor.node.isBlock(prev_node)) {
                  if (editor.node.isEmpty(prev_node) && !editor.node.isList(prev_node)) {
                    $(prev_node).remove();
                  }
                  else {
                    if (editor.node.isList(prev_node)) {
                      prev_node = $(prev_node).find('li:last').get(0);
                    }

                    // Remove last BR.
                    contents = editor.node.contents(prev_node);
                    if (contents.length && contents[contents.length - 1].tagName == 'BR') {
                      $(contents[contents.length - 1]).remove();
                    }

                    // Prev node is blockquote but the current one isn't.
                    if (prev_node.tagName == 'BLOCKQUOTE' && deep_parent.tagName != 'BLOCKQUOTE') {
                      contents = editor.node.contents(prev_node);
                      while (contents.length && editor.node.isBlock(contents[contents.length - 1])) {
                        prev_node = contents[contents.length - 1];
                        contents = editor.node.contents(prev_node);
                      }
                    }
                    // Prev node is not blockquote, but the current one is.
                    else if (prev_node.tagName != 'BLOCKQUOTE' && deep_parent.tagName == 'BLOCKQUOTE') {
                      contents = editor.node.contents(deep_parent);
                      while (contents.length && editor.node.isBlock(contents[0])) {
                        deep_parent = contents[0];
                        contents = editor.node.contents(deep_parent);
                      }
                    }

                    $(marker).replaceWith($.FE.MARKERS);
                    $(prev_node).append(editor.node.isEmpty(deep_parent) ? $.FE.MARKERS : deep_parent.innerHTML);
                    $(deep_parent).remove();
                  }
                }
                else {
                  $(marker).replaceWith($.FE.MARKERS);

                  if (deep_parent.tagName == 'BLOCKQUOTE' && prev_node.nodeType == Node.ELEMENT_NODE) {
                    $(prev_node).remove();
                  }
                  else {
                    $(prev_node).after(editor.node.isEmpty(deep_parent) ? '' : $(deep_parent).html());
                    $(deep_parent).remove();
                    if (prev_node.tagName == 'BR') $(prev_node).remove();
                  }
                }
              }
            }
          }
        }

        // No block tag.
        /* jshint ignore:start */
        /* jscs:disable */
        else {
          // This should never happen.
        }
        /* jshint ignore:end */
        /* jscs:enable */
      }
    }

    /**
     * Do backspace at the middle of a block tag.
     */
    function _middleBackspace (marker) {
      var prev_node = marker;

      // Get the parent node that has a prev sibling.
      while (!prev_node.previousSibling) {
        prev_node = prev_node.parentNode;

        if (editor.node.isElement(prev_node)) return false;
      }
      prev_node = prev_node.previousSibling;

      // Not block tag.
      var contents;
      if (!editor.node.isBlock(prev_node) && editor.node.isEditable(prev_node)) {
        contents = editor.node.contents(prev_node);

        // Previous node is text.
        while (prev_node.nodeType != Node.TEXT_NODE && !editor.node.isDeletable(prev_node) && contents.length && editor.node.isEditable(prev_node)) {
          prev_node = contents[contents.length - 1];
          contents = editor.node.contents(prev_node);
        }

        if (prev_node.nodeType == Node.TEXT_NODE) {
          if (editor.helpers.isIOS()) return true;

          var txt = prev_node.textContent;
          var len = txt.length - 1;

          // Tab UNDO.
          if (editor.opts.tabSpaces && txt.length >= editor.opts.tabSpaces) {
            var tab_str = txt.substr(txt.length - editor.opts.tabSpaces, txt.length - 1);
            if (tab_str.replace(/ /g, '').replace(new RegExp($.FE.UNICODE_NBSP, 'g'), '').length == 0) {
              len = txt.length - editor.opts.tabSpaces;
            }
          }

          prev_node.textContent = txt.substring(0, len);
          if (prev_node.textContent.length && prev_node.textContent.charCodeAt(prev_node.textContent.length - 1) == 55357) {
            prev_node.textContent = prev_node.textContent.substr(0, prev_node.textContent.length - 1);
          }

          // Remove node if empty.
          if (prev_node.textContent.length == 0) {
            if (prev_node.parentNode.childNodes.length == 2 && prev_node.parentNode == marker.parentNode && !editor.node.isBlock(prev_node.parentNode) && !editor.node.isElement(prev_node.parentNode)) {
              $(prev_node.parentNode).after($.FE.MARKERS);
              $(prev_node.parentNode).remove();
            }
            else {
              $(prev_node).after($.FE.MARKERS);

              // https://github.com/froala/wysiwyg-editor/issues/1379.
              if (editor.node.isElement(prev_node.parentNode) && !marker.nextSibling && prev_node.previousSibling && prev_node.previousSibling.tagName == 'BR') {
                $(marker).after('<br>');
              }

              prev_node.parentNode.removeChild(prev_node);
            }
          }
          else {
            $(prev_node).after($.FE.MARKERS);
          }
        }
        else if (editor.node.isDeletable(prev_node)) {
          $(prev_node).after($.FE.MARKERS);
          $(prev_node).remove();
        }
        else {
          if (editor.events.trigger('node.remove', [$(prev_node)]) !== false) {
            $(prev_node).after($.FE.MARKERS);
            $(prev_node).remove();
          }
        }
      }

      // Block tag but we are allowed to delete it.
      else if ($.FE.NO_DELETE_TAGS.indexOf(prev_node.tagName) < 0 && editor.node.isEditable(prev_node)) {
        if (editor.node.isEmpty(prev_node) && !editor.node.isList(prev_node)) {
          $(prev_node).remove();
          $(marker).replaceWith($.FE.MARKERS);
        }
        else {
          // List correction.
          if (editor.node.isList(prev_node)) prev_node = $(prev_node).find('li:last').get(0);

          contents = editor.node.contents(prev_node);
          if (contents && contents[contents.length - 1].tagName == 'BR') {
            $(contents[contents.length - 1]).remove();
          }

          contents = editor.node.contents(prev_node);
          while (contents && editor.node.isBlock(contents[contents.length - 1])) {
            prev_node = contents[contents.length - 1];
            contents = editor.node.contents(prev_node);
          }

          $(prev_node).append($.FE.MARKERS);

          var next_node = marker;
          while (!next_node.previousSibling) {
            next_node = next_node.parentNode;
          }

          while (next_node && next_node.tagName !== 'BR' && !editor.node.isBlock(next_node)) {
            var copy_node = next_node;
            next_node = next_node.nextSibling;
            $(prev_node).append(copy_node);
          }

          // Remove BR.
          if (next_node && next_node.tagName == 'BR') $(next_node).remove();

          $(marker).remove();
        }
      }

      else {
        if (marker.nextSibling && marker.nextSibling.tagName == 'BR') {
          $(marker.nextSibling).remove();
        }
      }
    }

    /**
     * Do backspace.
     */
    function backspace () {
      var do_default = false;

      // Add a marker in HTML.
      var marker = editor.markers.insert();

      if (!marker) return true;

      editor.$el.get(0).normalize();

      // We should remove invisible space first of all.
      var prev_node = marker.previousSibling;
      if (prev_node) {
        var txt = prev_node.textContent;
        if (txt && txt.length && txt.charCodeAt(txt.length - 1) == 8203) {
          if (txt.length == 1) {
            $(prev_node).remove()
          }
          else {
            prev_node.textContent = prev_node.textContent.substr(0, txt.length - 1);
            if (prev_node.textContent.length && prev_node.textContent.charCodeAt(prev_node.textContent.length - 1) == 55357) {
              prev_node.textContent = prev_node.textContent.substr(0, prev_node.textContent.length - 1);
            }
          }
        }
      }

      // Delete at end.
      if (_atEnd(marker)) {
        do_default = _middleBackspace(marker);
      }

      // Delete at start.
      else if (_atStart(marker)) {
        if (_inLi(marker) && _isAtStart(marker, $(marker).parents('li:first').get(0))) {
          editor.cursorLists._backspace(marker);
        }
        else {
          _startBackspace(marker);
        }
      }

      // Delete at middle.
      else {
        do_default = _middleBackspace(marker);
      }

      $(marker).remove();

      editor.$el.find('blockquote:empty').remove();
      editor.html.fillEmptyBlocks();
      editor.html.cleanEmptyTags();
      editor.clean.quotes();
      editor.clean.lists();
      editor.spaces.normalize();
      editor.selection.restore();

      return do_default;
    }

    /**
     * Delete at the end of a block tag.
     */
    function _endDel (marker) {
      var quote = $(marker).parentsUntil(editor.$el, 'BLOCKQUOTE').length > 0;
      var deep_parent = editor.node.deepestParent(marker, [], !quote);

      if (deep_parent && deep_parent.tagName == 'BLOCKQUOTE') {
        var m_parent = editor.node.deepestParent(marker, [$(marker).parentsUntil(editor.$el, 'BLOCKQUOTE').get(0)]);
        if (m_parent && m_parent.nextSibling) {
          deep_parent = m_parent;
        }
      }

      // Deepest parent is not the main element.
      if (deep_parent !== null) {
        var next_node = deep_parent.nextSibling;
        var contents;

        // We are inside a block tag.
        if (editor.node.isBlock(deep_parent) && (editor.node.isEditable(deep_parent) || editor.node.isDeletable(deep_parent))) {
          // There is a next node.
          if (next_node && $.FE.NO_DELETE_TAGS.indexOf(next_node.tagName) < 0) {
            if (editor.node.isDeletable(next_node)) {
              $(next_node).remove();
              $(marker).replaceWith($.FE.MARKERS);
            }
            else {
              // Next node is a block tag.
              if (editor.node.isBlock(next_node) && editor.node.isEditable(next_node)) {
                // Next node is a list.
                if (editor.node.isList(next_node)) {
                  // Current block tag is empty.
                  if (editor.node.isEmpty(deep_parent, true)) {
                    $(deep_parent).remove();

                    $(next_node).find('li:first').prepend($.FE.MARKERS);
                  }
                  else {
                    var $li = $(next_node).find('li:first');

                    if (deep_parent.tagName == 'BLOCKQUOTE') {
                      contents = editor.node.contents(deep_parent);
                      if (contents.length && editor.node.isBlock(contents[contents.length - 1])) {
                        deep_parent = contents[contents.length - 1];
                      }
                    }

                    // There are no nested lists.
                    if ($li.find('ul, ol').length === 0) {
                      $(marker).replaceWith($.FE.MARKERS);

                      // Remove any nodes that might be wrapped.
                      $li.find(editor.html.blockTagsQuery()).not('ol, ul, table').each (function () {
                        if (this.parentNode == $li.get(0)) {
                          $(this).replaceWith($(this).html() + (editor.node.isEmpty(this) ? '' : '<br>'));
                        }
                      });

                      $(deep_parent).append(editor.node.contents($li.get(0)));
                      $li.remove();

                      if ($(next_node).find('li').length === 0) $(next_node).remove();
                    }
                  }
                }
                else {
                  // Remove last BR.
                  contents = editor.node.contents(next_node);
                  if (contents.length && contents[0].tagName == 'BR') {
                    $(contents[0]).remove();
                  }

                  if (next_node.tagName != 'BLOCKQUOTE' && deep_parent.tagName == 'BLOCKQUOTE') {
                    contents = editor.node.contents(deep_parent);
                    while (contents.length && editor.node.isBlock(contents[contents.length - 1])) {
                      deep_parent = contents[contents.length - 1];
                      contents = editor.node.contents(deep_parent);
                    }
                  }
                  else if (next_node.tagName == 'BLOCKQUOTE' && deep_parent.tagName != 'BLOCKQUOTE') {
                    contents = editor.node.contents(next_node);
                    while (contents.length && editor.node.isBlock(contents[0])) {
                      next_node = contents[0];
                      contents = editor.node.contents(next_node);
                    }
                  }

                  $(marker).replaceWith($.FE.MARKERS);
                  $(deep_parent).append(next_node.innerHTML);
                  $(next_node).remove();
                }
              }
              else {
                $(marker).replaceWith($.FE.MARKERS);

                // var next_node = next_node.nextSibling;
                while (next_node && next_node.tagName !== 'BR' && !editor.node.isBlock(next_node) && editor.node.isEditable(next_node)) {
                  var copy_node = next_node;
                  next_node = next_node.nextSibling;
                  $(deep_parent).append(copy_node);
                }

                if (next_node && next_node.tagName == 'BR' && editor.node.isEditable(next_node)) {
                  $(next_node).remove();
                }
              }
            }
          }
        }

        // No block tag.
        /* jshint ignore:start */
        /* jscs:disable */
        else {
          // This should never happen.
        }
        /* jshint ignore:end */
        /* jscs:enable */
      }
    }

    /**
     * Delete at the middle of a block tag.
     */
    function _middleDel (marker) {
      var next_node = marker;

      // Get the parent node that has a next sibling.
      while (!next_node.nextSibling) {
        next_node = next_node.parentNode;

        if (editor.node.isElement(next_node)) return false;
      }
      next_node = next_node.nextSibling;

      // Handle the case when the next node is a BR.
      if (next_node.tagName == 'BR' && editor.node.isEditable(next_node)) {
        // There is a next sibling.
        if (next_node.nextSibling) {
          if (editor.node.isBlock(next_node.nextSibling) && editor.node.isEditable(next_node.nextSibling)) {
            if ($.FE.NO_DELETE_TAGS.indexOf(next_node.nextSibling.tagName) < 0) {
              next_node = next_node.nextSibling;
              $(next_node.previousSibling).remove();
            }
            else {
              $(next_node).remove();
              return;
            }
          }
        }

        // No next sibling. We should check if BR is at the end.
        else if (_atEnd(next_node)) {
          if (_inLi(marker)) {
            editor.cursorLists._del(marker);
          }
          else {
            var deep_parent = editor.node.deepestParent(next_node);
            if (deep_parent) {
              $(next_node).remove();
              _endDel(marker);
            }
          }

          return;
        }
      }

      // Not block tag.
      var contents;
      if (!editor.node.isBlock(next_node) && editor.node.isEditable(next_node)) {
        contents = editor.node.contents(next_node);

        // Next node is text.
        while (next_node.nodeType != Node.TEXT_NODE && contents.length && !editor.node.isDeletable(next_node) && editor.node.isEditable(next_node)) {
          next_node = contents[0];
          contents = editor.node.contents(next_node);
        }

        if (next_node.nodeType == Node.TEXT_NODE) {
          $(next_node).before($.FE.MARKERS);

          if (next_node.textContent.length && next_node.textContent.charCodeAt(0) == 55357) {
            next_node.textContent = next_node.textContent.substring(2, next_node.textContent.length);
          }
          else {
            next_node.textContent = next_node.textContent.substring(1, next_node.textContent.length);
          }
        }
        else if (editor.node.isDeletable(next_node)) {
          $(next_node).before($.FE.MARKERS);
          $(next_node).remove();
        }
        else {
          if (editor.events.trigger('node.remove', [$(next_node)]) !== false) {
            $(next_node).before($.FE.MARKERS);
            $(next_node).remove();
          }
        }

        $(marker).remove();
      }

      // Block tag.
      else if ($.FE.NO_DELETE_TAGS.indexOf(next_node.tagName) < 0 && (editor.node.isEditable(next_node) || editor.node.isDeletable(next_node))) {
        if (editor.node.isDeletable(next_node)) {
          $(marker).replaceWith($.FE.MARKERS);
          $(next_node).remove();
        }
        else {
          if (editor.node.isList(next_node)) {
            // There is a previous sibling.
            if (marker.previousSibling) {
              $(next_node).find('li:first').prepend(marker);
              editor.cursorLists._backspace(marker);
            }

            // No previous sibling.
            else {
              $(next_node).find('li:first').prepend($.FE.MARKERS);
              $(marker).remove();
            }
          }
          else {
            contents = editor.node.contents(next_node);
            if (contents && contents[0].tagName == 'BR') {
              $(contents[0]).remove();
            }

            // Deal with blockquote.
            if (contents && next_node.tagName == 'BLOCKQUOTE') {
              var node = contents[0];
              $(marker).before($.FE.MARKERS);
              while (node && node.tagName != 'BR') {
                var tmp = node;
                node = node.nextSibling;
                $(marker).before(tmp);
              }

              if (node && node.tagName == 'BR') {
                $(node).remove();
              }
            }
            else {
              $(marker)
                .after($(next_node).html())
                .after($.FE.MARKERS);

              $(next_node).remove();
            }
          }
        }
      }
    }

    /**
     * Delete.
     */
    function del () {
      var marker = editor.markers.insert();

      if (!marker) return false;

      editor.$el.get(0).normalize();

      // Delete at end.
      if (_atEnd(marker)) {
        if (_inLi(marker)) {
          if ($(marker).parents('li:first').find('ul, ol').length === 0) {
            editor.cursorLists._del(marker);
          }
          else {
            var $li = $(marker).parents('li:first').find('ul:first, ol:first').find('li:first');
            $li = $li.find(editor.html.blockTagsQuery()).get(-1) || $li;

            $li.prepend(marker);
            editor.cursorLists._backspace(marker);
          }
        }
        else {
          _endDel(marker);
        }
      }

      // Delete at start.
      else if (_atStart(marker)) {
        _middleDel(marker);
      }

      // Delete at middle.
      else {
        _middleDel(marker);
      }

      $(marker).remove();
      editor.$el.find('blockquote:empty').remove();
      editor.html.fillEmptyBlocks();
      editor.html.cleanEmptyTags();
      editor.clean.quotes();
      editor.clean.lists();
      editor.spaces.normalize();
      editor.selection.restore();
    }

    function _cleanNodesToRemove() {
      editor.$el.find('.fr-to-remove').each (function () {
        var contents = editor.node.contents(this);
        for (var i = 0; i < contents.length; i++) {
          if (contents[i].nodeType == Node.TEXT_NODE) {
            contents[i].textContent = contents[i].textContent.replace(/\u200B/g, '');
          }
        }

        $(this).replaceWith(this.innerHTML);
      })
    }

    /**
     * Enter at the end of a block tag.
     */
    function _endEnter (marker, shift, quote) {
      var deep_parent = editor.node.deepestParent(marker, [], !quote);
      var default_tag;

      if (deep_parent && deep_parent.tagName == 'BLOCKQUOTE') {
        if (_isAtEnd(marker, deep_parent)) {
          default_tag = editor.html.defaultTag();
          if (default_tag) {
            $(deep_parent).after('<' + default_tag + '>' + $.FE.MARKERS + '<br>' + '</' + default_tag + '>');
          }
          else {
            $(deep_parent).after($.FE.MARKERS + '<br>');
          }

          $(marker).remove();
          return false;
        }
        else {
          _middleEnter(marker, shift, quote);
          return false;
        }
      }

      // We are right in the main element.
      if (deep_parent == null) {
        default_tag = editor.html.defaultTag();
        if (!default_tag || !editor.node.isElement(marker.parentNode)) {
          $(marker).replaceWith('<br/>' + $.FE.MARKERS + '<br/>');
        }
        else {
          $(marker).replaceWith('<' + default_tag + '>' + $.FE.MARKERS + '<br>' + '</' + default_tag + '>');
        }
      }

      // There is a parent.
      else {
        // Block tag parent.
        var c_node = marker;
        var str = '';
        if (!editor.node.isBlock(deep_parent) || shift) {
          str = '<br/>';
        }

        var c_str = '';
        var o_str = '';

        default_tag = editor.html.defaultTag();
        var open_default_tag = '';
        var close_default_tag = '';
        if (default_tag && editor.node.isBlock(deep_parent)) {
          open_default_tag = '<' + default_tag + '>';
          close_default_tag = '</' + default_tag + '>';

          if (deep_parent.tagName == default_tag.toUpperCase()) {
            open_default_tag = editor.node.openTagString($(deep_parent).clone().removeAttr('id').get(0));
          }
        }

        do {
          c_node = c_node.parentNode;

          // Shift condition.
          if (!shift || c_node != deep_parent || (shift && !editor.node.isBlock(deep_parent))) {
            c_str = c_str + editor.node.closeTagString(c_node);

            // Open str when there is a block parent.
            if (c_node == deep_parent && editor.node.isBlock(deep_parent)) {
              o_str = open_default_tag + o_str;
            }
            else {
              var cls = (c_node.tagName == 'A' && _isAtEnd(marker, c_node)) ? 'fr-to-remove' : '';
              o_str = editor.node.openTagString($(c_node).clone().addClass(cls).get(0)) + o_str;
            }
          }
        } while (c_node != deep_parent);

        // Add BR if deep parent is block tag.
        str = c_str + str + o_str + ((marker.parentNode == deep_parent && editor.node.isBlock(deep_parent)) ? '' : $.FE.INVISIBLE_SPACE) + $.FE.MARKERS;

        if (editor.node.isBlock(deep_parent) && !$(deep_parent).find('*:last').is('br')) {
          $(deep_parent).append('<br/>');
        }

        $(marker).after('<span id="fr-break"></span>');
        $(marker).remove();

        // Add a BR after to make sure we display the last line.
        if ((!deep_parent.nextSibling || editor.node.isBlock(deep_parent.nextSibling)) && !editor.node.isBlock(deep_parent)) {
          $(deep_parent).after('<br>');
        }

        var html;
        // No shift.
        if (!shift && editor.node.isBlock(deep_parent)) {
          html = editor.node.openTagString(deep_parent) + $(deep_parent).html() + close_default_tag;
        }
        else {
          html = editor.node.openTagString(deep_parent) + $(deep_parent).html() + editor.node.closeTagString(deep_parent);
        }

        html = html.replace(/<span id="fr-break"><\/span>/g, str);

        $(deep_parent).replaceWith(html);
      }
    }

    /**
     * Start at the beginning of a block tag.
     */
    function _startEnter (marker, shift, quote) {
      var deep_parent = editor.node.deepestParent(marker, [], !quote);
      var default_tag;

      if (deep_parent && deep_parent.tagName == 'BLOCKQUOTE') {
        if (_isAtStart(marker, deep_parent)) {
          default_tag = editor.html.defaultTag();
          if (default_tag) {
            $(deep_parent).before('<' + default_tag + '>' + $.FE.MARKERS + '<br>' + '</' + default_tag + '>');
          }
          else {
            $(deep_parent).before($.FE.MARKERS + '<br>');
          }

          $(marker).remove();
          return false;
        }
        else if (_isAtEnd(marker, deep_parent)) {
          _endEnter(marker, shift, true);
        }
        else {
          _middleEnter(marker, shift, true);
        }
      }

      // We are right in the main element.
      if (deep_parent == null) {
        default_tag = editor.html.defaultTag();

        if (!default_tag || !editor.node.isElement(marker.parentNode)) {
          $(marker).replaceWith('<br>' + $.FE.MARKERS);
        }
        else {
          $(marker).replaceWith('<' + default_tag + '>' + $.FE.MARKERS + '<br>' + '</' + default_tag + '>');
        }
      }
      else {
        if (editor.node.isBlock(deep_parent)) {
          if (shift) {
            $(marker).remove();
            $(deep_parent).prepend('<br>' + $.FE.MARKERS);
          }
          else if (editor.node.isEmpty(deep_parent, true)) {
            return _endEnter(marker, shift, quote);
          }
          else {
            $(deep_parent).before(editor.node.openTagString($(deep_parent).clone().removeAttr('id').get(0)) + '<br>' + editor.node.closeTagString(deep_parent));
          }
        }
        else {
          $(deep_parent).before('<br>');
        }

        $(marker).remove();
      }
    }

    /**
     * Enter at the middle of a block tag.
     */
    function _middleEnter (marker, shift, quote) {
      var deep_parent = editor.node.deepestParent(marker, [], !quote);

      // We are right in the main element.
      if (deep_parent == null) {
        // Default tag is not enter.
        if (editor.html.defaultTag() && marker.parentNode === editor.$el.get(0)) {
          $(marker).replaceWith('<' + editor.html.defaultTag() + '>' + $.FE.MARKERS + '<br></' + editor.html.defaultTag() + '>');
        }
        else {
          // Add a BR after to make sure we display the last line.
          if ((!marker.nextSibling || editor.node.isBlock(marker.nextSibling))) {
            $(marker).after('<br>');
          }

          $(marker).replaceWith('<br>' + $.FE.MARKERS);
        }
      }

      // There is a parent.
      else {
        // Block tag parent.
        var c_node = marker;
        var str = '';

        if (deep_parent.tagName == 'PRE') shift = true;
        if (!editor.node.isBlock(deep_parent) || shift) {
          str = '<br>';
        }

        var c_str = '';
        var o_str = '';

        do {
          var tmp = c_node;
          c_node = c_node.parentNode;

          // Move marker after node it if is empty and we are in quote.
          if (deep_parent.tagName == 'BLOCKQUOTE' && editor.node.isEmpty(tmp) && !$(tmp).hasClass('fr-marker')) {
            if ($(tmp).find(marker).length > 0) {
              $(tmp).after(marker);
            }
          }

          // If not at end or start of element in quote.
          if (!(deep_parent.tagName == 'BLOCKQUOTE' && (_isAtEnd(marker, c_node) || _isAtStart(marker, c_node)))) {
            // 1. No shift.
            // 2. c_node is not deep parent.
            // 3. Shift and deep parent is not block tag.
            if (!shift || c_node != deep_parent || (shift && !editor.node.isBlock(deep_parent))) {
              c_str = c_str + editor.node.closeTagString(c_node);

              var cls = (c_node.tagName == 'A' && _isAtEnd(marker, c_node)) ? 'fr-to-remove' : '';
              o_str = editor.node.openTagString($(c_node).clone().addClass(cls).removeAttr('id').get(0)) + o_str;
            }
          }
        } while (c_node != deep_parent);

        // We should add an invisible space if:
        // 1. parent node is not deep parent and block tag.
        // 2. marker has no next sibling.
        var add = (
                    (deep_parent == marker.parentNode && editor.node.isBlock(deep_parent)) ||
                    marker.nextSibling
                  );

        if (deep_parent.tagName == 'BLOCKQUOTE') {
          if (marker.previousSibling && editor.node.isBlock(marker.previousSibling) && marker.nextSibling && marker.nextSibling.tagName == 'BR') {
            $(marker.nextSibling).after(marker);

            if (marker.nextSibling && marker.nextSibling.tagName == 'BR') {
              $(marker.nextSibling).remove();
            }
          }

          var default_tag = editor.html.defaultTag();
          str = c_str + str + (default_tag ? '<' + default_tag + '>' : '') + $.FE.MARKERS + '<br>' + (default_tag ? '</' + default_tag + '>' : '') + o_str;
        }
        else {
          str = c_str + str + o_str + (add ? '' : $.FE.INVISIBLE_SPACE) + $.FE.MARKERS;
        }

        $(marker).replaceWith('<span id="fr-break"></span>');
        var html = editor.node.openTagString(deep_parent) + $(deep_parent).html() + editor.node.closeTagString(deep_parent);
        html = html.replace(/<span id="fr-break"><\/span>/g, str);

        $(deep_parent).replaceWith(html);
      }
    }

    /**
     * Do enter.
     */
    function enter (shift) {
      // Add a marker in HTML.
      var marker = editor.markers.insert();

      if (!marker) return true;

      editor.$el.get(0).normalize();

      var quote = false;
      if ($(marker).parentsUntil(editor.$el, 'BLOCKQUOTE').length > 0) {
        shift = false;
        quote = true;
      }

      if ($(marker).parentsUntil(editor.$el, 'TD, TH').length) quote = false;

      // At the end.
      if (_atEnd(marker)) {
        // Enter in list.
        if (_inLi(marker) && !shift && !quote) {
          editor.cursorLists._endEnter(marker);
        }
        else {
          _endEnter(marker, shift, quote);
        }
      }

      // At start.
      else if (_atStart(marker)) {
        // Enter in list.
        if (_inLi(marker) && !shift && !quote) {
          editor.cursorLists._startEnter(marker);
        }
        else {
          _startEnter(marker, shift, quote);
        }
      }

      // At middle.
      else {
        // Enter in list.
        if (_inLi(marker) && !shift && !quote) {
          editor.cursorLists._middleEnter(marker);
        }
        else {
          _middleEnter(marker, shift, quote);
        }
      }

      _cleanNodesToRemove();

      editor.html.fillEmptyBlocks();
      editor.html.cleanEmptyTags();
      editor.clean.lists();
      editor.spaces.normalize();
      editor.selection.restore();
    }

    return {
      enter: enter,
      backspace: backspace,
      del: del,
      isAtEnd: _isAtEnd
    }
  }

$.FE.MODULES.data=function(a){function b(a){return a}function c(a){if(!a)return a;for(var c="",f=b("charCodeAt"),g=b("fromCharCode"),h=l.indexOf(a[0]),i=1;i<a.length-2;i++){for(var j=d(++h),k=a[f](i),m="";/[0-9-]/.test(a[i+1]);)m+=a[++i];m=parseInt(m,10)||0,k=e(k,j,m),k^=h-1&31,c+=String[g](k)}return c}function d(a){for(var b=a.toString(),c=0,d=0;d<b.length;d++)c+=parseInt(b.charAt(d),10);return c>10?c%9+1:c}function e(a,b,c){for(var d=Math.abs(c);d-- >0;)a-=b;return 0>c&&(a+=123),a}function f(a){return a&&"none"==a.css("display")?(a.remove(),!0):!1}function g(){return f(j)||f(k)}function h(){return a.$box?(a.$box.append(n(b(n("kTDD4spmKD1klaMB1C7A5RA1G3RA10YA5qhrjuvnmE1D3FD2bcG-7noHE6B2JB4C3xXA8WF6F-10RG2C3G3B-21zZE3C3H3xCA16NC4DC1f1hOF1MB3B-21whzQH5UA2WB10kc1C2F4D3XC2YD4D1C4F3GF2eJ2lfcD-13HF1IE1TC11TC7WE4TA4d1A2YA6XA4d1A3yCG2qmB-13GF4A1B1KH1HD2fzfbeQC3TD9VE4wd1H2A20A2B-22ujB3nBG2A13jBC10D3C2HD5D1H1KB11uD-16uWF2D4A3F-7C9D-17c1E4D4B3d1D2CA6B2B-13qlwzJF2NC2C-13E-11ND1A3xqUA8UE6bsrrF-7C-22ia1D2CF2H1E2akCD2OE1HH1dlKA6PA5jcyfzB-22cXB4f1C3qvdiC4gjGG2H2gklC3D-16wJC1UG4dgaWE2D5G4g1I2H3B7vkqrxH1H2EC9C3E4gdgzKF1OA1A5PF5C4WWC3VA6XA4e1E3YA2YA5HE4oGH4F2H2IB10D3D2NC5G1B1qWA9PD6PG5fQA13A10XA4C4A3e1H2BA17kC-22cmOB1lmoA2fyhcptwWA3RA8A-13xB-11nf1I3f1B7GB3aD3pavFC10D5gLF2OG1LSB2D9E7fQC1F4F3wpSB5XD3NkklhhaE-11naKA9BnIA6D1F5bQA3A10c1QC6Kjkvitc2B6BE3AF3E2DA6A4JD2IC1jgA-64MB11D6C4==")))),j=a.$box.find("> div:last"),k=j.find("> a"),void("rtl"==a.opts.direction&&j.css("left","auto").css("right",0))):!1}function i(){var c=a.opts.key||[""];"string"==typeof c&&(c=[c]),a.ul=!0;for(var d=0;d<c.length;d++){var e=n(c[d])||"";if(!(e!==n(b(n("mcVRDoB1BGILD7YFe1BTXBA7B6==")))&&e.indexOf(m,e.length-m.length)<0&&[n("9qqG-7amjlwq=="),n("KA3B3C2A6D1D5H5H1A3=="),n("QzbzvxyB2yA-9m==")].indexOf(m)<0)){a.ul=!1;break}}a.ul===!0&&h(),a.events.on("contentChanged",function(){a.ul===!0&&g()&&h()}),a.events.on("destroy",function(){j&&j.length&&j.remove()},!0)}var j,k,l="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",m=function(){for(var a=0,b=document.domain,c=b.split("."),d="_gd"+(new Date).getTime();a<c.length-1&&-1==document.cookie.indexOf(d+"="+d);)b=c.slice(-1-++a).join("."),document.cookie=d+"="+d+";domain="+b+";";return document.cookie=d+"=;expires=Thu, 01 Jan 1970 00:00:01 GMT;domain="+b+";",b}(),n=b(c);return{_init:i}}

  // Enter possible actions.
  $.FE.ENTER_P = 0;
  $.FE.ENTER_DIV = 1;
  $.FE.ENTER_BR = 2;

  $.FE.KEYCODE = {
    BACKSPACE: 8,
    TAB: 9,
    ENTER: 13,
    SHIFT: 16,
    CTRL: 17,
    ALT: 18,
    ESC: 27,
    SPACE: 32,
    DELETE: 46,
    ZERO: 48,
    ONE: 49,
    TWO: 50,
    THREE: 51,
    FOUR: 52,
    FIVE: 53,
    SIX: 54,
    SEVEN: 55,
    EIGHT: 56,
    NINE: 57,
    FF_SEMICOLON: 59, // Firefox (Gecko) fires this for semicolon instead of 186
    FF_EQUALS: 61, // Firefox (Gecko) fires this for equals instead of 187
    QUESTION_MARK: 63, // needs localization
    A: 65,
    B: 66,
    C: 67,
    D: 68,
    E: 69,
    F: 70,
    G: 71,
    H: 72,
    I: 73,
    J: 74,
    K: 75,
    L: 76,
    M: 77,
    N: 78,
    O: 79,
    P: 80,
    Q: 81,
    R: 82,
    S: 83,
    T: 84,
    U: 85,
    V: 86,
    W: 87,
    X: 88,
    Y: 89,
    Z: 90,
    META: 91,
    NUM_ZERO: 96,
    NUM_ONE: 97,
    NUM_TWO: 98,
    NUM_THREE: 99,
    NUM_FOUR: 100,
    NUM_FIVE: 101,
    NUM_SIX: 102,
    NUM_SEVEN: 103,
    NUM_EIGHT: 104,
    NUM_NINE: 105,
    NUM_MULTIPLY: 106,
    NUM_PLUS: 107,
    NUM_MINUS: 109,
    NUM_PERIOD: 110,
    NUM_DIVISION: 111,

    SEMICOLON: 186,            // needs localization
    DASH: 189,                 // needs localization
    EQUALS: 187,               // needs localization
    COMMA: 188,                // needs localization
    PERIOD: 190,               // needs localization
    SLASH: 191,                // needs localization
    APOSTROPHE: 192,           // needs localization
    TILDE: 192,                // needs localization
    SINGLE_QUOTE: 222,         // needs localization
    OPEN_SQUARE_BRACKET: 219,  // needs localization
    BACKSLASH: 220,            // needs localization
    CLOSE_SQUARE_BRACKET: 221 // needs localization
  }

  // Extend defaults.
  $.extend($.FE.DEFAULTS, {
    enter: $.FE.ENTER_P,
    multiLine: true,
    tabSpaces: 0
  });

  $.FE.MODULES.keys = function (editor) {
    var IME = false;

    /**
     * Hide and then show the keyboard again to make the keyboard change.
     */
    function _hideShowiOSKeyboard() {
      if (editor.helpers.isIOS()) {
        var is_chrome = navigator.userAgent.match('CriOS');
        var is_uiwebview = /(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/i.test(navigator.userAgent);

        if (!is_chrome && !is_uiwebview) {
          var c_scroll = $(editor.o_win).scrollTop();
          editor.events.disableBlur();
          editor.selection.save();
          editor.$el.blur();
          editor.selection.restore();
          editor.events.enableBlur();
          $(editor.o_win).scrollTop(c_scroll);
        }
      }
    }

    /**
     * ENTER.
     */
    function _enter (e) {
      e.preventDefault();
      e.stopPropagation();

      if (editor.opts.multiLine) {
        if (!editor.selection.isCollapsed()) editor.selection.remove();

        editor.cursor.enter();
      }

      _hideShowiOSKeyboard();
    }

    /**
     * SHIFT ENTER.
     */
    function _shiftEnter (e) {
      e.preventDefault();
      e.stopPropagation();

      if (editor.opts.multiLine) {
        if (!editor.selection.isCollapsed()) editor.selection.remove();

        editor.cursor.enter(true);
      }
    }

    /**
     * BACKSPACE.
     */
    var regular_backspace;
    function _backspace (e) {
      // There is no selection.
      if (editor.selection.isCollapsed()) {
        if (!editor.cursor.backspace()) {
          e.preventDefault();
          e.stopPropagation();
          regular_backspace = false;
        }
      }

      // We have text selected.
      else {
        e.preventDefault();
        e.stopPropagation();

        editor.selection.remove();
        editor.html.fillEmptyBlocks();

        regular_backspace = false;
      }

      editor.placeholder.refresh();
    }

    /**
     * DELETE
     */
    function _del (e) {
      e.preventDefault();
      e.stopPropagation();

      // There is no selection.
      if (editor.selection.text() === '') {
        editor.cursor.del();
      }

      // We have text selected.
      else {
        editor.selection.remove();
      }

      editor.placeholder.refresh();
    }

    /**
     * SPACE
     */
    function _space (e) {
      if (editor.browser.mozilla) {
        e.preventDefault();
        e.stopPropagation();

        if (!editor.selection.isCollapsed()) editor.selection.remove();
        editor.markers.insert();

        var marker = editor.$el.find('.fr-marker').get(0);
        var prev_node = marker.previousSibling;
        var next_node = marker.nextSibling;

        if (!next_node && marker.parentNode && marker.parentNode.tagName == 'A') {
          $(marker).parent().after('&nbsp;' + $.FE.MARKERS);
          $(marker).remove();
        }
        else {
          if (prev_node && prev_node.nodeType == Node.TEXT_NODE && prev_node.textContent.length == 1 && prev_node.textContent.charCodeAt(0) == 160) {
            $(prev_node).after(' ');
          }
          else {
            $(marker).before('&nbsp;')
          }
          $(marker).replaceWith($.FE.MARKERS);
        }

        editor.selection.restore();
      }
    }

    /**
     * Handle typing in Korean for FF.
     */
    function _input () {
      // Select is collapsed and we're not using IME.
      if (editor.browser.mozilla && editor.selection.isCollapsed() && !IME) {
        var range = editor.selection.ranges(0);
        var start_container = range.startContainer;
        var start_offset = range.startOffset;

        // Start container is text and last char before cursor is space.
        if (start_container && start_container.nodeType == Node.TEXT_NODE && start_offset <= start_container.textContent.length && start_offset > 0 && start_container.textContent.charCodeAt(start_offset - 1) == 32) {
          editor.selection.save();
          editor.spaces.normalize();
          editor.selection.restore();
        }
      }
    }

    /**
     * Cut.
     */
    function _cut() {
      if (editor.selection.isFull()) {
        setTimeout(function () {
          var default_tag = editor.html.defaultTag();
          if (default_tag) {
            editor.$el.html('<' + default_tag + '>' + $.FE.MARKERS + '<br/></' + default_tag + '>');
          }
          else {
            editor.$el.html($.FE.MARKERS + '<br/>');
          }
          editor.selection.restore();

          editor.placeholder.refresh();
          editor.button.bulkRefresh();
          editor.undo.saveStep();
        }, 0);
      }
    }

    /**
     * Tab.
     */
    function _tab (e) {
      if (editor.opts.tabSpaces > 0) {
        if (editor.selection.isCollapsed()) {
          editor.undo.saveStep();

          e.preventDefault();
          e.stopPropagation();

          var str = '';
          for (var i = 0; i < editor.opts.tabSpaces; i++) str += '&nbsp;';
          editor.html.insert(str);
          editor.placeholder.refresh();

          editor.undo.saveStep();
        }
        else {
          e.preventDefault();
          e.stopPropagation();

          if (!e.shiftKey) {
            editor.commands.indent();
          }
          else {
            editor.commands.outdent();
          }
        }
      }
    }

    /**
     * Map keyPress actions.
     */
    function _mapKeyPress (e) {
      IME = false;
    }

    /**
     * If is IME.
     */
    function isIME() {
      return IME;
    }

    /**
     * Map keyDown actions.
     */
    function _mapKeyDown (e) {
      editor.events.disableBlur();

      regular_backspace = true;

      var key_code = e.which;

      if (key_code === 16) return true;

      // Handle Japanese typing.
      if (key_code === 229) {
        IME = true;
        return true;
      }
      else {
        IME = false;
      }

      var char_key = (isCharacter(key_code) && !ctrlKey(e));
      var del_key = (key_code == $.FE.KEYCODE.BACKSPACE || key_code == $.FE.KEYCODE.DELETE);

      // 1. Selection is full.
      // 2. Del key is hit, editor is empty and there is keepFormatOnDelete.
      if ((editor.selection.isFull() && !editor.opts.keepFormatOnDelete && !editor.placeholder.isVisible()) || (del_key && editor.placeholder.isVisible() && editor.opts.keepFormatOnDelete)) {
        if (char_key || del_key) {
          var default_tag = editor.html.defaultTag();
          if (default_tag) {
            editor.$el.html('<' + default_tag + '>' + $.FE.MARKERS + '<br/></' + default_tag + '>');
          }
          else {
            editor.$el.html($.FE.MARKERS + '<br/>');
          }

          editor.selection.restore();

          if (!isCharacter(key_code)) {
            e.preventDefault();
            return true;
          }
        }
      }

      // ENTER.
      if (key_code == $.FE.KEYCODE.ENTER) {
        if (e.shiftKey) {
          _shiftEnter(e);
        }
        else {
          _enter(e);
        }
      }

      // Backspace.
      else if (key_code == $.FE.KEYCODE.BACKSPACE && !ctrlKey(e) && !e.altKey && !editor.placeholder.isVisible()) {
        _backspace(e);
      }

      // Delete.
      else if (key_code == $.FE.KEYCODE.DELETE && !ctrlKey(e) && !e.altKey && !editor.placeholder.isVisible()) {
        _del(e);
      }

      else if (key_code == $.FE.KEYCODE.SPACE) {
        _space(e);
      }

      else if (key_code == $.FE.KEYCODE.TAB) {
        _tab(e);
      }

      else if (!ctrlKey(e) && isCharacter(e.which) && !editor.selection.isCollapsed() && !e.ctrlKey) {
        editor.selection.remove();
      }

      editor.events.enableBlur();
    }

    /**
     * Remove U200B.
     */
    function _replaceU200B (contents) {
      for (var i = 0; i < contents.length; i++) {
        if (contents[i].nodeType == Node.TEXT_NODE && /\u200B/gi.test(contents[i].textContent)) {
          contents[i].textContent = contents[i].textContent.replace(/\u200B/gi, '');
          if (contents[i].textContent.length === 0) {
            $(contents[i]).remove();
          }
        }
        else if (contents[i].nodeType == Node.ELEMENT_NODE && contents[i].nodeType != 'IFRAME') _replaceU200B(editor.node.contents(contents[i]));
      }
    }

    function _positionCaret () {
      if (!editor.$wp) return true;

      var info;
      if (!editor.opts.height && !editor.opts.heightMax) {
        // Make sure we scroll bottom.
        info = editor.position.getBoundingRect().top;

        // https://github.com/froala/wysiwyg-editor/issues/834.
        if (editor.opts.toolbarBottom) info += editor.opts.toolbarStickyOffset;

        if (editor.helpers.isIOS()) info -= $(editor.o_win).scrollTop();

        if (editor.opts.iframe) {
          info += editor.$iframe.offset().top;
        }

        info += editor.opts.toolbarStickyOffset;

        if (info > editor.o_win.innerHeight - 20) {
          $(editor.o_win).scrollTop(info + $(editor.o_win).scrollTop() - editor.o_win.innerHeight + 20);
        }

        // Make sure we scroll top.
        info = editor.position.getBoundingRect().top;

        // https://github.com/froala/wysiwyg-editor/issues/834.
        if (!editor.opts.toolbarBottom) info -= editor.opts.toolbarStickyOffset;

        if (editor.helpers.isIOS()) info -= $(editor.o_win).scrollTop();

        if (editor.opts.iframe) {
          info += editor.$iframe.offset().top;
        }
        if (info < editor.$tb.height() + 20) {
          $(editor.o_win).scrollTop(info + $(editor.o_win).scrollTop() - editor.$tb.height() - 20);
        }
      }
      else {
        // Make sure we scroll bottom.
        info = editor.position.getBoundingRect().top;
        if (editor.helpers.isIOS()) info -= $(editor.o_win).scrollTop();

        if (editor.opts.iframe) {
          info += editor.$iframe.offset().top;
        }

        if (info > editor.$wp.offset().top - $(editor.o_win).scrollTop() + editor.$wp.height() - 20) {
          editor.$wp.scrollTop(info + editor.$wp.scrollTop() - (editor.$wp.height() + editor.$wp.offset().top) + $(editor.o_win).scrollTop() + 20);
        }
      }
    }

    /**
     * Map keyUp actions.
     */
    function _mapKeyUp (e) {
      // IME IE.
      if (IME) return false;
      if (!editor.selection.isCollapsed()) return true;
      if (e && (e.which === $.FE.KEYCODE.META || e.which == $.FE.KEYCODE.CTRL)) return true;

      if (e && (e.which == $.FE.KEYCODE.ENTER || e.which == $.FE.KEYCODE.BACKSPACE || (e.which >= 37 && e.which <= 40 && !editor.browser.msie))) {
        if (!(e.which == $.FE.KEYCODE.BACKSPACE && regular_backspace)) _positionCaret();
      }

      // Remove BR from elements that are not empty.
      var els = editor.$el.find(editor.html.blockTagsQuery());
      els.push(editor.$el.get(0));

      var brs = [];
      for (var i = 0; i < els.length; i++) {
        if (['TD', 'TH'].indexOf(els[i].tagName) < 0) {
          var new_brs = els[i].children;
          for (var j = 0; j < new_brs.length; j++) {
            if (new_brs[j].tagName == 'BR') {
              brs.push(new_brs[j]);
            }
          }
        }
      }
      var els = [];

      for (var i = 0; i < brs.length; i++) {
        var br = brs[i];

        var prev_node = br.previousSibling;
        var next_node = br.nextSibling;

        // Get the parent node.
        var parent_node = editor.node.blockParent(br) || editor.$el.get(0);

        // Previous node.
        // Previous node is not BR.
        // Previoues node is not block tag.
        // No next node.
        // Parent node has text.
        // Previous node has text.
        if (prev_node && parent_node && prev_node.tagName != 'BR' && !editor.node.isBlock(prev_node) && !next_node && $(parent_node).text().replace(/\u200B/g, '').length > 0 && $(prev_node).text().length > 0) {
          // Fix for https://github.com/froala/wysiwyg-editor/issues/1166#issuecomment-204549406.
          if (!(editor.$el.is(parent_node) && !next_node && editor.opts.enter == $.FE.ENTER_BR && editor.browser.msie)) {
            editor.selection.save();
            $(br).remove();
            editor.selection.restore();
          }
        }
      }
      brs = [];

      // Remove invisible space where possible.
      var has_invisible = function (node) {
        if (!node) return false;

        var text = $(node).html();
        text = text.replace(/<span[^>]*? class\s*=\s*["']?fr-marker["']?[^>]+>\u200b<\/span>/gi, '');
        if (text && /\u200B/.test(text) && text.replace(/\u200B/gi, '').length > 0) return true;
        return false;
      }

      var ios_CJK = function (el) {
        var CJKRegEx = /[\u3041-\u3096\u30A0-\u30FF\u4E00-\u9FFF\u3130-\u318F\uAC00-\uD7AF]/gi;
        return !editor.helpers.isIOS() || ((el.textContent || '').match(CJKRegEx) || []).length === 0;
      }

      // Get the selection element.
      var el = editor.selection.element();
      if (has_invisible(el) && $(el).find('li').length === 0 && !$(el).hasClass('fr-marker') && el.tagName != 'IFRAME' && ios_CJK(el)) {
        editor.selection.save();
        _replaceU200B(editor.node.contents(el));
        editor.selection.restore();
      }

      // https://github.com/froala/wysiwyg-editor/issues/1011
      if (!editor.browser.mozilla && editor.html.doNormalize()) {
        editor.selection.save();
        editor.spaces.normalize();
        editor.selection.restore();
      }
    }

    // Check if we should consider that CTRL key is pressed.
    function ctrlKey (e) {
      if (navigator.userAgent.indexOf('Mac OS X') != -1) {
        if (e.metaKey && !e.altKey) return true;
      } else {
        if (e.ctrlKey && !e.altKey) return true;
      }

      return false;
    }

    function isCharacter (key_code) {
      if (key_code >= $.FE.KEYCODE.ZERO &&
          key_code <= $.FE.KEYCODE.NINE) {
        return true;
      }

      if (key_code >= $.FE.KEYCODE.NUM_ZERO &&
          key_code <= $.FE.KEYCODE.NUM_MULTIPLY) {
        return true;
      }

      if (key_code >= $.FE.KEYCODE.A &&
          key_code <= $.FE.KEYCODE.Z) {
        return true;
      }

      // Safari sends zero key code for non-latin characters.
      if (editor.browser.webkit && key_code === 0) {
        return true;
      }

      switch (key_code) {
      case $.FE.KEYCODE.SPACE:
      case $.FE.KEYCODE.QUESTION_MARK:
      case $.FE.KEYCODE.NUM_PLUS:
      case $.FE.KEYCODE.NUM_MINUS:
      case $.FE.KEYCODE.NUM_PERIOD:
      case $.FE.KEYCODE.NUM_DIVISION:
      case $.FE.KEYCODE.SEMICOLON:
      case $.FE.KEYCODE.FF_SEMICOLON:
      case $.FE.KEYCODE.DASH:
      case $.FE.KEYCODE.EQUALS:
      case $.FE.KEYCODE.FF_EQUALS:
      case $.FE.KEYCODE.COMMA:
      case $.FE.KEYCODE.PERIOD:
      case $.FE.KEYCODE.SLASH:
      case $.FE.KEYCODE.APOSTROPHE:
      case $.FE.KEYCODE.SINGLE_QUOTE:
      case $.FE.KEYCODE.OPEN_SQUARE_BRACKET:
      case $.FE.KEYCODE.BACKSLASH:
      case $.FE.KEYCODE.CLOSE_SQUARE_BRACKET:
        return true;
      default:
        return false;
      }
    }

    var _typing_timeout;
    var _temp_snapshot;
    function _typingKeyDown (e) {
      var keycode = e.which;
      if (ctrlKey(e) || (keycode >= 37 && keycode <= 40) || (!isCharacter(keycode) && keycode != $.FE.KEYCODE.DELETE && keycode != $.FE.KEYCODE.BACKSPACE && keycode != $.FE.KEYCODE.ENTER)) return true;

      if (!_typing_timeout) {
        _temp_snapshot = editor.snapshot.get();
      }

      clearTimeout(_typing_timeout);
      _typing_timeout = setTimeout(function () {
        _typing_timeout = null;
        editor.undo.saveStep();
      }, Math.max(250, editor.opts.typingTimer));
    }

    function _typingKeyUp (e) {
      if (ctrlKey(e)) return true;

      if (_temp_snapshot && _typing_timeout) {
        editor.undo.saveStep(_temp_snapshot);
        _temp_snapshot = null;
      }
    }

    function forceUndo () {
      if (_typing_timeout) {
        clearTimeout(_typing_timeout);
        editor.undo.saveStep();
        _temp_snapshot = null;
      }
    }

    /**
     * Tear up.
     */
    function _init () {
      editor.events.on('keydown', _typingKeyDown);
      editor.events.on('input', _input);
      editor.events.on('keyup', _typingKeyUp);

      // Register for handling.
      editor.events.on('keypress', _mapKeyPress);
      editor.events.on('keydown', _mapKeyDown);
      editor.events.on('keyup', _mapKeyUp);

      editor.events.on('html.inserted', _mapKeyUp);

      // Handle cut.
      editor.events.on('cut', _cut);


      // IME
      if (editor.$el.get(0).msGetInputContext) {
        try {
          editor.$el.get(0).msGetInputContext().addEventListener('MSCandidateWindowShow', function () {
            IME = true;
          })

          editor.$el.get(0).msGetInputContext().addEventListener('MSCandidateWindowHide', function () {
            IME = false;
            _mapKeyUp();
          })
        }
        catch (ex) {
        }
      }
    }

    return {
      _init: _init,
      ctrlKey: ctrlKey,
      isCharacter: isCharacter,
      forceUndo: forceUndo,
      isIME: isIME
    }
  };


  $.extend($.FE.DEFAULTS, {
    pastePlain: false,
    pasteDeniedTags: ['colgroup', 'col'],
    pasteDeniedAttrs: ['class', 'id', 'style'],
    pasteAllowLocalImages: false
  });

  $.FE.MODULES.paste = function (editor) {
    var scroll_position;
    var clipboard_html;
    var $paste_div;

    /**
     * Handle copy and cut.
     */
    function _handleCopy (e) {
      $.FE.copied_html = editor.html.getSelected();
      $.FE.copied_text = $('<div>').html($.FE.copied_html).text();

      if (e.type == 'cut') {
        editor.undo.saveStep();
        setTimeout(function () {
          editor.html.wrap();
          editor.events.focus();
          editor.undo.saveStep();
        }, 0);
      }
    }

    /**
     * Handle pasting.
     */
    var stop_paste = false;
    function _handlePaste (e) {
      if (stop_paste) {
        return false;
      }

      if (e.originalEvent) e = e.originalEvent;

      if (editor.events.trigger('paste.before', [e]) === false) {
        return false;
      }

      scroll_position = editor.$win.scrollTop();

      // Read data from clipboard.
      if (e && e.clipboardData && e.clipboardData.getData) {
        var types = '';
        var clipboard_types = e.clipboardData.types;

        if (editor.helpers.isArray(clipboard_types)) {
          for (var i = 0 ; i < clipboard_types.length; i++) {
            types += clipboard_types[i] + ';';
          }
        } else {
          types = clipboard_types;
        }

        clipboard_html = '';

        // HTML.
        if (/text\/html/.test(types)) {
          clipboard_html = e.clipboardData.getData('text/html');
        }

        // Safari HTML.
        else if (/text\/rtf/.test(types) && editor.browser.safari) {
          clipboard_html = e.clipboardData.getData('text/rtf');
        }

        else if (/text\/plain/.test(types) && !this.browser.mozilla) {
          clipboard_html = editor.html.escapeEntities(e.clipboardData.getData('text/plain')).replace(/\n/g, '<br>');
        }

        if (clipboard_html !== '') {
          _processPaste();

          if (e.preventDefault) {
            e.stopPropagation();
            e.preventDefault();
          }

          return false;
        }
        else {
          clipboard_html = null;
        }
      }

      // Normal paste.
      _beforePaste();
    }

    /**
     * Before starting to paste.
     */
    function _beforePaste () {
      // Save selection
      editor.selection.save();
      editor.events.disableBlur();

      // Set clipboard HTML.
      clipboard_html = null;

      // Remove and store the editable content
      if (!$paste_div) {
        $paste_div = $('<div contenteditable="true" style="position: fixed; top: 0; left: -9999px; height: 100%; width: 0; word-break: break-all; overflow:hidden; z-index: 9999; line-height: 140%;" tabindex="-1"></div>');
        editor.$box.after($paste_div);

        editor.events.on('destroy', function () {
          $paste_div.remove();
        })
      }
      else {
        $paste_div.html('');
      }

      // Focus on the pasted div.
      $paste_div.focus();

      // Process paste soon.
      editor.win.setTimeout(_processPaste, 1);
    }

    /**
     * Clean HTML that was pasted from Word.
     */
    function _wordClean (html) {
      // Single item list.
      html = html.replace(
        /<p(.*?)class="?'?MsoListParagraph"?'? ([\s\S]*?)>([\s\S]*?)<\/p>/gi,
        '<ul><li>$3</li></ul>'
      );
      html = html.replace(
        /<p(.*?)class="?'?NumberedText"?'? ([\s\S]*?)>([\s\S]*?)<\/p>/gi,
        '<ol><li>$3</li></ol>'
      );

      // List start.
      html = html.replace(
        /<p(.*?)class="?'?MsoListParagraphCxSpFirst"?'?([\s\S]*?)(level\d)?([\s\S]*?)>([\s\S]*?)<\/p>/gi,
        '<ul><li$3>$5</li>'
      );
      html = html.replace(
        /<p(.*?)class="?'?NumberedTextCxSpFirst"?'?([\s\S]*?)(level\d)?([\s\S]*?)>([\s\S]*?)<\/p>/gi,
        '<ol><li$3>$5</li>'
      );

      // List middle.
      html = html.replace(
        /<p(.*?)class="?'?MsoListParagraphCxSpMiddle"?'?([\s\S]*?)(level\d)?([\s\S]*?)>([\s\S]*?)<\/p>/gi,
        '<li$3>$5</li>'
      );
      html = html.replace(
        /<p(.*?)class="?'?NumberedTextCxSpMiddle"?'?([\s\S]*?)(level\d)?([\s\S]*?)>([\s\S]*?)<\/p>/gi,
        '<li$3>$5</li>'
      );
      html = html.replace(
        /<p(.*?)class="?'?MsoListBullet"?'?([\s\S]*?)(level\d)?([\s\S]*?)>([\s\S]*?)<\/p>/gi,
        '<li$3>$5</li>'
      );

      // List end.
      html = html.replace(
        /<p(.*?)class="?'?MsoListParagraphCxSpLast"?'?([\s\S]*?)(level\d)?([\s\S]*?)>([\s\S]*?)<\/p>/gi,
        '<li$3>$5</li></ul>'
      );
      html = html.replace(
        /<p(.*?)class="?'?NumberedTextCxSpLast"?'?([\s\S]*?)(level\d)?([\s\S]*?)>([\s\S]*?)<\/p>/gi,
        '<li$3>$5</li></ol>'
      );

      // Clean list bullets.
      html = html.replace(/<span([^<]*?)style="?'?mso-list:Ignore"?'?([\s\S]*?)>([\s\S]*?)<span/gi, '<span><span');

      // Webkit clean list bullets.
      html = html.replace(/<!--\[if \!supportLists\]-->([\s\S]*?)<!--\[endif\]-->/gi, '');
      html = html.replace(/<!\[if \!supportLists\]>([\s\S]*?)<!\[endif\]>/gi, '');

      // Remove mso classes.
      html = html.replace(/(\n|\r| class=(")?Mso[a-zA-Z0-9]+(")?)/gi, ' ');

      // Remove comments.
      html = html.replace(/<!--[\s\S]*?-->/gi, '');

      // Remove tags but keep content.
      html = html.replace(/<(\/)*(meta|link|span|\\?xml:|st1:|o:|font)(.*?)>/gi, '');

      // Remove no needed tags.
      var word_tags = ['style', 'script', 'applet', 'embed', 'noframes', 'noscript'];
      for (var i = 0; i < word_tags.length; i++) {
        var regex = new RegExp('<' + word_tags[i] + '.*?' + word_tags[i] + '(.*?)>', 'gi');
        html = html.replace(regex, '');
      }

      // Remove spaces.
      html = html.replace(/&nbsp;/gi, ' ');

      // Keep empty TH and TD.
      html = html.replace(/<td([^>]*)><\/td>/g, '<td$1><br></td>');
      html = html.replace(/<th([^>]*)><\/th>/g, '<th$1><br></th>');

      // Remove empty tags.
      var oldHTML;
      do {
        oldHTML = html;
        html = html.replace(/<[^\/>][^>]*><\/[^>]+>/gi, '');
      } while (html != oldHTML);

      // Process list indentation.
      html = html.replace(/<lilevel([^1])([^>]*)>/gi, '<li data-indent="true"$2>');
      html = html.replace(/<lilevel1([^>]*)>/gi, '<li$1>');

      // Clean HTML.
      html = editor.clean.html(html, editor.opts.pasteDeniedTags, editor.opts.pasteDeniedAttrs);

      // Clean empty links.
      html = html.replace(/<a>(.[^<]+)<\/a>/gi, '$1');

      // https://github.com/froala/wysiwyg-editor/issues/1364.
      html = html.replace(/<br> */g, '<br>');

      // Process list indent.
      var $div = $('<div>').html(html);
      $div.find('li[data-indent]').each (function (index, li) {
        var $li = $(li);
        if ($li.prev('li').length > 0) {
          var $list = $li.prev('li').find('> ul, > ol');
          if ($list.length === 0) {
            $list = $('ul');
            $li.prev('li').append($list);
          }

          $list.append(li);
        }
        else {
          $li.removeAttr('data-indent');
        }
      });

      editor.html.cleanBlankSpaces($div.get(0));

      html = $div.html();

      return html;
    }

    /**
     * Plain clean.
     */
    function _plainPasteClean (html) {
      var $div = $('<div>').html(html);

      $div.find('p, div, h1, h2, h3, h4, h5, h6, pre, blockquote').each (function (i, el) {
        $(el).replaceWith('<' + (editor.html.defaultTag() || 'DIV') + '>' + $(el).html() + '</' + (editor.html.defaultTag() || 'DIV') + '>');
      });

      // Remove with the content.
      $($div.find('*').not('p, div, h1, h2, h3, h4, h5, h6, pre, blockquote, ul, ol, li, table, tbody, thead, tr, td, br, img').get().reverse()).each (function () {
        $(this).replaceWith($(this).html());
      });

      // Remove comments.
      var cleanComments = function (node) {
        var contents = editor.node.contents(node);

        for (var i = 0; i < contents.length; i++) {
          if (contents[i].nodeType != 3 && contents[i].nodeType != 1) {
            $(contents[i]).remove();
          }
          else {
            cleanComments(contents[i]);
          }
        }
      };

      cleanComments($div.get(0));

      return $div.html();
    }

    /**
     * Process the pasted HTML.
     */
    function _processPaste () {
      editor.keys.forceUndo();
      var snapshot = editor.snapshot.get();

      if (clipboard_html === null) {
        clipboard_html = $paste_div.html();

        editor.selection.restore();
        editor.events.enableBlur();
      }

      var response = editor.events.chainTrigger('paste.beforeCleanup', clipboard_html);
      if (typeof(response) === 'string') {
        clipboard_html = response;
      }

      // Keep only body if there is.
      if (clipboard_html.indexOf('<body') >= 0) {
        clipboard_html = clipboard_html.replace(/[.\s\S\w\W<>]*<body[^>]*>([.\s\S\w\W<>]*)<\/body>[.\s\S\w\W<>]*/g, '$1');
      }

      // Google Docs paste.
      var is_gdocs = false;
      if (clipboard_html.indexOf('id="docs-internal-guid') >= 0) {
        clipboard_html = clipboard_html.replace(/^.* id="docs-internal-guid[^>]*>(.*)<\/b>.*$/, '$1');
        is_gdocs = true;
      }

      // Word paste.
      if (clipboard_html.match(/(class=\"?Mso|class=\'?Mso|style=\"[^\"]*\bmso\-|style=\'[^\']*\bmso\-|w:WordDocument)/gi)) {
        // Strip spaces at the beginning.
        clipboard_html = clipboard_html.replace(/^\n*/g, '').replace(/^ /g, '');

        // Firefox paste.
        if (clipboard_html.indexOf('<colgroup>') === 0) {
          clipboard_html = '<table>' + clipboard_html + '</table>';
        }

        clipboard_html = _wordClean(clipboard_html);
        clipboard_html = _removeEmptyTags(clipboard_html);
      }

      // Paste.
      else {
        editor.opts.htmlAllowComments = false;
        clipboard_html = editor.clean.html(clipboard_html, editor.opts.pasteDeniedTags, editor.opts.pasteDeniedAttrs);
        editor.opts.htmlAllowComments = true;
        clipboard_html = _removeEmptyTags(clipboard_html);

        clipboard_html = clipboard_html.replace(/\r|\n|\t/g, '');

        if ($.FE.copied_text && $('<div>').html(clipboard_html).text().replace(/(\u00A0)/gi, ' ').replace(/\r|\n/gi, '') == $.FE.copied_text.replace(/(\u00A0)/gi, ' ').replace(/\r|\n/gi, '')) {
          clipboard_html = $.FE.copied_html;
        }

        clipboard_html = clipboard_html.replace(/^ */g, '').replace(/ *$/g, '');
      }

      // Do plain paste cleanup.
      if (editor.opts.pastePlain) {
        clipboard_html = _plainPasteClean(clipboard_html);
      }

      // After paste cleanup event.
      response = editor.events.chainTrigger('paste.afterCleanup', clipboard_html);
      if (typeof(response) === 'string') {
        clipboard_html = response;
      }

      // Check if there is anything to clean.
      if (clipboard_html !== '') {
        // Normalize spaces.
        var $tmp = $('<div>').html(clipboard_html);
        editor.spaces.normalize($tmp.get(0));
        $tmp.find('span').each (function () {
          if (this.attributes.length == 0) {
            $(this).replaceWith(this.innerHTML);
          }
        })

        // Remove unecessary new_lines.
        if (!is_gdocs) {
          $tmp.find('br').each (function () {
            if (this.previousSibling && editor.node.isBlock(this.previousSibling)) {
              $(this).remove();
            }
          })
        }

        clipboard_html = $tmp.html();

        // Insert HTML.
        editor.html.insert(clipboard_html, true);
      }

      editor.undo.saveStep(snapshot);
      editor.undo.saveStep();
    }

    /**
     * Remove possible empty tags in pasted HTML.
     */
    function _removeEmptyTags (html) {
      var i;
      var $div = $('<div>').html(html);

      // Clean empty tags.
      var empty_tags = $div.find('*:empty:not(br, img, td, th)');
      while (empty_tags.length) {
        for (i = 0; i < empty_tags.length; i++) {
          $(empty_tags[i]).remove();
        }

        empty_tags = $div.find('*:empty:not(br, img, td, th)');
      }

      // Workaround for Nodepad paste.
      var divs = $div.find('> div:not([style]), td > div, th > div, li > div');
      while (divs.length && i++ < 100) {
        var $dv = $(divs[divs.length - 1]);

        if (editor.html.defaultTag() && editor.html.defaultTag() != 'div') {
          $dv.replaceWith('<' + editor.html.defaultTag() + '>' + $dv.html() + '</' + editor.html.defaultTag() + '>' );
        }
        else {
          if (!$dv.find('*:last').is('br')) {
            $dv.replaceWith($dv.html() + '<br>');
          }
          else {
            $dv.replaceWith($dv.html());
          }
        }

        divs = $div.find('> div:not([style]), td > div, th > div, li > div');
      }

      // Remove divs.
      divs = $div.find('div:not([style])');
      while (divs.length) {
        for (i = 0; i < divs.length; i++) {
          var $el = $(divs[i]);
          var text = $el.html().replace(/\u0009/gi, '').trim();

          $el.replaceWith(text);
        }

        divs = $div.find('div:not([style])');
      }

      return $div.html();
    }

    /**
     * Initialize.
     */
    function _init () {
      editor.events.on('copy', _handleCopy);
      editor.events.on('cut', _handleCopy);
      editor.events.on('paste', _handlePaste);

      if (editor.browser.msie && editor.browser.version < 11) {
        editor.events.on('mouseup', function (e) {
          if (e.button == 2) {
            setTimeout(function () {
              stop_paste = false;
            }, 50);
            stop_paste = true;
          }
        }, true)

        editor.events.on('beforepaste', _handlePaste);
      }
    }

    return {
      _init: _init
    }
  };


  $.extend($.FE.DEFAULTS, {
    shortcutsEnabled: ['show', 'bold', 'italic', 'underline', 'strikeThrough', 'indent', 'outdent', 'undo', 'redo'],
    shortcutsHint: true
  });

  $.FE.SHORTCUTS_MAP = {};

  $.FE.RegisterShortcut = function (key, cmd, val, letter, shift, option) {
    $.FE.SHORTCUTS_MAP[(shift ? '^' : '') + (option ? '@' : '') + key] = {
      cmd: cmd,
      val: val,
      letter: letter,
      shift: shift,
      option: option
    }

    $.FE.DEFAULTS.shortcutsEnabled.push(cmd);
  }

  $.FE.RegisterShortcut($.FE.KEYCODE.E, 'show', null, 'E', false, false);
  $.FE.RegisterShortcut($.FE.KEYCODE.B, 'bold', null, 'B', false, false);
  $.FE.RegisterShortcut($.FE.KEYCODE.I, 'italic', null, 'I', false, false);
  $.FE.RegisterShortcut($.FE.KEYCODE.U, 'underline', null, 'U', false, false);
  $.FE.RegisterShortcut($.FE.KEYCODE.S, 'strikeThrough', null, 'S', false, false);
  $.FE.RegisterShortcut($.FE.KEYCODE.CLOSE_SQUARE_BRACKET, 'indent', null, ']', false, false);
  $.FE.RegisterShortcut($.FE.KEYCODE.OPEN_SQUARE_BRACKET, 'outdent', null, '[', false, false);
  $.FE.RegisterShortcut($.FE.KEYCODE.Z, 'undo', null, 'Z', false, false);
  $.FE.RegisterShortcut($.FE.KEYCODE.Z, 'redo', null, 'Z', true, false);

  $.FE.MODULES.shortcuts = function (editor) {
    var inverse_map = null;

    function get (cmd) {
      if (!editor.opts.shortcutsHint) return null;

      if (!inverse_map) {
        inverse_map = {};
        for (var key in $.FE.SHORTCUTS_MAP) {
          if ($.FE.SHORTCUTS_MAP.hasOwnProperty(key) && editor.opts.shortcutsEnabled.indexOf($.FE.SHORTCUTS_MAP[key].cmd) >= 0) {
            inverse_map[$.FE.SHORTCUTS_MAP[key].cmd + '.' + ($.FE.SHORTCUTS_MAP[key].val || '')] = {
              shift: $.FE.SHORTCUTS_MAP[key].shift,
              option: $.FE.SHORTCUTS_MAP[key].option,
              letter: $.FE.SHORTCUTS_MAP[key].letter
            }
          }
        }
      }

      var srct = inverse_map[cmd];

      if (!srct) return null;
      return (editor.helpers.isMac() ? String.fromCharCode(8984) : 'Ctrl+') + (srct.shift ? (editor.helpers.isMac() ? String.fromCharCode(8679) : 'Shift+') : '') + (srct.option ? (editor.helpers.isMac() ? String.fromCharCode(8997) : 'Alt+') : '') + srct.letter;
    }

    /**
     * Execute shortcut.
     */
    function exec (e) {
      if (!editor.core.hasFocus()) return true;

      var keycode = e.which;

      var ctrlKey = navigator.userAgent.indexOf('Mac OS X') != -1 ? e.metaKey : e.ctrlKey;

      // Build shortcuts map.
      var map_key = (e.shiftKey ? '^' : '') + (e.altKey ? '@' : '') + keycode;
      if (ctrlKey && $.FE.SHORTCUTS_MAP[map_key]) {
        var cmd = $.FE.SHORTCUTS_MAP[map_key].cmd;

        // Check if shortcut is enabled.
        if (cmd && editor.opts.shortcutsEnabled.indexOf(cmd) >= 0) {
          var val = $.FE.SHORTCUTS_MAP[map_key].val;

          // Search for button.
          var $btn;
          if (cmd && !val) {
            $btn = editor.$tb.find('.fr-command[data-cmd="' + cmd + '"]');
          }
          else if (cmd && val) {
            $btn = editor.$tb.find('.fr-command[data-cmd="' + cmd + '"][data-param1="' + val + '"]');
          }

          // Button found.
          if ($btn.length) {
            e.preventDefault();
            e.stopPropagation();

            $btn.parents('.fr-toolbar').data('instance', editor);

            if (e.type == 'keydown') {
              editor.button.exec($btn);
            }

            return false;
          }

          // Search for command.
          else if (cmd && editor.commands[cmd]) {
            e.preventDefault();
            e.stopPropagation();

            if (e.type == 'keydown') {
              editor.commands[cmd]();
            }

            return false;
          }
        }
      }
    }

    /**
     * Initialize.
     */
    function _init () {
      editor.events.on('keydown', exec, true);
      editor.events.on('keyup', exec, true);
    }

    return {
      _init: _init,
      get: get
    }
  }


  $.FE.MODULES.snapshot = function (editor) {
    /**
     * Get the index of a node inside it's parent.
     */
    function _getNodeIndex (node) {
      var childNodes = node.parentNode.childNodes;
      var idx = 0;
      var prevNode = null;
      for (var i = 0; i < childNodes.length; i++) {
        if (prevNode) {
          // Current node is text and it is empty.
          var isEmptyText = (childNodes[i].nodeType === Node.TEXT_NODE && childNodes[i].textContent === '');

          // Previous node is text, current node is text.
          var twoTexts = (prevNode.nodeType === Node.TEXT_NODE && childNodes[i].nodeType === Node.TEXT_NODE);

          if (!isEmptyText && !twoTexts) idx++;
        }

        if (childNodes[i] == node) return idx;

        prevNode = childNodes[i];
      }
    }

    /**
     * Determine the location of the node inside the element.
     */
    function _getNodeLocation (node) {
      var loc = [];
      if (!node.parentNode) return [];
      while (!editor.node.isElement(node)) {
        loc.push(_getNodeIndex(node));
        node = node.parentNode;
      }

      return loc.reverse();
    }

    /**
     * Get the range offset inside the node.
     */
    function _getRealNodeOffset (node, offset) {
      while (node && node.nodeType === Node.TEXT_NODE) {
        var prevNode = node.previousSibling;
        if (prevNode && prevNode.nodeType == Node.TEXT_NODE) {
          offset += prevNode.textContent.length;
        }
        node = prevNode;
      }

      return offset;
    }

    /**
     * Codify each range.
     */
    function _getRange (range) {
      return {
        scLoc: _getNodeLocation(range.startContainer),
        scOffset: _getRealNodeOffset(range.startContainer, range.startOffset),
        ecLoc: _getNodeLocation(range.endContainer),
        ecOffset: _getRealNodeOffset(range.endContainer, range.endOffset)
      }
    }

    /**
     * Get the current snapshot.
     */
    function get () {
      var snapshot = {};

      editor.events.trigger('snapshot.before');

      snapshot.html = editor.$wp ? editor.$el.html() : editor.$oel.get(0).outerHTML;

      snapshot.ranges = [];
      if (editor.$wp && editor.selection.inEditor() && editor.core.hasFocus()) {
        var ranges = editor.selection.ranges();
        for (var i = 0; i < ranges.length; i++) {
          snapshot.ranges.push(_getRange(ranges[i]));
        }
      }

      editor.events.trigger('snapshot.after');

      return snapshot;
    }

    /**
     * Determine node by its location in the main element.
     */
    function _getNodeByLocation (loc) {
      var node = editor.$el.get(0);
      for (var i = 0; i < loc.length; i++) {
        node = node.childNodes[loc[i]];
      }

      return node;
    }

    /**
     * Restore range from snapshot.
     */
    function _restoreRange (sel, range_snapshot) {
      try {
        // Get range info.
        var startNode = _getNodeByLocation(range_snapshot.scLoc);
        var startOffset = range_snapshot.scOffset;
        var endNode = _getNodeByLocation(range_snapshot.ecLoc);
        var endOffset = range_snapshot.ecOffset;

        // Restore range.
        var range = editor.doc.createRange();
        range.setStart(startNode, startOffset);
        range.setEnd(endNode, endOffset);

        sel.addRange(range);
      }
      catch (ex) {
        console.warn (ex)
      }
    }

    /**
     * Restore snapshot.
     */
    function restore (snapshot) {
      // Restore HTML.
      if (editor.$el.html() != snapshot.html) editor.$el.html(snapshot.html);

      // Get selection.
      var sel = editor.selection.get();

      // Make sure to clear current selection.
      editor.selection.clear();

      // Focus.
      editor.events.focus(true);

      // Restore Ranges.
      for (var i = 0; i < snapshot.ranges.length; i++) {
        _restoreRange(sel, snapshot.ranges[i]);
      }
    }

    /**
     * Compare two snapshots.
     */
    function equal (s1, s2) {
      if (s1.html != s2.html) return false;
      if (editor.core.hasFocus() && JSON.stringify(s1.ranges) != JSON.stringify(s2.ranges)) return false;

      return true;
    }

    return {
      get: get,
      restore: restore,
      equal: equal
    }
  };


  $.FE.MODULES.undo = function (editor) {
    /**
     * Disable the default browser undo.
     */
    function _disableBrowserUndo (e) {
      var keyCode = e.which;
      var ctrlKey = editor.keys.ctrlKey(e);

      // Ctrl Key.
      if (ctrlKey) {
        if (keyCode == 90 && e.shiftKey) {
          e.preventDefault();
        }

        if (keyCode == 90) {
          e.preventDefault();
        }
      }
    }

    function canDo () {
      if (editor.undo_stack.length === 0 || editor.undo_index <= 1) {
        return false;
      }

      return true;
    }

    function canRedo () {
      if (editor.undo_index == editor.undo_stack.length) {
        return false;
      }

      return true;
    }

    var last_html = null;
    function saveStep (snapshot) {
      if (!editor.undo_stack || editor.undoing || editor.$el.get(0).querySelectorAll('.fr-marker').length) return false;

      if (typeof snapshot == 'undefined') {
        snapshot = editor.snapshot.get();

        if (!editor.undo_stack[editor.undo_index - 1] || !editor.snapshot.equal(editor.undo_stack[editor.undo_index - 1], snapshot)) {
          dropRedo();
          editor.undo_stack.push(snapshot);
          editor.undo_index++;

          if (snapshot.html != last_html) {
            editor.events.trigger('contentChanged');
            last_html = snapshot.html;
          }
        }
      }
      else {
        dropRedo();
        if (editor.undo_index > 0) {
          editor.undo_stack[editor.undo_index - 1] = snapshot;
        }
        else {
          editor.undo_stack.push(snapshot);
          editor.undo_index++;
        }
      }
    }

    function dropRedo () {
      if (!editor.undo_stack || editor.undoing) return false;

      while (editor.undo_stack.length > editor.undo_index) {
        editor.undo_stack.pop();
      }
    }

    function _do () {
      if (editor.undo_index > 1) {
        editor.undoing = true;

        // Get snapshot.
        var snapshot = editor.undo_stack[--editor.undo_index - 1];

        // Clear any existing content changed timers.
        clearTimeout(editor._content_changed_timer);

        // Restore snapshot.
        editor.snapshot.restore(snapshot);
        last_html = snapshot.html;

        // Hide popups.
        editor.popups.hideAll();

        // Enable toolbar.
        editor.toolbar.enable();

        // Call content changed.
        editor.events.trigger('contentChanged');

        editor.events.trigger('commands.undo');

        editor.undoing = false;
      }
    }

    function _redo () {
      if (editor.undo_index < editor.undo_stack.length) {
        editor.undoing = true;

        // Get snapshot.
        var snapshot = editor.undo_stack[editor.undo_index++];

        // Clear any existing content changed timers.
        clearTimeout(editor._content_changed_timer)

        // Restore snapshot.
        editor.snapshot.restore(snapshot);
        last_html = snapshot.html;

        // Hide popups.
        editor.popups.hideAll();

        // Enable toolbar.
        editor.toolbar.enable();

        // Call content changed.
        editor.events.trigger('contentChanged');

        editor.events.trigger('commands.redo');

        editor.undoing = false;
      }
    }

    function reset () {
      editor.undo_index = 0;
      editor.undo_stack = [];
    }

    function _destroy () {
      editor.undo_stack = [];
    }

    /**
     * Initialize
     */
    function _init () {
      reset();
      editor.events.on('initialized', function () {
        last_html = editor.html.get(false, true);
      });

      editor.events.on('blur', function () {
        editor.undo.saveStep();
      })

      editor.events.on('keydown', _disableBrowserUndo);

      editor.events.on('destroy', _destroy);
    }

    return {
      _init: _init,
      run: _do,
      redo: _redo,
      canDo: canDo,
      canRedo: canRedo,
      dropRedo: dropRedo,
      reset: reset,
      saveStep: saveStep
    }
  };


  $.FE.ICON_DEFAULT_TEMPLATE = 'font_awesome';

  $.FE.ICON_TEMPLATES = {
    font_awesome: '<i class="fa fa-[NAME]"></i>',
    text: '<span style="text-align: center;">[NAME]</span>',
    image: '<img src=[SRC] alt=[ALT] />'
  }

  $.FE.ICONS = {
    bold: {NAME: 'bold'},
    italic: {NAME: 'italic'},
    underline: {NAME: 'underline'},
    strikeThrough: {NAME: 'strikethrough'},
    subscript: {NAME: 'subscript'},
    superscript: {NAME: 'superscript'},
    color: {NAME: 'tint'},
    outdent: {NAME: 'outdent'},
    indent: {NAME: 'indent'},
    undo: {NAME: 'rotate-left'},
    redo: {NAME: 'rotate-right'},
    insertHR: {NAME: 'minus'},
    clearFormatting: {NAME: 'eraser'},
    selectAll: {NAME: 'mouse-pointer'}
  }

  $.FE.DefineIconTemplate = function (name, options) {
    $.FE.ICON_TEMPLATES[name] = options;
  }

  $.FE.DefineIcon = function (name, options) {
    $.FE.ICONS[name] = options;
  }

  $.FE.MODULES.icon = function (editor) {
    function create (command) {
      var icon = null;
      var info = $.FE.ICONS[command];
      if (typeof info != 'undefined') {
        var template = info.template || $.FE.ICON_DEFAULT_TEMPLATE;
        if (template && (template = $.FE.ICON_TEMPLATES[template])) {
          icon = template.replace(/\[([a-zA-Z]*)\]/g, function (str, a1) {
            return (a1 == 'NAME' ? (info[a1] || command) : info[a1]);
          });
        }
      }

      return (icon || command);
    }

    return {
      create: create
    }
  };


  $.FE.MODULES.tooltip = function (editor) {
    function hide () {
      // Position fixed for: https://github.com/froala/wysiwyg-editor/issues/1247.
      if (editor.$tooltip) editor.$tooltip.removeClass('fr-visible').css('left', '-3000px').css('position', 'fixed');
    }

    function to ($el, above) {
      if (!$el.data('title')) {
        $el.data('title', $el.attr('title'));
      }

      if (!$el.data('title')) return false;

      if (!editor.$tooltip) _init();

      $el.removeAttr('title');
      editor.$tooltip.text($el.data('title'));
      editor.$tooltip.addClass('fr-visible');

      var left = $el.offset().left + ($el.outerWidth() - editor.$tooltip.outerWidth()) / 2;

      // Normalize screen position.
      if (left < 0) left = 0;
      if (left + editor.$tooltip.outerWidth() > $(editor.o_win).width()) {
        left = $(editor.o_win).width() - editor.$tooltip.outerWidth();
      }

      if (typeof above == 'undefined') above = editor.opts.toolbarBottom;
      var top = !above ? $el.offset().top + $el.outerHeight() : $el.offset().top - editor.$tooltip.height();

      editor.$tooltip.css('position', '');
      editor.$tooltip.css('left', left);
      editor.$tooltip.css('top', top);

      if ($(editor.o_doc).find('body').css('position') != 'static') {
        editor.$tooltip.css('margin-left', - $(editor.o_doc).find('body').offset().left);
        editor.$tooltip.css('margin-top', - $(editor.o_doc).find('body').offset().top);
      }
      else {
        editor.$tooltip.css('margin-left', '');
        editor.$tooltip.css('margin-top', '');
      }
    }

    function bind ($el, selector, above) {
      if (!editor.helpers.isMobile()) {
        editor.events.$on($el, 'mouseenter', selector, function (e) {
          if (!$(e.currentTarget).hasClass('fr-disabled') && !editor.edit.isDisabled()) {
            to($(e.currentTarget), above);
          }
        }, true);

        editor.events.$on($el, 'mouseleave ' + editor._mousedown + ' ' + editor._mouseup, selector, function (e) {
          hide();
        }, true);
      }
    }

    function _init () {
      if (!editor.helpers.isMobile()) {
        if (!editor.shared.$tooltip) {
          editor.shared.$tooltip = $('<div class="fr-tooltip"></div>');

          editor.$tooltip = editor.shared.$tooltip;

          if (editor.opts.theme) {
            editor.$tooltip.addClass(editor.opts.theme + '-theme');
          }

          $(editor.o_doc).find('body').append(editor.$tooltip);
        }
        else {
          editor.$tooltip = editor.shared.$tooltip;
        }

        editor.events.on('shared.destroy', function () {
          editor.$tooltip.html('').removeData().remove();
          editor.$tooltip = null;
        }, true);
      }
    }

    return {
      hide: hide,
      to: to,
      bind: bind
    }
  };


  $.FE.MODULES.button = function (editor) {
    var buttons = [];

    if (editor.opts.toolbarInline || editor.opts.toolbarContainer) {
      if (!editor.shared.buttons) editor.shared.buttons = [];
      buttons = editor.shared.buttons;
    }

    var popup_buttons = [];
    if (!editor.shared.popup_buttons) editor.shared.popup_buttons = [];
    popup_buttons = editor.shared.popup_buttons;

    /**
     * Click was made on a dropdown button.
     */
    function _dropdownButtonClick (e) {
      // Get current btn and dropdown.
      var $btn = $(e.currentTarget);
      var $dropdown = $btn.next();

      var active = $btn.hasClass('fr-active');
      var mobile = editor.helpers.isMobile();

      var $active_dropdowns = $('.fr-dropdown.fr-active').not($btn);

      var inst = $btn.parents('.fr-toolbar, .fr-popup').data('instance') || editor;

      // Hide keyboard. We need the entire space.
      if (inst.helpers.isIOS() && inst.$el.get(0).querySelectorAll('.fr-marker').length == 0) {
        inst.selection.save();
        inst.selection.clear();
        inst.selection.restore();
      }

      // Dropdown is not active.
      if (!active) {
        // Call refresh on show.
        var cmd = $btn.data('cmd');
        $dropdown.find('.fr-command').removeClass('fr-active');
        if ($.FE.COMMANDS[cmd] && $.FE.COMMANDS[cmd].refreshOnShow) {
          $.FE.COMMANDS[cmd].refreshOnShow.apply(inst, [$btn, $dropdown]);
        }

        $dropdown.css('left', $btn.offset().left - $btn.parent().offset().left - (editor.opts.direction == 'rtl' ? $dropdown.width() - $btn.outerWidth() : 0));

        if (!editor.opts.toolbarBottom) {
          $dropdown.css('top', $btn.position().top + $btn.outerHeight());
        }
        else {
          $dropdown.css('bottom', editor.$tb.height() - $btn.position().top);
        }
      }

      // Blink and activate.
      $btn.addClass('fr-blink').toggleClass('fr-active');
      setTimeout (function () {
        $btn.removeClass('fr-blink');
      }, 300);

      // Check if it exceeds window on the right.
      if ($dropdown.offset().left + $dropdown.outerWidth() > $(editor.opts.scrollableContainer).offset().left +  $(editor.opts.scrollableContainer).outerWidth()) {
        $dropdown.css('margin-left', -($dropdown.offset().left + $dropdown.outerWidth() - $(editor.opts.scrollableContainer).offset().left - $(editor.opts.scrollableContainer).outerWidth()))
      }

      // Hide dropdowns that might be active.
      $active_dropdowns.removeClass('fr-active');
      $active_dropdowns.parent('.fr-toolbar:not(.fr-inline)').css('zIndex', '');

      if ($btn.parents('.fr-popup').length == 0 && !editor.opts.toolbarInline) {
        if ($btn.hasClass('fr-active')) {
          editor.$tb.css('zIndex', (editor.opts.zIndex || 1) + 4);
        }
        else {
          editor.$tb.css('zIndex', '');
        }
      }
    }

    function exec ($btn) {
      // Blink.
      $btn.addClass('fr-blink');
      setTimeout (function () {
        $btn.removeClass('fr-blink');
      }, 500);

      // Get command, value and additional params.
      var cmd = $btn.data('cmd');
      var params = [];
      while (typeof $btn.data('param' + (params.length + 1)) != 'undefined') {
        params.push($btn.data('param' + (params.length + 1)));
      }

      // Hide dropdowns that might be active including the current one.
      var $active_dropdowns = $('.fr-dropdown.fr-active');
      if ($active_dropdowns.length) {
        $active_dropdowns.removeClass('fr-active');

        $active_dropdowns.parent('.fr-toolbar:not(.fr-inline)').css('zIndex', '');
      }

      // Call the command.
      $btn.parents('.fr-popup, .fr-toolbar').data('instance').commands.exec(cmd, params);
    }

    /**
     * Click was made on a command button.
     */
    function _commandButtonClick (e) {
      var $btn = $(e.currentTarget);
      exec($btn);
    }

    function _click (e) {
      var $btn = $(e.currentTarget);

      var inst = $btn.parents('.fr-popup, .fr-toolbar').data('instance');

      if ($btn.parents('.fr-popup').length == 0 && !$btn.data('popup')) {
        inst.popups.hideAll();
      }

      // Popups are visible, but not in the current instance.
      if (inst.popups.areVisible() && !inst.popups.areVisible(inst)) {
        // Hide markers in other instances.
        for (var i = 0; i < $.FE.INSTANCES.length; i++) {
          if ($.FE.INSTANCES[i] != inst && $.FE.INSTANCES[i].popups && $.FE.INSTANCES[i].popups.areVisible()) {
            $.FE.INSTANCES[i].$el.find('.fr-marker').remove();
          }
        }

        inst.popups.hideAll();
      }

      // Dropdown button.
      if ($btn.hasClass('fr-dropdown')) {
        _dropdownButtonClick(e);
      }

      // Regular button.
      else {
        _commandButtonClick(e);

        if ($.FE.COMMANDS[$btn.data('cmd')] && $.FE.COMMANDS[$btn.data('cmd')].refreshAfterCallback != false) {
          inst.button.bulkRefresh();
        }
      }
    }

    function _hideActiveDropdowns ($el) {
      var $active_dropdowns = $el.find('.fr-dropdown.fr-active');

      if ($active_dropdowns.length) {
        $active_dropdowns.removeClass('fr-active');

        $active_dropdowns.parent('.fr-toolbar:not(.fr-inline)').css('zIndex', '');
      }
    }

    /**
     * Click in the dropdown menu.
     */
    function _dropdownMenuClick (e) {
      e.preventDefault();
      e.stopPropagation();
    }

    /**
     * Click on the dropdown wrapper.
     */
    function _dropdownWrapperClick (e) {
      e.stopPropagation();

      // Prevent blurring.
      if (!editor.helpers.isMobile()) {
        return false;
      }
    }

    /**
     * Bind callbacks for commands.
     */
    function bindCommands ($el, tooltipAbove) {
      editor.events.bindClick($el, '.fr-command:not(.fr-disabled)', _click);

      // Click on the dropdown menu.
      editor.events.$on($el, editor._mousedown + ' ' + editor._mouseup + ' ' + editor._move, '.fr-dropdown-menu', _dropdownMenuClick, true);

      // Click on the dropdown wrapper.
      editor.events.$on($el, editor._mousedown + ' ' + editor._mouseup + ' ' + editor._move, '.fr-dropdown-menu .fr-dropdown-wrapper', _dropdownWrapperClick, true);

      // Hide dropdowns that might be active.
      var _document = $el.get(0).ownerDocument;
      var _window = 'defaultView' in _document ? _document.defaultView : _document.parentWindow;
      var hideDropdowns = function (e) {
        if (!e || (e.type == editor._mouseup && e.target != $('html').get(0)) || (e.type == 'keydown' && ((editor.keys.isCharacter(e.which) && !editor.keys.ctrlKey(e)) || e.which == $.FE.KEYCODE.ESC))) {
          _hideActiveDropdowns($el);
        }
      }
      editor.events.$on($(_window), editor._mouseup + ' resize keydown', hideDropdowns, true);

      if (editor.opts.iframe) {
        editor.events.$on(editor.$win, editor._mouseup, hideDropdowns, true);
      }

      // Add refresh.
      if ($el.hasClass('fr-popup')) {
        $.merge(popup_buttons, $el.find('.fr-btn').toArray());
      }
      else {
        $.merge(buttons, $el.find('.fr-btn').toArray());
      }

      // Assing tooltips to buttons.
      editor.tooltip.bind($el, '.fr-btn, .fr-title', tooltipAbove);
    }

    /**
     * Create the content for dropdown.
     */
    function _content (command, info) {
      var c = '';

      if (info.html) {
        if (typeof info.html == 'function') {
          c += info.html.call(editor);
        }
        else {
          c += info.html;
        }
      }
      else {
        var options = info.options;
        if (typeof options == 'function') options = options();

        c += '<ul class="fr-dropdown-list">';
        for (var val in options) {
          if (options.hasOwnProperty(val)) {
            var shortcut = editor.shortcuts.get(command + '.' + val);
            if (shortcut) {
              shortcut = '<span class="fr-shortcut">' + shortcut + '</span>';
            }
            else {
              shortcut = '';
            }

            c += '<li><a class="fr-command" data-cmd="' + command + '" data-param1="' + val + '" title="' + options[val] + '">' + editor.language.translate(options[val]) + '</a></li>';
          }
        }
        c += '</ul>';
      }

      return c;
    }

    /**
     * Create button.
     */
    function _build (command, info, visible) {
      var display_selection = info.displaySelection;
      if (typeof display_selection == 'function') {
        display_selection = display_selection(editor);
      }

      var icon;
      if (display_selection) {
        var default_selection = (typeof info.defaultSelection == 'function' ? info.defaultSelection(editor) : info.defaultSelection);
        icon = '<span style="width:' + (info.displaySelectionWidth || 100) + 'px">' + (default_selection || editor.language.translate(info.title)) + '</span>';
      }
      else {
        icon = editor.icon.create(info.icon || command)
      }

      var popup = info.popup ? ' data-popup="true"' : '';

      var shortcut = editor.shortcuts.get(command + '.');
      if (shortcut) {
        shortcut = ' (' + shortcut + ')';
      }
      else {
        shortcut = '';
      }

      var btn = '<button type="button" tabindex="-1" aria-label="' + (editor.language.translate(info.title) || '') + '" title="' + (editor.language.translate(info.title) || '') + shortcut + '" class="fr-command fr-btn' + (info.type == 'dropdown' ? ' fr-dropdown' : '') + (info.displaySelection ? ' fr-selection' : '') + (info.back ? ' fr-back' : '') + (info.disabled ? ' fr-disabled' : '') + (!visible ? ' fr-hidden' : '') + '" data-cmd="' + command + '"' + popup + '>' + icon + '</button>';

      if (info.type == 'dropdown') {
        // Build dropdown.
        var dropdown = '<div class="fr-dropdown-menu"><div class="fr-dropdown-wrapper"><div class="fr-dropdown-content">';

        dropdown += _content(command, info);

        dropdown += '</div></div></div>';

        btn += dropdown;
      }

      return btn;
    }

    function buildList (buttons, visible_buttons) {
      var str = '';
      for (var i = 0; i < buttons.length; i++) {
        var cmd_name = buttons[i];
        var cmd_info = $.FE.COMMANDS[cmd_name];

        if (cmd_info && typeof cmd_info.plugin !== 'undefined' && editor.opts.pluginsEnabled.indexOf(cmd_info.plugin) < 0) continue;

        if (cmd_info) {
          var visible = typeof visible_buttons != 'undefined' ? visible_buttons.indexOf(cmd_name) >= 0 : true;
          str += _build(cmd_name, cmd_info, visible);
        }
        else if (cmd_name == '|') {
          str += '<div class="fr-separator fr-vs"></div>';
        }
        else if (cmd_name == '-') {
          str += '<div class="fr-separator fr-hs"></div>';
        }
      }

      return str;
    }

    function refresh ($btn) {
      var inst = $btn.parents('.fr-popup, .fr-toolbar').data('instance') || editor;

      var cmd = $btn.data('cmd');

      var $dropdown;
      if (!$btn.hasClass('fr-dropdown')) $btn.removeClass('fr-active');
      else $dropdown = $btn.next();

      if ($.FE.COMMANDS[cmd] && $.FE.COMMANDS[cmd].refresh) {
       $.FE.COMMANDS[cmd].refresh.apply(inst, [$btn, $dropdown]);
      }
      else if (editor.refresh[cmd]) {
        inst.refresh[cmd]($btn, $dropdown);
      }
    }

    function _bulkRefresh (btns) {
      var inst = editor.$tb ? (editor.$tb.data('instance') || editor) : editor;

      // Check the refresh event.
      if (editor.events.trigger('buttons.refresh') == false) return true;

      setTimeout(function () {
        var focused = (inst.selection.inEditor() && inst.core.hasFocus());

        for (var i = 0; i < btns.length; i++) {
          var $btn = $(btns[i]);
          var cmd = $btn.data('cmd');
          if ($btn.parents('.fr-popup').length == 0) {
            if (focused || ($.FE.COMMANDS[cmd] && $.FE.COMMANDS[cmd].forcedRefresh)) {
              inst.button.refresh($btn);
            }
            else {
              if (!$btn.hasClass('fr-dropdown')) $btn.removeClass('fr-active');
            }
          }
          else if ($btn.parents('.fr-popup').is(':visible')) {
            inst.button.refresh($btn);
          }
        }
      }, 0);
    }

    /**
     * Do buttons refresh.
     */
    function bulkRefresh () {
      _bulkRefresh(buttons);
      _bulkRefresh(popup_buttons);
    }

    function _destroy () {
      buttons = [];
      popup_buttons = [];
    }

    /**
     * Initialize.
     */
    function _init () {
      // Assign refresh and do refresh.
      if (editor.opts.toolbarInline) {
        editor.events.on('toolbar.show', bulkRefresh);
      }
      else {
        editor.events.on('mouseup', bulkRefresh);
        editor.events.on('keyup', bulkRefresh);
        editor.events.on('blur', bulkRefresh);
        editor.events.on('focus', bulkRefresh);
        editor.events.on('contentChanged', bulkRefresh);
      }

      editor.events.on('shared.destroy', _destroy);
    }

    return {
      _init: _init,
      buildList: buildList,
      bindCommands: bindCommands,
      refresh: refresh,
      bulkRefresh: bulkRefresh,
      exec: exec
    }
  };


  $.FE.POPUP_TEMPLATES = {
    'text.edit': '[_EDIT_]'
  };

  $.FE.RegisterTemplate = function (name, template) {
    $.FE.POPUP_TEMPLATES[name] = template;
  }

  $.FE.MODULES.popups = function (editor) {
    if (!editor.shared.popups) editor.shared.popups = {};
    var popups = editor.shared.popups;

    function setContainer(id, $container) {
      if (!$container.is(':visible')) $container = $(editor.opts.scrollableContainer);

      if (!$container.is(popups[id].data('container'))) {
        popups[id].data('container', $container);
        $container.append(popups[id]);
      }
    }

    /**
     * Show popup at a specific position.
     */
    function show (id, left, top, obj_height) {
      // Restore selection on show if it is there.
      if (areVisible() && editor.$el.find('.fr-marker').length > 0) {
        editor.events.disableBlur();
        editor.selection.restore();
      }

      hideAll([id]);

      if (!popups[id]) return false;

      // Hide active dropdowns.
      $('.fr-dropdown.fr-active').removeClass('fr-active').parent('.fr-toolbar').css('zIndex', '');

      // Set the current instance for the popup.
      popups[id].data('instance', editor);
      if (editor.$tb) editor.$tb.data('instance', editor);

      var width = popups[id].outerWidth();
      var height = popups[id].outerHeight();
      var is_visible = isVisible(id);
      popups[id].addClass('fr-active').removeClass('fr-hidden').find('input, textarea').removeAttr('disabled');

      var $container = popups[id].data('container');

      // If container is toolbar then increase zindex.
      if ($container.is(editor.$tb)) editor.$tb.css('zIndex', (editor.opts.zIndex || 1) + 4);

      // Inline mode when container is toolbar.
      if (editor.opts.toolbarInline && $container && editor.$tb && $container.get(0) == editor.$tb.get(0)) {
        setContainer(id, $(editor.opts.scrollableContainer));
        top = editor.$tb.offset().top - editor.helpers.getPX(editor.$tb.css('margin-top'));
        left = editor.$tb.offset().left + editor.$tb.outerWidth() / 2 + (parseFloat(editor.$tb.find('.fr-arrow').css('margin-left')) || 0) + editor.$tb.find('.fr-arrow').outerWidth() / 2;

        if (editor.$tb.hasClass('fr-above') && top) {
          top += editor.$tb.outerHeight();
        }

        obj_height = 0;
      }

      // Apply iframe correction.
      $container = popups[id].data('container');
      if (editor.opts.iframe && !obj_height && !is_visible) {
        if (left) left -= editor.$iframe.offset().left;
        if (top) top -= editor.$iframe.offset().top;
      }

      // Apply left correction.
      if (left) left = left - width / 2;

      // Toolbar at the bottom and container is toolbar.
      if (editor.opts.toolbarBottom && $container && editor.$tb && $container.get(0) == editor.$tb.get(0)) {
        popups[id].addClass('fr-above');
        if (top) top = top - popups[id].outerHeight();
      }

      // Position editor.
      popups[id].removeClass('fr-active');
      editor.position.at(left, top, popups[id], obj_height || 0);
      popups[id].addClass('fr-active');

      // Focus in the first field.
      var active_popup = popups[id].find('input:visible, textarea:visible').get(0);
      if (active_popup) {
        // Save selection if necessary.

        if (editor.$el.find('.fr-marker').length == 0 && editor.core.hasFocus()) {
          editor.selection.save();
        }

        editor.events.disableBlur();
        $(active_popup).select().focus();
      }

      if (editor.opts.toolbarInline) editor.toolbar.hide();

      editor.events.trigger('popups.show.' + id);

      // https://github.com/froala/wysiwyg-editor/issues/1248
      _events(id)._repositionPopup();

      _unmarkExit();
    }

    function onShow (id, callback) {
      editor.events.on('popups.show.' + id, callback);
    }

    /**
     * Find visible popup.
     */
    function isVisible (id) {
      return (popups[id] && popups[id].hasClass('fr-active') && editor.core.sameInstance(popups[id])) || false;
    }

    /**
     * Check if there is any popup visible.
     */
    function areVisible (new_instance) {
      for (var id in popups) {
        if (popups.hasOwnProperty(id)) {
          if (isVisible(id) && (typeof new_instance == 'undefined' || popups[id].data('instance') == new_instance)) return true;
        }
      }

      return false;
    }

    /**
     * Hide popup.
     */
    function hide (id) {
      if (popups[id] && popups[id].hasClass('fr-active')) {
        popups[id].removeClass('fr-active fr-above');
        editor.events.trigger('popups.hide.' + id);

        // Reset toolbar zIndex.
        if (editor.$tb) {
          if (editor.opts.zIndex > 1) {
            editor.$tb.css('zIndex', editor.opts.zIndex + 1);
          }
          else {
            editor.$tb.css('zIndex', '');
          }
        }

        editor.events.disableBlur();
        popups[id].find('input, textarea, button').filter(':focus').blur();
        popups[id].find('input, textarea').attr('disabled', 'disabled');
      }
    }

    /**
     * Assign an event for hiding.
     */
    function onHide (id, callback) {
      editor.events.on('popups.hide.' + id, callback);
    }

    /**
     * Get the popup with the specific id.
     */
    function get (id) {
      var $popup = popups[id];

      if ($popup && !$popup.data('inst' + editor.id)) {
        var ev = _events(id);
        _bindInstanceEvents(ev, id);
      }

      return $popup;
    }

    function onRefresh (id, callback) {
      editor.events.on('popups.refresh.' + id, callback);
    }

    /**
     * Refresh content inside the popup.
     */
    function refresh (id) {
      editor.events.trigger('popups.refresh.' + id);

      var btns = popups[id].find('.fr-command');
      for (var i = 0; i < btns.length; i++) {
        var $btn = $(btns[i]);
        if ($btn.parents('.fr-dropdown-menu').length == 0) {
          editor.button.refresh($btn);
        }
      }
    }

    /**
     * Hide all popups.
     */
    function hideAll (except) {
      if (typeof except == 'undefined') except = [];

      for (var id in popups) {
        if (popups.hasOwnProperty(id)) {
          if (except.indexOf(id) < 0) {
            hide(id);
          }
        }
      }
    }

    editor.shared.exit_flag = false;
    function _markExit () {
      editor.shared.exit_flag = true;
    }

    function _unmarkExit () {
      editor.shared.exit_flag = false;
    }

    function _canExit () {
      return editor.shared.exit_flag;
    }

    function _buildTemplate (id, template) {
      // Load template.
      var html = $.FE.POPUP_TEMPLATES[id];
      if (typeof html == 'function') html = html.apply(editor);

      for (var nm in template) {
        if (template.hasOwnProperty(nm)) {
          html = html.replace('[_' + nm.toUpperCase() + '_]', template[nm]);
        }
      }

      return html;
    }

    function _build (id, template) {
      var html = _buildTemplate(id, template);

      var $popup = $('<div class="fr-popup' + (editor.helpers.isMobile() ? ' fr-mobile' : ' fr-desktop') +  (editor.opts.toolbarInline ? ' fr-inline' : '') + '"><span class="fr-arrow"></span>' + html + '</div>');

      if (editor.opts.theme) {
        $popup.addClass(editor.opts.theme + '-theme');
      }

      if (editor.opts.zIndex > 1) {
        editor.$tb.css('z-index', editor.opts.zIndex + 2);
      }

      if (editor.opts.direction != 'auto') {
        $popup.removeClass('fr-ltr fr-rtl').addClass('fr-' + editor.opts.direction);
      }

      $popup.find('input, textarea').attr('dir', editor.opts.direction).attr('disabled', 'disabled');

      var $container = $('body');
      $container.append($popup);
      $popup.data('container', $container);

      popups[id] = $popup;

      // Bind commands from the popup.
      editor.button.bindCommands($popup, false);

      return $popup;
    }

    function _events (id) {
      var $popup = popups[id];

      return {
        /**
         * Resize window.
         */
        _windowResize: function () {
          var inst = $popup.data('instance') || editor;

          if (!inst.helpers.isMobile() && $popup.is(':visible')) {
            inst.events.disableBlur();
            inst.popups.hide(id);
            inst.events.enableBlur();
          }
        },

        /**
         * Focus on an input.
         */
        _inputFocus: function (e) {
          var inst = $popup.data('instance') || editor;

          e.preventDefault();

          // IE workaround.
          setTimeout(function () {
            inst.events.enableBlur();
          }, 0);

          // Reposition scroll on mobile to the original one.
          if (inst.helpers.isMobile()) {
            var t = $(inst.o_win).scrollTop();
            setTimeout(function () {
              $(inst.o_win).scrollTop(t);
            }, 0);
          }
        },

        /**
         * Blur on an input.
         */
        _inputBlur: function (e) {
          var inst = $popup.data('instance') || editor;

          // Do not do blur on window change.
          if (document.activeElement != this && $(this).is(':visible')) {
            if (inst.events.blurActive()) {
              inst.events.trigger('blur');
            }

            inst.events.enableBlur();
          }
        },

        /**
         * Keydown on an input.
         */
        _inputKeydown: function (e) {
          var inst = $popup.data('instance') || editor;
          var key_code = e.which;

          // Tabbing.
          if ($.FE.KEYCODE.TAB == key_code) {
            e.preventDefault();

            var inputs = $popup.find('input, textarea, button, select').filter(':visible').not(':disabled').toArray();
            inputs.sort(function (a, b) {
              if (e.shiftKey) return $(a).attr('tabIndex') < $(b).attr('tabIndex');
              return $(a).attr('tabIndex') > $(b).attr('tabIndex');
            });

            inst.events.disableBlur();
            var idx = inputs.indexOf(this) + 1;
            if (idx == inputs.length) idx = 0;
            $(inputs[idx]).focus();
          }

          // ENTER.
          else if ($.FE.KEYCODE.ENTER == key_code) {
            if ($popup.find('.fr-submit:visible').length > 0) {
              e.preventDefault();
              e.stopPropagation();
              inst.events.disableBlur();
              inst.button.exec($popup.find('.fr-submit:visible:first'));
            }
          }

          // ESC.
          else if ($.FE.KEYCODE.ESC == key_code) {
            e.preventDefault();
            e.stopPropagation();

            if (inst.$el.find('.fr-marker')) {
              inst.events.disableBlur();
              $(this).data('skip', true);
              inst.selection.restore();
              inst.events.enableBlur();
            }

            if (isVisible(id) && $popup.find('.fr-back:visible').length) {
              inst.button.exec($popup.find('.fr-back:visible:first'))
            }
            else {
              inst.popups.hide(id);
            }

            if (inst.opts.toolbarInline) inst.toolbar.showInline(null, true);

            return false;
          }

          // Other KEY. Stop propagation to the window.
          else {
            e.stopPropagation();
          }
        },

        /**
         * Window keydown.
         */
        _windowKeydown: function (e) {
          if (!editor.core.sameInstance($popup)) return true;

          var inst = $popup.data('instance') || editor;

          var key_code = e.which;

          // ESC.
          if ($.FE.KEYCODE.ESC == key_code) {
            if (isVisible(id) && inst.opts.toolbarInline) {
              e.stopPropagation();

              if (isVisible(id) && $popup.find('.fr-back:visible').length) {
                inst.button.exec($popup.find('.fr-back:visible:first'))
              }
              else {
                inst.popups.hide(id);
                inst.toolbar.showInline(null, true);
              }
              return false;
            }
            else {
              if (isVisible(id) && $popup.find('.fr-back:visible').length) {
                inst.button.exec($popup.find('.fr-back:visible:first'))
              }
              else {
                inst.popups.hide(id);
              }
            }
          }
        },

        /**
         * Editor keydown.
         */
        _editorKeydown: function (e) {
          var inst = $popup.data('instance') || editor;

          // ESC.
          if (!inst.keys.ctrlKey(e) && e.which != $.FE.KEYCODE.ESC) {
            if (isVisible(id) && $popup.find('.fr-back:visible').length) {
              inst.button.exec($popup.find('.fr-back:visible:first'))
            }
            else {
              inst.popups.hide(id);
            }
          }
        },

        /**
         * Handling hitting the popup elements with the mouse.
         */
        _preventFocus: function (e) {
          var inst = $popup.data('instance') || editor;
          inst.events.disableBlur();

          // Get the original target.
          var originalTarget = e.originalEvent ? (e.originalEvent.target || e.originalEvent.originalTarget) : null;

          // Define the input selector.
          var input_selector = 'input, textarea, button, select, label, .fr-command';

          // Click was not made inside an input.
          if (originalTarget && !$(originalTarget).is(input_selector) && $(originalTarget).parents(input_selector).length === 0) {
            e.stopPropagation();
            return false;
          }

          // Click was made on another input inside popup. Prevent propagation of the event.
          else if (originalTarget && $(originalTarget).is(input_selector)) {
            e.stopPropagation();
          }

          _unmarkExit();
        },

        /**
         * Mouseup inside the editor.
         */
        _editorMouseup: function (e) {
          // Check if popup is visible and we can exit.
          if ($popup.is(':visible') && _canExit()) {
            // If we have an input focused, then disable blur.
            if ($popup.find('input:focus, textarea:focus, button:focus, select:focus').filter(':visible').length > 0) {
              editor.events.disableBlur();
            }
          }
        },

        /**
         * Mouseup on window.
         */
        _windowMouseup: function (e) {
          if (!editor.core.sameInstance($popup)) return true;

          var inst = $popup.data('instance') || editor;

          if ($popup.is(':visible') && _canExit()) {
            e.stopPropagation();
            inst.markers.remove();
            inst.popups.hide(id);

            _unmarkExit();
          }
        },

        /**
         * Placeholder effect.
         */
        _doPlaceholder: function (e) {
          var $label = $(this).next();
          if ($label.length == 0) {
            $(this).after('<label>' + $(this).attr('placeholder') + '</label>');
          }

          $(this).toggleClass('fr-not-empty', $(this).val() != '');
        },

        /**
         * Reposition popup.
         */
        _repositionPopup: function (e) {
          // No height set or toolbar inline.
          if (!(editor.opts.height || editor.opts.heightMax) || editor.opts.toolbarInline) return true;

          if (editor.$wp && isVisible(id) && $popup.parent().get(0) == $(editor.opts.scrollableContainer).get(0)) {
            // Popup top - wrapper top.
            var p_top = $popup.offset().top - editor.$wp.offset().top;

            // Wrapper height.
            var w_height = editor.$wp.outerHeight();

            if ($popup.hasClass('fr-above')) p_top += $popup.outerHeight();

            // 1. Popup top > w_height.
            // 2. Popup top + popup height < 0.
            if (p_top > w_height || p_top < 0) {
              $popup.addClass('fr-hidden');
            }
            else {
              $popup.removeClass('fr-hidden');
            }
          }
        }
      }
    }

    function _bindInstanceEvents (ev, id) {
      // Editor mouseup.
      editor.events.on('mouseup', ev._editorMouseup, true);
      if (editor.$wp) editor.events.on('keydown', ev._editorKeydown);

      // Hide all popups on blur.
      editor.events.on('blur', function (e) {
        if (areVisible()) editor.markers.remove();

        hideAll();
      });

      // Update the position of the popup.
      if (editor.$wp && !editor.helpers.isMobile()) {
        editor.events.$on(editor.$wp, 'scroll.popup' + id, ev._repositionPopup);
      }

      editor.events.on('window.keydown', ev._windowKeydown);
      editor.events.on('window.mouseup', ev._windowMouseup, true);

      popups[id].data('inst' + editor.id, true);

      editor.events.on('destroy', function () {
        if (editor.core.sameInstance(popups[id])) {
          popups[id].removeClass('fr-active').appendTo('body');
        }
      }, true)
    }

    /**
     * Create a popup.
     */
    function create (id, template) {
      var $popup = _build(id, template);

      // Build events.
      var ev = _events(id);

      _bindInstanceEvents(ev, id);

      // Input Focus / Blur / Keydown.
      editor.events.$on($popup, 'mousedown mouseup touchstart touchend touch', '*', ev._preventFocus, true);
      editor.events.$on($popup, 'focus', 'input, textarea, button, select', ev._inputFocus, true);
      editor.events.$on($popup, 'blur', 'input, textarea, button, select', ev._inputBlur, true);
      editor.events.$on($popup, 'keydown', 'input, textarea, button, select', ev._inputKeydown, true);

      // Placeholder.
      editor.events.$on($popup, 'keydown keyup change input', 'input, textarea', ev._doPlaceholder, true);

      // Toggle checkbox.
      if (editor.helpers.isIOS()) {
        editor.events.$on($popup, 'touchend', 'label', function () {
          $('#' + $(this).attr('for')).prop('checked', function (i, val) {
            return !val;
          })
        }, true);
      }

      // Window mouseup.
      editor.events.$on($(editor.o_win), 'resize', ev._windowResize, true);

      return $popup;
    }

    /**
     * Destroy.
     */
    function _destroy () {
      for (var id in popups) {
        if (popups.hasOwnProperty(id)) {
          var $popup = popups[id];
          $popup.html('').removeData().remove();
          popups[id] = null;
        }
      }

      popups = [];
    }

    /**
     * Initialization.
     */
    function _init () {
      editor.events.on('shared.destroy', _destroy, true);

      editor.events.on('window.mousedown', _markExit);
      editor.events.on('window.touchmove', _unmarkExit);

      editor.events.on('mousedown', function (e) {
        if (areVisible()) {
          e.stopPropagation();

          // Remove markers.
          editor.$el.find('.fr-marker').remove();

          // Prepare for exit.
          _markExit();

          // Disable blur.
          editor.events.disableBlur();
        }
      })
    }

    return {
      _init: _init,
      create: create,
      get: get,
      show: show,
      hide: hide,
      onHide: onHide,
      hideAll: hideAll,
      setContainer: setContainer,
      refresh: refresh,
      onRefresh: onRefresh,
      onShow: onShow,
      isVisible: isVisible,
      areVisible: areVisible
    }
  };


  $.FE.MODULES.position = function (editor) {
    /**
    * Get bounding rect around selection.
    *
    */
    function getBoundingRect () {
      var boundingRect;

      var range = editor.selection.ranges(0);
      if (range && range.collapsed && editor.selection.inEditor()) {
        var remove = false;
        if (editor.$el.find('.fr-marker').length == 0) {
          editor.selection.save();
          remove = true;
        }

        var $marker = editor.$el.find('.fr-marker:first');
        $marker.css('display', 'inline');
        $marker.css('line-height', '');
        var offset = $marker.offset();
        var height = $marker.outerHeight();
        $marker.css('display', 'none');
        $marker.css('line-height', 0);

        boundingRect = {}
        boundingRect.left = offset.left;
        boundingRect.width = 0;
        boundingRect.height = height;
        boundingRect.top = offset.top - (editor.helpers.isIOS() ? 0 : $(editor.o_win).scrollTop());
        boundingRect.right = 1;
        boundingRect.bottom = 1;
        boundingRect.ok = true;

        if (remove) editor.selection.restore();
      }
      else if (range) {
        boundingRect = range.getBoundingClientRect();
      }

      return boundingRect;
    }

    /**
     * Normalize top positioning.
     */
    function _topNormalized ($el, top, obj_height) {
      var height = $el.outerHeight();

      if (!editor.helpers.isMobile() && editor.$tb && $el.parent().get(0) != editor.$tb.get(0)) {
        // 1. Parent offset + toolbar top + toolbar height > scrollableContainer height.
        // 2. Selection doesn't go above the screen.
        var p_height = $el.parent().height() - 20 - (editor.opts.toolbarBottom ? editor.$tb.outerHeight() : 0);
        var p_offset = $el.parent().offset().top;
        var new_top = top - height - (obj_height || 0);

        if ($el.parent().get(0) == $(editor.opts.scrollableContainer).get(0)) p_offset = p_offset - $el.parent().position().top;

        var s_height = $(editor.opts.scrollableContainer).get(0).scrollHeight;
        if (p_offset + top + height > $(editor.opts.scrollableContainer).offset().top + s_height && $el.parent().offset().top + new_top > 0) {
          top = new_top;
          $el.addClass('fr-above');
        }
        else {
          $el.removeClass('fr-above');
        }
      }

      return top;
    }

    /**
     * Normalize left position.
     */
    function _leftNormalized ($el, left) {
      var width = $el.outerWidth();

      // Normalize right.
      if (left + width > $(editor.opts.scrollableContainer).width() - 10) {
        left = $(editor.opts.scrollableContainer).width() - width - 10;
      }

      // Normalize left.
      if (left < 0) {
        left = 10;
      }

      return left;
    }

    /**
     * Place editor below selection.
     */
    function forSelection ($el) {
      var selection_rect = getBoundingRect();

      $el.css('top', 0).css('left', 0);

      var top = selection_rect.top + selection_rect.height;
      var left = selection_rect.left + selection_rect.width / 2 - $el.outerWidth() / 2 + $(editor.o_win).scrollLeft();

      if (!editor.opts.iframe) {
        top += $(editor.o_win).scrollTop();
      }

      at(left, top, $el, selection_rect.height);
    }

    /**
     * Position element at the specified position.
     */
    function at (left, top, $el, obj_height) {
      var $container = $el.data('container');

      if ($container && (!$container.is('body') || $container.css('position') != 'static')) {
        if (left) left -= $container.offset().left;
        if (top) top -= $container.offset().top;

        if ($container.get(0).tagName != 'BODY') {
          if (left) left += $container.scrollLeft();
          if (top) top += $container.scrollTop();
        }
        else if ($container.css('position') == 'absolute') {
          if (left) left += $container.position().left;
          if (top) top += $container.position().top;
        }
      }

      // Apply iframe correction.
      if (editor.opts.iframe && $container && editor.$tb && $container.get(0) != editor.$tb.get(0)) {
        if (left) left += editor.$iframe.offset().left;
        if (top) top += editor.$iframe.offset().top;
      }

      var new_left = _leftNormalized($el, left);

      if (left) {
        // Set the new left.
        $el.css('left', new_left);

        // Normalize arrow.
        var $arrow = $el.find('.fr-arrow');
        if (!$arrow.data('margin-left')) $arrow.data('margin-left', editor.helpers.getPX($arrow.css('margin-left')));
        $arrow.css('margin-left', left - new_left + $arrow.data('margin-left'));
      }

      if (top) $el.css('top', _topNormalized($el, top, obj_height));
    }

    /**
     * Special case for update sticky on iOS.
     */
    function _updateIOSSticky (el) {
      var $el = $(el);
      var is_on = $el.is('.fr-sticky-on');
      var prev_top = $el.data('sticky-top');
      var scheduled_top = $el.data('sticky-scheduled');

      // Create a dummy div that we show then sticky is on.
      if (typeof prev_top == 'undefined') {
        $el.data('sticky-top', 0);
        var $dummy = $('<div class="fr-sticky-dummy" style="height: ' + $el.outerHeight() + 'px;"></div>');
        editor.$box.prepend($dummy);
      }
      else {
        editor.$box.find('.fr-sticky-dummy').css('height', $el.outerHeight());
      }

      // Position sticky doesn't work when the keyboard is on the screen.
      if (editor.core.hasFocus() || editor.$tb.find('input:visible:focus').length > 0) {
        // Get the current scroll.
        var x_scroll = $(window).scrollTop();

        // Get the current top.
        // We make sure that we keep it within the editable box.
        var x_top = Math.min(Math.max(x_scroll - editor.$tb.parent().offset().top, 0), editor.$tb.parent().outerHeight() - $el.outerHeight());

        // Not the same top and different than the already scheduled.
        if (x_top != prev_top && x_top != scheduled_top) {
          // Clear any too soon change to avoid flickering.
          clearTimeout($el.data('sticky-timeout'));

          // Store the current scheduled top.
          $el.data('sticky-scheduled', x_top);

          // Hide the toolbar for a rich experience.
          if ($el.outerHeight() < x_scroll - editor.$tb.parent().offset().top) {
            $el.addClass('fr-opacity-0');
          }

          // Set the timeout for changing top.
          // Based on the test 100ms seems to be the best timeout.
          $el.data('sticky-timeout', setTimeout(function () {
            // Get the current top.
            var c_scroll = $(window).scrollTop();
            var c_top = Math.min(Math.max(c_scroll - editor.$tb.parent().offset().top, 0), editor.$tb.parent().outerHeight() - $el.outerHeight());

            if (c_top > 0 && editor.$tb.parent().get(0).tagName == 'BODY') c_top += editor.$tb.parent().position().top;

            // Don't update if it is not different than the prev top.
            if (c_top != prev_top) {
              $el.css('top', Math.max(c_top, 0));

              $el.data('sticky-top', c_top);
              $el.data('sticky-scheduled', c_top);
            }

            // Show toolbar.
            $el.removeClass('fr-opacity-0');
          }, 100));
        }

        // Turn on sticky mode.
        if (!is_on) {
          $el.css('top', '0');
          $el.width(editor.$tb.parent().width());
          $el.addClass('fr-sticky-on');
          editor.$box.addClass('fr-sticky-box');
        }
      }

      // Turn off sticky mode.
      else {
        clearTimeout($(el).css('sticky-timeout'));
        $el.css('top', '0');
        $el.css('position', '');
        $el.width('');
        $el.data('sticky-top', 0);
        $el.removeClass('fr-sticky-on');
        editor.$box.removeClass('fr-sticky-box');
      }
    }

    /**
     * Update sticky location for browsers that don't support sticky.
     * https://github.com/filamentgroup/fixed-sticky
     *
     * The MIT License (MIT)
     *
     * Copyright (c) 2013 Filament Group
     */
    function _updateSticky (el) {
      if( !el.offsetWidth ) { return; }

      var el_top;
      var el_bottom;
      var $el = $(el);
      var height = $el.outerHeight();

      var position = $el.data('sticky-position');

      // Viewport height.
      var viewport_height = $(editor.opts.scrollableContainer == 'body' ? editor.o_win : editor.opts.scrollableContainer).outerHeight();

      var scrollable_top = 0;
      var scrollable_bottom = 0;
      if (editor.opts.scrollableContainer !== 'body') {
        scrollable_top = $(editor.opts.scrollableContainer).offset().top;
        scrollable_bottom = $(editor.o_win).outerHeight() - scrollable_top - viewport_height;
      }

      var offset_top = editor.opts.scrollableContainer == 'body' ? $(editor.o_win).scrollTop() : scrollable_top;

      var is_on = $el.is('.fr-sticky-on');

      // Decide parent.
      if (!$el.data('sticky-parent')) {
        $el.data('sticky-parent', $el.parent());
      }
      var $parent = $el.data('sticky-parent');
      var parent_top = $parent.offset().top;
      var parent_height = $parent.outerHeight();


      if (!$el.data('sticky-offset')) {
        $el.data('sticky-offset', true);
        $el.after('<div class="fr-sticky-dummy" style="height: ' + height + 'px;"></div>');
      }

      // Detect position placement.
      if (!position) {
        // Some browsers require fixed/absolute to report accurate top/left values.
        var skip_setting_fixed = $el.css('top') !== 'auto' || $el.css('bottom') !== 'auto';

        // Set to position fixed for a split of second.
        if(!skip_setting_fixed) {
          $el.css('position', 'fixed');
        }

        // Find position.
        position = {
          top: $el.hasClass('fr-top'),
          bottom: $el.hasClass('fr-bottom')
        };

        // Remove position fixed.
        if(!skip_setting_fixed) {
          $el.css('position', '');
        }

        // Store position.
        $el.data('sticky-position', position);

        $el.data('top', $el.hasClass('fr-top') ? $el.css('top') : 'auto');
        $el.data('bottom', $el.hasClass('fr-bottom') ? $el.css('bottom') : 'auto');
      }

      // Detect if is OK to fix at the top.
      var isFixedToTop = function () {
        // 1. Top condition.
        // 2. Bottom condition.
        return parent_top <  offset_top + el_top &&
                parent_top + parent_height - height >= offset_top + el_top;
      }

      // Detect if it is OK to fix at the bottom.
      var isFixedToBottom = function () {
        return parent_top + height < offset_top + viewport_height - el_bottom &&
                parent_top + parent_height > offset_top + viewport_height - el_bottom ;
      }

      el_top = editor.helpers.getPX($el.data('top'));
      el_bottom = editor.helpers.getPX($el.data('bottom'));

      var at_top = (position.top && isFixedToTop());
      var at_bottom = (position.bottom && isFixedToBottom());

      // Should be fixed.
      if (at_top || at_bottom) {
        $el.css('width', $parent.width() + 'px');

        if (!is_on) {
          $el.addClass('fr-sticky-on')
          $el.removeClass('fr-sticky-off');

          if ($el.css('top')) {
            if ($el.data('top') != 'auto') {
              $el.css('top', editor.helpers.getPX($el.data('top')) + scrollable_top);
            }
            else {
              $el.data('top', 'auto');
            }
          }

          if ($el.css('bottom')) {
            if ($el.data('bottom') != 'auto') {
              $el.css('bottom', editor.helpers.getPX($el.data('bottom')) + scrollable_bottom);
            }
            else {
              $el.css('bottom', 'auto');
            }
          }
        }
      }

      // Shouldn't be fixed.
      else {
        if (!$el.hasClass('fr-sticky-off')) {
          // Reset.
          $el.width('');
          $el.removeClass('fr-sticky-on');
          $el.addClass('fr-sticky-off');

          if ($el.css('top') && $el.css('top') != 'auto') {
            $el.css('top', 0);
          }

          if ($el.css('bottom')) $el.css('bottom', 0);
        }
      }
    }

   /**
    * Test if browser supports sticky.
    * https://github.com/filamentgroup/fixed-sticky
    *
    * The MIT License (MIT)
    *
    * Copyright (c) 2013 Filament Group
    */
    function _testSticky () {
      var el = document.createElement('test');
      var mStyle = el.style;

      mStyle.cssText = 'position:' + [ '-webkit-', '-moz-', '-ms-', '-o-', '' ].join('sticky; position:') + ' sticky;';

      return mStyle['position'].indexOf('sticky') !== -1 && !editor.helpers.isIOS() && !editor.helpers.isAndroid();
    }

    /**
     * Initialize sticky position.
     */
    function _initSticky () {
      if (!_testSticky()) {
        editor._stickyElements = [];

        // iOS special case.
        if (editor.helpers.isIOS()) {
          // Use an animation frame to make sure we're always OK with the updates.
          var animate = function () {
            editor.helpers.requestAnimationFrame()(animate);

            for (var i = 0; i < editor._stickyElements.length; i++) {
              _updateIOSSticky(editor._stickyElements[i]);
            }
          };
          animate();

          // Hide toolbar on touchmove. This is very useful on iOS versions < 8.
          editor.events.$on($(editor.o_win), 'scroll', function () {
            if (editor.core.hasFocus()) {
              for (var i = 0; i < editor._stickyElements.length; i++) {
                var $el = $(editor._stickyElements[i]);
                var $parent = $el.parent();
                var c_scroll = $(window).scrollTop();

                if ($el.outerHeight() < c_scroll - $parent.offset().top) {
                  $el.addClass('fr-opacity-0');
                  $el.data('sticky-top', -1);
                  $el.data('sticky-scheduled', -1);
                }
              }
            }
          }, true);
        }

        // Default case. Do the updates on scroll.
        else {
          editor.events.$on($(editor.opts.scrollableContainer == 'body' ? editor.o_win : editor.opts.scrollableContainer), 'scroll', refresh, true);
          editor.events.$on($(editor.o_win), 'resize', refresh, true);

          editor.events.on('initialized', refresh);
          editor.events.on('focus', refresh);

          editor.events.$on($(editor.o_win), 'resize', 'textarea', refresh, true);
        }
      }

      editor.events.on('destroy', function (e) {
        editor._stickyElements = [];
      });
    }

    function refresh () {
      for (var i = 0; i < editor._stickyElements.length; i++) {
        _updateSticky(editor._stickyElements[i]);
      }
    }

    /**
     * Mark element as sticky.
     */
    function addSticky ($el) {
      $el.addClass('fr-sticky');

      if (editor.helpers.isIOS()) $el.addClass('fr-sticky-ios');

      if (!_testSticky()) {
        editor._stickyElements.push($el.get(0));
      }
    }

    function _init () {
      _initSticky();
    }

    return {
      _init: _init,
      forSelection: forSelection,
      addSticky: addSticky,
      refresh: refresh,
      at: at,
      getBoundingRect: getBoundingRect
    }
  };


  $.FE.MODULES.refresh = function (editor) {
    function undo ($btn) {
      $btn.toggleClass('fr-disabled', !editor.undo.canDo());
    }

    function redo ($btn) {
      $btn.toggleClass('fr-disabled', !editor.undo.canRedo());
    }

    function indent ($btn) {
      if ($btn.hasClass('fr-no-refresh')) return false;

      var blocks = editor.selection.blocks();
      for (var i = 0; i < blocks.length; i++) {
        var p_node = blocks[i].previousSibling;
        while (p_node && p_node.nodeType == Node.TEXT_NODE && p_node.textContent.length === 0) {
          p_node = p_node.previousSibling;
        }
        if (blocks[i].tagName == 'LI' && !p_node) {
          $btn.addClass('fr-disabled');
        }
        else {
          $btn.removeClass('fr-disabled');
          return true;
        }
      }
    }

    function outdent ($btn) {
      if ($btn.hasClass('fr-no-refresh')) return false;

      var blocks = editor.selection.blocks();
      for (var i = 0; i < blocks.length; i++) {
        var prop = (editor.opts.direction == 'rtl' || $(blocks[i]).css('direction') == 'rtl') ? 'margin-right' : 'margin-left';

        if (blocks[i].tagName == 'LI' || blocks[i].parentNode.tagName == 'LI') {
          $btn.removeClass('fr-disabled');
          return true;
        }

        if (editor.helpers.getPX($(blocks[i]).css(prop)) > 0) {
          $btn.removeClass('fr-disabled');
          return true;
        }
      }

      $btn.addClass('fr-disabled');
    }

    return {
      undo: undo,
      redo: redo,
      outdent: outdent,
      indent: indent
    }
  };


  $.extend($.FE.DEFAULTS, {
    editInPopup: false
  });

  $.FE.MODULES.textEdit = function (editor) {
    function _initPopup () {
      // Image buttons.
      var txt = '<div id="fr-text-edit-' + editor.id + '" class="fr-layer fr-text-edit-layer"><div class="fr-input-line"><input type="text" placeholder="' + editor.language.translate('Text') + '" tabIndex="1"></div><div class="fr-action-buttons"><button type="button" class="fr-command fr-submit" data-cmd="updateText" tabIndex="2">' + editor.language.translate('Update') + '</button></div></div>'

      var template = {
        edit: txt
      };

      var $popup = editor.popups.create('text.edit', template);
    }

    function _showPopup () {
      var $popup = editor.popups.get('text.edit');

      var text;
      if (editor.$el.prop('tagName') === 'INPUT') {
        text = editor.$el.attr('placeholder');
      }
      else {
        text = editor.$el.text();
      }

      $popup.find('input').val(text).trigger('change');
      editor.popups.setContainer('text.edit', $('body'));
      editor.popups.show('text.edit', editor.$el.offset().left + editor.$el.outerWidth() / 2, editor.$el.offset().top + editor.$el.outerHeight(), editor.$el.outerHeight());
    }

    function _initEvents () {
      // Show edit popup.
      editor.events.$on(editor.$el, editor._mouseup, function (e) {
        setTimeout (function () {
          _showPopup();
        }, 10);
      })
    }

    function update () {
      var $popup = editor.popups.get('text.edit');

      var new_text = $popup.find('input').val();

      if (new_text.length == 0) new_text = editor.opts.placeholderText;

      if (editor.$el.prop('tagName') === 'INPUT') {
        editor.$el.attr('placeholder', new_text);
      }
      else {
        editor.$el.text(new_text);
      }

      editor.events.trigger('contentChanged');

      editor.popups.hide('text.edit');
    }

    /**
     * Initialize.
     */
    function _init () {
      if (editor.opts.editInPopup) {
        _initPopup();
        _initEvents();
      }
    }

    return {
      _init: _init,
      update: update
    }
  };

  $.FE.RegisterCommand('updateText', {
    focus: false,
    undo: false,
    callback: function () {
      this.textEdit.update();
    }
  })


  // Extend defaults.
  $.extend($.FE.DEFAULTS, {
    toolbarBottom: false,
    toolbarButtons: ['fullscreen', 'bold', 'italic', 'underline', 'strikeThrough', 'subscript', 'superscript', 'fontFamily', 'fontSize', '|', 'color', 'emoticons', 'inlineStyle', 'paragraphStyle', '|', 'paragraphFormat', 'align', 'formatOL', 'formatUL', 'outdent', 'indent', 'quote', 'insertHR', '-', 'insertLink', 'insertImage', 'insertVideo', 'insertFile', 'insertTable', 'undo', 'redo', 'clearFormatting', 'selectAll', 'html', 'applyFormat', 'removeFormat'],
    toolbarButtonsXS: ['bold', 'italic', 'fontFamily', 'fontSize', '|', 'undo', 'redo'],
    toolbarButtonsSM: ['bold', 'italic', 'underline', '|', 'fontFamily', 'fontSize', 'insertLink', 'insertImage', 'table', '|', 'undo', 'redo'],
    toolbarButtonsMD: ['fullscreen', 'bold', 'italic', 'underline', 'fontFamily', 'fontSize', 'color', 'paragraphStyle', 'paragraphFormat', 'align', 'formatOL', 'formatUL', 'outdent', 'indent', 'quote', 'insertHR', '-', 'insertLink', 'insertImage', 'insertVideo', 'insertFile', 'insertTable', 'undo', 'redo', 'clearFormatting'],
    toolbarContainer: null,
    toolbarInline: false,
    toolbarSticky: true,
    toolbarStickyOffset: 0,
    toolbarVisibleWithoutSelection: false
  });

  $.FE.MODULES.toolbar = function (editor) {
    var _document, _window;

    // Create a button map for each screen size.
    var _buttons_map = [];
    _buttons_map[$.FE.XS] = editor.opts.toolbarButtonsXS || editor.opts.toolbarButtons;
    _buttons_map[$.FE.SM] = editor.opts.toolbarButtonsSM || editor.opts.toolbarButtons;
    _buttons_map[$.FE.MD] = editor.opts.toolbarButtonsMD || editor.opts.toolbarButtons;
    _buttons_map[$.FE.LG] = editor.opts.toolbarButtons;

    function _addOtherButtons (buttons, toolbarButtons) {
      for (var i = 0; i < toolbarButtons.length; i++) {
        if (toolbarButtons[i] != '-' && toolbarButtons[i] != '|' && buttons.indexOf(toolbarButtons[i]) < 0) {
          buttons.push(toolbarButtons[i]);
        }
      }
    }

    /**
     * Add buttons to the toolbar.
     */
    function _addButtons () {
      var _buttons = $.merge([], _screenButtons());
      _addOtherButtons(_buttons, editor.opts.toolbarButtonsXS || []);
      _addOtherButtons(_buttons, editor.opts.toolbarButtonsSM || []);
      _addOtherButtons(_buttons, editor.opts.toolbarButtonsMD || []);
      _addOtherButtons(_buttons, editor.opts.toolbarButtons);

      for (var i = _buttons.length - 1; i >= 0; i--) {
        if (_buttons[i] != '-' && _buttons[i] != '|' && _buttons.indexOf(_buttons[i]) < i) {
          _buttons.splice(i, 1);
        }
      }

      var buttons_list = editor.button.buildList(_buttons, _screenButtons());
      editor.$tb.append(buttons_list);
      editor.button.bindCommands(editor.$tb);
    }

    /**
     * The buttons that should be visible on the current screen size.
     */
    function _screenButtons () {
      var screen_size = editor.helpers.screenSize();
      return _buttons_map[screen_size];
    }

    function _showScreenButtons () {
      var c_buttons = _screenButtons();

      // Remove separator from toolbar.
      editor.$tb.find('.fr-separator').remove();

      // Hide all buttons.
      editor.$tb.find('> .fr-command').addClass('fr-hidden');

      // Reorder buttons.
      for (var i = 0; i < c_buttons.length; i++) {
        if (c_buttons[i] == '|' || c_buttons[i] == '-') {
          editor.$tb.append(editor.button.buildList([c_buttons[i]]));
        }
        else {
          var $btn = editor.$tb.find('> .fr-command[data-cmd="' + c_buttons[i] + '"]');
          var $dropdown = null;
          if ($btn.next().hasClass('fr-dropdown-menu')) $dropdown = $btn.next();

          $btn.removeClass('fr-hidden').appendTo(editor.$tb);
          if ($dropdown) $dropdown.appendTo(editor.$tb);
        }
      }
    }

    /**
     * Set the buttons visibility based on screen size.
     */
    function _setVisibility () {
      editor.events.$on($(editor.o_win), 'resize', _showScreenButtons, true);
      editor.events.$on($(editor.o_win), 'orientationchange', _showScreenButtons, true);
    }

    function showInline (e, force) {
      setTimeout(function () {
        if (e && e.which == $.FE.KEYCODE.ESC) {
          // Nothing.
        }
        else if (editor.selection.inEditor() && editor.core.hasFocus() && !editor.popups.areVisible()) {
          if ((editor.opts.toolbarVisibleWithoutSelection && e && e.type != 'keyup') || (!editor.selection.isCollapsed() && !editor.keys.isIME()) || force) {
            editor.$tb.data('instance', editor);

            // Check if we should actually show the toolbar.
            if (editor.events.trigger('toolbar.show', [e]) == false) return false;

            if (!editor.opts.toolbarContainer) {
              editor.position.forSelection(editor.$tb);
            }

            editor.$tb.show();
          }
        }
      }, 0);
    }

    function hide (e) {
      // Prevent hiding when dropdown is active and we scoll in it.
      // https://github.com/froala/wysiwyg-editor/issues/1290
      var $active_dropdowns = $('.fr-dropdown.fr-active');
      if ($active_dropdowns.next().find(editor.o_doc.activeElement).length) return true;

      // Check if we should actually hide the toolbar.
      if (editor.events.trigger('toolbar.hide') !== false) {
        editor.$tb.hide();
      }
    }

    function show () {
      // Check if we should actually hide the toolbar.
      if (editor.events.trigger('toolbar.show') == false) return false;

      editor.$tb.show();
    }

    /**
     * Set the events for show / hide toolbar.
     */
    function _initInlineBehavior () {
      // Window mousedown.
      editor.events.on('window.mousedown', hide);

      // Element keydown.
      editor.events.on('keydown', hide);

      // Element blur.
      editor.events.on('blur', hide);

      // Window mousedown.
      editor.events.on('window.mouseup', showInline);

      if (editor.helpers.isMobile()) {
        if (!editor.helpers.isIOS()) {
          editor.events.on('window.touchend', showInline);

          if (editor.browser.mozilla) {
            setInterval(showInline, 200);
          }
        }
      }
      else {
        editor.events.on('window.keyup', showInline);
      }

      // Hide editor on ESC.
      editor.events.on('keydown', function (e) {
        if (e && e.which == $.FE.KEYCODE.ESC) {
          hide();
        }
      });

      editor.events.$on(editor.$wp, 'scroll.toolbar', showInline);
      editor.events.on('commands.after', showInline);

      if (editor.helpers.isMobile()) {
        editor.events.$on(editor.$doc, 'selectionchange', showInline);
        editor.events.$on(editor.$doc, 'orientationchange', showInline);
      }
    }


    function _initPositioning () {
      // Toolbar is inline.
      if (editor.opts.toolbarInline) {
        // Mobile should handle this as regular.
        $(editor.opts.scrollableContainer).append(editor.$tb);

        // Add toolbar to body.
        editor.$tb.data('container', $(editor.opts.scrollableContainer));

        // Add inline class.
        editor.$tb.addClass('fr-inline');

        // Add arrow.
        editor.$tb.prepend('<span class="fr-arrow"></span>')

        // Init mouse behavior.
        _initInlineBehavior();

        editor.opts.toolbarBottom = false;
      }

      // Toolbar is normal.
      else {
        // Won't work on iOS.
        if (editor.opts.toolbarBottom && !editor.helpers.isIOS()) {
          editor.$box.append(editor.$tb);
          editor.$tb.addClass('fr-bottom');
          editor.$box.addClass('fr-bottom');
        }
        else {
          editor.opts.toolbarBottom = false;
          editor.$box.prepend(editor.$tb);
          editor.$tb.addClass('fr-top');
          editor.$box.addClass('fr-top');
        }

        editor.$tb.addClass('fr-basic');

        if (editor.opts.toolbarSticky) {
          if (editor.opts.toolbarStickyOffset) {
            if (editor.opts.toolbarBottom) {
              editor.$tb.css('bottom', editor.opts.toolbarStickyOffset);
            }
            else {
              editor.$tb.css('top', editor.opts.toolbarStickyOffset);
            }
          }

          editor.position.addSticky(editor.$tb);
        }
      }
    }

    /**
     * Destroy.
     */
    function _sharedDestroy () {
      editor.$tb.html('').removeData().remove();
      editor.$tb = null;
    }

    function _destroy () {
      editor.$box.removeClass('fr-top fr-bottom fr-inline fr-basic');
      editor.$box.find('.fr-sticky-dummy').remove();
    }

    function _setDefaults () {
      if (editor.opts.theme) {
        editor.$tb.addClass(editor.opts.theme + '-theme');
      }

      if (editor.opts.zIndex > 1) {
        editor.$tb.css('z-index', editor.opts.zIndex + 1);
      }

      // Set direction.
      if (editor.opts.direction != 'auto') {
        editor.$tb.removeClass('fr-ltr fr-rtl').addClass('fr-' + editor.opts.direction);
      }

      // Mark toolbar for desktop / mobile.
      if (!editor.helpers.isMobile()) {
        editor.$tb.addClass('fr-desktop');
      }
      else {
        editor.$tb.addClass('fr-mobile');
      }

      // Set the toolbar specific position inline / normal.
      if (!editor.opts.toolbarContainer) {
        _initPositioning();
      }
      else {
        if (editor.opts.toolbarInline) {
          _initInlineBehavior();
          hide();
        }

        if (editor.opts.toolbarBottom) editor.$tb.addClass('fr-bottom');
        else editor.$tb.addClass('fr-top');
      }

      // Set documetn and window for toolbar.
      _document = editor.$tb.get(0).ownerDocument;
      _window = 'defaultView' in _document ? _document.defaultView : _document.parentWindow;

      // Add buttons to the toolbar.
      // Set their visibility for different screens.
      // Asses commands to the butttons.
      _addButtons();
      _setVisibility();

      // Make sure we don't trigger blur.
      editor.events.$on(editor.$tb, editor._mousedown + ' ' + editor._mouseup, function (e) {
        var originalTarget = e.originalEvent ? (e.originalEvent.target || e.originalEvent.originalTarget) : null;
        if (originalTarget && originalTarget.tagName != 'INPUT' && !editor.edit.isDisabled()) {
          e.stopPropagation();
          e.preventDefault();
          return false;
        }
      }, true);
    }

    /**
     * Initialize
     */
    var tb_exists = false;
    function _init () {
      if (!editor.$wp) return false;

      // Container for toolbar.
      if (editor.opts.toolbarContainer) {
        // Shared toolbar.
        if (!editor.shared.$tb) {
          editor.shared.$tb = $('<div class="fr-toolbar"></div>');
          editor.$tb = editor.shared.$tb;
          $(editor.opts.toolbarContainer).append(editor.$tb);
          _setDefaults();
          editor.$tb.data('instance', editor);
        }
        else {
          editor.$tb = editor.shared.$tb;

          if (editor.opts.toolbarInline) _initInlineBehavior();
        }

        if (editor.opts.toolbarInline) {
          // Update box.
          editor.$box.addClass('fr-inline');
        }
        else {
          editor.$box.addClass('fr-basic');
        }

        // On focus set the current instance.
        editor.events.on('focus', function () {
          editor.$tb.data('instance', editor);
        }, true);

        editor.opts.toolbarInline = false;
      }
      else {
        // Inline toolbar.
        if (editor.opts.toolbarInline) {
          // Update box.
          editor.$box.addClass('fr-inline');

          // Check for shared toolbar.
          if (!editor.shared.$tb) {
            editor.shared.$tb = $('<div class="fr-toolbar"></div>');
            editor.$tb = editor.shared.$tb;
            _setDefaults();
          }
          else {
            editor.$tb = editor.shared.$tb;

            // Init mouse behavior.
            _initInlineBehavior();
          }
        }
        else {
          editor.$box.addClass('fr-basic');
          editor.$tb = $('<div class="fr-toolbar"></div>');
          _setDefaults();

          editor.$tb.data('instance', editor);
        }
      }

      // Destroy.
      editor.events.on('destroy', _destroy, true);
      editor.events.on(!editor.opts.toolbarInline ? 'destroy' : 'shared.destroy', _sharedDestroy, true);
    }

    var disabled = false;
    function disable () {
      if (!disabled && editor.$tb) {
        editor.$tb.find('> .fr-command').addClass('fr-disabled fr-no-refresh');
        disabled = true;
      }
    }

    function enable () {
      if (disabled && editor.$tb) {
        editor.$tb.find('> .fr-command').removeClass('fr-disabled fr-no-refresh');
        disabled = false;
      }

      editor.button.bulkRefresh();
    }

    return {
      _init: _init,
      hide: hide,
      show: show,
      showInline: showInline,
      disable: disable,
      enable: enable
    }
  };



}));

};


modules['helpcenter/primary/data/passwords'] = function(require, exports, module, modules) {
var passwords = ['password', '123456', '12345678', '1234', 'qwerty', '12345', 'dragon', 'pussy', 'baseball', 'football', 'letmein', 'monkey', '696969', 'abc123', 'mustang', 'michael', 'shadow', 'master', 'jennifer', '111111', '2000', 'jordan', 'superman', 'harley', '1234567', 'fuckme', 'hunter', 'fuckyou', 'trustno1', 'ranger', 'buster', 'thomas', 'tigger', 'robert', 'soccer', 'fuck', 'batman', 'test', 'pass', 'killer', 'hockey', 'george', 'charlie', 'andrew', 'michelle', 'love', 'sunshine', 'jessica', 'asshole', '6969', 'pepper', 'daniel', 'access', '123456789', '654321', 'joshua', 'maggie', 'starwars', 'silver', 'william', 'dallas', 'yankees', '123123', 'ashley', '666666', 'hello', 'amanda', 'orange', 'biteme', 'freedom', 'computer', 'sexy', 'thunder', 'nicole', 'ginger', 'heather', 'hammer', 'summer', 'corvette', 'taylor', 'fucker', 'austin', '1111', 'merlin', 'matthew', '121212', 'golfer', 'cheese', 'princess', 'martin', 'chelsea', 'patrick', 'richard', 'diamond', 'yellow', 'bigdog', 'secret', 'asdfgh', 'sparky', 'cowboy', 'camaro', 'anthony', 'matrix', 'falcon', 'iloveyou', 'bailey', 'guitar', 'jackson', 'purple', 'scooter', 'phoenix', 'aaaaaa', 'morgan', 'tigers', 'porsche', 'mickey', 'maverick', 'cookie', 'nascar', 'peanut', 'justin', '131313', 'money', 'horny', 'samantha', 'panties', 'steelers', 'joseph', 'snoopy', 'boomer', 'whatever', 'iceman', 'smokey', 'gateway', 'dakota', 'cowboys', 'eagles', 'chicken', 'dick', 'black', 'zxcvbn', 'please', 'andrea', 'ferrari', 'knight', 'hardcore', 'melissa', 'compaq', 'coffee', 'booboo', 'bitch', 'johnny', 'bulldog', 'xxxxxx', 'welcome', 'james', 'player', 'ncc1701', 'wizard', 'scooby', 'charles', 'junior', 'internet', 'bigdick', 'mike', 'brandy', 'tennis', 'blowjob', 'banana', 'monster', 'spider', 'lakers', 'miller', 'rabbit', 'enter', 'mercedes', 'brandon', 'steven', 'fender', 'john', 'yamaha', 'diablo', 'chris', 'boston', 'tiger', 'marine', 'chicago', 'rangers', 'gandalf', 'winter', 'bigtits', 'barney', 'edward', 'raiders', 'porn', 'badboy', 'blowme', 'spanky', 'bigdaddy', 'johnson', 'chester', 'london', 'midnight', 'blue', 'fishing', '000000', 'hannah', 'slayer', '11111111', 'rachel', 'sexsex', 'redsox', 'thx1138', 'asdf', 'marlboro', 'panther', 'zxcvbnm', 'arsenal', 'oliver', 'qazwsx', 'mother', 'victoria', '7777777', 'jasper', 'angel', 'david', 'winner', 'crystal', 'golden', 'butthead', 'viking', 'jack', 'iwantu', 'shannon', 'murphy', 'angels', 'prince', 'cameron', 'girls', 'madison', 'wilson', 'carlos', 'hooters', 'willie', 'startrek', 'captain', 'maddog', 'jasmine', 'butter', 'booger', 'angela', 'golf', 'lauren', 'rocket', 'tiffany', 'theman', 'dennis', 'liverpoo', 'flower', 'forever', 'green', 'jackie', 'muffin', 'turtle', 'sophie', 'danielle', 'redskins', 'toyota', 'jason', 'sierra', 'winston', 'debbie', 'giants', 'packers', 'newyork', 'jeremy', 'casper', 'bubba', '112233', 'sandra', 'lovers', 'mountain', 'united', 'cooper', 'driver', 'tucker', 'helpme', 'fucking', 'pookie', 'lucky', 'maxwell', '8675309', 'bear', 'suckit', 'gators', '5150', '222222', 'shithead', 'fuckoff', 'jaguar', 'monica', 'fred', 'happy', 'hotdog', 'tits', 'gemini', 'lover', 'xxxxxxxx', '777777', 'canada', 'nathan', 'victor', 'florida', '88888888', 'nicholas', 'rosebud', 'metallic', 'doctor', 'trouble', 'success', 'stupid', 'tomcat', 'warrior', 'peaches', 'apples', 'fish', 'qwertyui', 'magic', 'buddy', 'dolphins', 'rainbow', 'gunner', '987654', 'freddy', 'alexis', 'braves', 'cock', '2112', '1212', 'cocacola', 'xavier', 'dolphin', 'testing', 'bond007', 'member', 'calvin', 'voodoo', '7777', 'samson', 'alex', 'apollo', 'fire', 'tester', 'walter', 'beavis', 'voyager', 'peter', 'porno', 'bonnie', 'rush2112', 'beer', 'apple', 'scorpio', 'jonathan', 'skippy', 'sydney', 'scott', 'red123', 'power', 'gordon', 'travis', 'beaver', 'star', 'jackass', 'flyers', 'boobs', '232323', 'zzzzzz', 'steve', 'rebecca', 'scorpion', 'doggie', 'legend', 'ou812', 'yankee', 'blazer', 'bill', 'runner', 'birdie', 'bitches', '555555', 'parker', 'topgun', 'asdfasdf', 'heaven', 'viper', 'animal', '2222', 'bigboy', '4444', 'arthur', 'baby', 'private', 'godzilla', 'donald', 'williams', 'lifehack', 'phantom', 'dave', 'rock', 'august', 'sammy', 'cool', 'brian', 'platinum', 'jake', 'bronco', 'paul', 'mark', 'frank', 'heka6w2', 'copper', 'billy', 'cumshot', 'garfield', 'willow', 'cunt', 'little', 'carter', 'slut', 'albert', '69696969', 'kitten', 'super', 'jordan23', 'eagle1', 'shelby', 'america', '11111', 'jessie', 'house', 'free', '123321', 'chevy', 'bullshit', 'white', 'broncos', 'horney', 'surfer', 'nissan', '999999', 'saturn', 'airborne', 'elephant', 'marvin', 'shit', 'action', 'adidas', 'qwert', 'kevin', '1313', 'explorer', 'walker', 'police', 'christin', 'december', 'benjamin', 'wolf', 'sweet', 'therock', 'king', 'online', 'dickhead', 'brooklyn', 'teresa', 'cricket', 'sharon', 'dexter', 'racing', 'penis', 'gregory', '0000', 'teens', 'redwings', 'dreams', 'michigan', 'hentai', 'magnum', '87654321', 'nothing', 'donkey', 'trinity', 'digital', '333333', 'stella', 'cartman', 'guinness', '123abc', 'speedy', 'buffalo', 'kitty', 'pimpin', 'eagle', 'einstein', 'kelly', 'nelson', 'nirvana', 'vampire', 'xxxx', 'playboy', 'louise', 'pumpkin', 'snowball', 'test123', 'girl', 'sucker', 'mexico', 'beatles', 'fantasy', 'ford', 'gibson', 'celtic', 'marcus', 'cherry', 'cassie', '888888', 'natasha', 'sniper', 'chance', 'genesis', 'hotrod', 'reddog', 'alexande', 'college', 'jester', 'passw0rd', 'bigcock', 'smith', 'lasvegas', 'carmen', 'slipknot', '3333', 'death', 'kimberly', '1q2w3e', 'eclipse', '1q2w3e4r', 'stanley', 'samuel', 'drummer', 'homer', 'montana', 'music', 'aaaa', 'spencer', 'jimmy', 'carolina', 'colorado', 'creative', 'hello1', 'rocky', 'goober', 'friday', 'bollocks', 'scotty', 'abcdef', 'bubbles', 'hawaii', 'fluffy', 'mine', 'stephen', 'horses', 'thumper', '5555', 'pussies', 'darkness', 'asdfghjk', 'pamela', 'boobies', 'buddha', 'vanessa', 'sandman', 'naughty', 'douglas', 'honda', 'matt', 'azerty', '6666', 'shorty', 'money1', 'beach', 'loveme', '4321', 'simple', 'poohbear', '444444', 'badass', 'destiny', 'sarah', 'denise', 'vikings', 'lizard', 'melanie', 'assman', 'sabrina', 'nintendo', 'water', 'good', 'howard', 'time', '123qwe', 'november', 'xxxxx', 'october', 'leather', 'bastard', 'young', '101010', 'extreme', 'hard', 'password1', 'vincent', 'pussy1', 'lacrosse', 'hotmail', 'spooky', 'amateur', 'alaska', 'badger', 'paradise', 'maryjane', 'poop', 'crazy', 'mozart', 'video', 'russell', 'vagina', 'spitfire', 'anderson', 'norman', 'eric', 'cherokee', 'cougar', 'barbara', 'long', '420420', 'family', 'horse', 'enigma', 'allison', 'raider', 'brazil', 'blonde', 'jones', '55555', 'dude', 'drowssap', 'jeff', 'school', 'marshall', 'lovely', '1qaz2wsx', 'jeffrey', 'caroline', 'franklin', 'booty', 'molly', 'snickers', 'leslie', 'nipples', 'courtney', 'diesel', 'rocks', 'eminem', 'westside', 'suzuki', 'daddy', 'passion', 'hummer', 'ladies', 'zachary', 'frankie', 'elvis', 'reggie', 'alpha', 'suckme', 'simpson', 'patricia', '147147', 'pirate', 'tommy', 'semperfi', 'jupiter', 'redrum', 'freeuser', 'wanker', 'stinky', 'ducati', 'paris', 'natalie', 'babygirl', 'bishop', 'windows', 'spirit', 'pantera', 'monday', 'patches', 'brutus', 'houston', 'smooth', 'penguin', 'marley', 'forest', 'cream', '212121', 'flash', 'maximus', 'nipple', 'bobby', 'bradley', 'vision', 'pokemon', 'champion', 'fireman', 'indian', 'softball', 'picard', 'system', 'clinton', 'cobra', 'enjoy', 'lucky1', 'claire', 'claudia', 'boogie', 'timothy', 'marines', 'security', 'dirty', 'admin', 'wildcats', 'pimp', 'dancer', 'hardon', 'veronica', 'fucked', 'abcd1234', 'abcdefg', 'ironman', 'wolverin', 'remember', 'great', 'freepass', 'bigred', 'squirt', 'justice', 'francis', 'hobbes', 'kermit', 'pearljam', 'mercury', 'domino', '9999', 'denver', 'brooke', 'rascal', 'hitman', 'mistress', 'simon', 'tony', 'bbbbbb', 'friend', 'peekaboo', 'naked', 'budlight', 'electric', 'sluts', 'stargate', 'saints', 'bondage', 'brittany', 'bigman', 'zombie', 'swimming', 'duke', 'qwerty1', 'babes', 'scotland', 'disney', 'rooster', 'brenda', 'mookie', 'swordfis', 'candy', 'duncan', 'olivia', 'hunting', 'blink182', 'alicia', '8888', 'samsung', 'bubba1', 'whore', 'virginia', 'general', 'passport', 'aaaaaaaa', 'erotic', 'liberty', 'arizona', 'jesus', 'abcd', 'newport', 'skipper', 'rolltide', 'balls', 'happy1', 'galore', 'christ', 'weasel', '242424', 'wombat', 'digger', 'classic', 'bulldogs', 'poopoo', 'accord', 'popcorn', 'turkey', 'jenny', 'amber', 'bunny', 'mouse', '007007', 'titanic', 'liverpool', 'dreamer', 'everton', 'friends', 'chevelle', 'carrie', 'gabriel', 'psycho', 'nemesis', 'burton', 'pontiac', 'connor', 'eatme', 'lickme', 'roland', 'cumming', 'mitchell', 'ireland', 'lincoln', 'arnold', 'spiderma', 'patriots', 'goblue', 'devils', 'eugene', 'empire', 'asdfg', 'cardinal', 'brown', 'shaggy', 'froggy', 'qwer', 'kawasaki', 'kodiak', 'people', 'phpbb', 'light', '54321', 'kramer', 'chopper', 'hooker', 'honey', 'whynot', 'lesbian', 'lisa', 'baxter', 'adam', 'snake', 'teen', 'ncc1701d', 'qqqqqq', 'airplane', 'britney', 'avalon', 'sandy', 'sugar', 'sublime', 'stewart', 'wildcat', 'raven', 'scarface', 'elizabet', '123654', 'trucks', 'wolfpack', 'pervert', 'lawrence', 'raymond', 'redhead', 'american', 'alyssa', 'bambam', 'movie', 'woody', 'shaved', 'snowman', 'tiger1', 'chicks', 'raptor', '1969', 'stingray', 'shooter', 'france', 'stars', 'madmax', 'kristen', 'sports', 'jerry', '789456', 'garcia', 'simpsons', 'lights', 'ryan', 'looking', 'chronic', 'alison', 'hahaha', 'packard', 'hendrix', 'perfect', 'service', 'spring', 'srinivas', 'spike', 'katie', '252525', 'oscar', 'brother', 'bigmac', 'suck', 'single', 'cannon', 'georgia', 'popeye', 'tattoo', 'texas', 'party', 'bullet', 'taurus', 'sailor', 'wolves', 'panthers', 'japan', 'strike', 'flowers', 'pussycat', 'chris1', 'loverboy', 'berlin', 'sticky', 'marina', 'tarheels', 'fisher', 'russia', 'connie', 'wolfgang', 'testtest', 'mature', 'bass', 'catch22', 'juice', 'michael1', 'nigger', '159753', 'women', 'alpha1', 'trooper', 'hawkeye', 'head', 'freaky', 'dodgers', 'pakistan', 'machine', 'pyramid', 'vegeta', 'katana', 'moose', 'tinker', 'coyote', 'infinity', 'inside', 'pepsi', 'letmein1', 'bang', 'control', 'hercules', 'morris', 'james1', 'tickle', 'outlaw', 'browns', 'billybob', 'pickle', 'test1', 'michele', 'antonio', 'sucks', 'pavilion', 'changeme', 'caesar', 'prelude', 'tanner', 'adrian', 'darkside', 'bowling', 'wutang', 'sunset', 'robbie', 'alabama', 'danger', 'zeppelin', 'juan', 'rusty', 'pppppp', 'nick', '2001', 'ping', 'darkstar', 'madonna', 'qwe123', 'bigone', 'casino', 'cheryl', 'charlie1', 'mmmmmm', 'integra', 'wrangler', 'apache', 'tweety', 'qwerty12', 'bobafett', 'simone', 'none', 'business', 'sterling', 'trevor', 'transam', 'dustin', 'harvey', 'england', '2323', 'seattle', 'ssssss', 'rose', 'harry', 'openup', 'pandora', 'pussys', 'trucker', 'wallace', 'indigo', 'storm', 'malibu', 'weed', 'review', 'babydoll', 'doggy', 'dilbert', 'pegasus', 'joker', 'catfish', 'flipper', 'valerie', 'herman', 'fuckit', 'detroit', 'kenneth', 'cheyenne', 'bruins', 'stacey', 'smoke', 'joey', 'seven', 'marino', 'fetish', 'xfiles', 'wonder', 'stinger', 'pizza', 'babe', 'pretty', 'stealth', 'manutd', 'gracie', 'gundam', 'cessna', 'longhorn', 'presario', 'mnbvcxz', 'wicked', 'mustang1', 'victory', '21122112', 'shelly', 'awesome', 'athena', 'q1w2e3r4', 'help', 'holiday', 'knicks', 'street', 'redneck', '12341234', 'casey', 'gizmo', 'scully', 'dragon1', 'devildog', 'triumph', 'eddie', 'bluebird', 'shotgun', 'peewee', 'ronnie', 'angel1', 'daisy', 'special', 'metallica', 'madman', 'country', 'impala', 'lennon', 'roscoe', 'omega', 'access14', 'enterpri', 'miranda', 'search', 'smitty', 'blizzard', 'unicorn', 'tight', 'rick', 'ronald', 'asdf1234', 'harrison', 'trigger', 'truck', 'danny', 'home', 'winnie', 'beauty', 'thailand', '1234567890', 'cadillac', 'castle', 'tyler', 'bobcat', 'buddy1', 'sunny', 'stones', 'asian', 'freddie', 'chuck', 'butt', 'loveyou', 'norton', 'hellfire', 'hotsex', 'indiana', 'short', 'panzer', 'lonewolf', 'trumpet', 'colors', 'blaster', '12121212', 'fireball', 'logan', 'precious', 'aaron', 'elaine', 'jungle', 'atlanta', 'gold', 'corona', 'curtis', 'nikki', 'polaris', 'timber', 'theone', 'baller', 'chipper', 'orlando', 'island', 'skyline', 'dragons', 'dogs', 'benson', 'licker', 'goldie', 'engineer', 'kong', 'pencil', 'basketba', 'open', 'hornet', 'world', 'linda', 'barbie', 'chan', 'farmer', 'valentin', 'wetpussy', 'indians', 'larry', 'redman', 'foobar', 'travel', 'morpheus', 'bernie', 'target', '141414', 'hotstuff', 'photos', 'laura', 'savage', 'holly', 'rocky1', 'fuck_inside', 'dollar', 'turbo', 'design', 'newton', 'hottie', 'moon', '202020', 'blondes', '4128', 'lestat', 'avatar', 'future', 'goforit', 'random', 'abgrtyu', 'jjjjjj', 'cancer', 'q1w2e3', 'smiley', 'goldberg', 'express', 'virgin', 'zipper', 'wrinkle1', 'stone', 'andy', 'babylon', 'dong', 'powers', 'consumer', 'dudley', 'monkey1', 'serenity', 'samurai', '99999999', 'bigboobs', 'skeeter', 'lindsay', 'joejoe', 'master1', 'aaaaa', 'chocolat', 'christia', 'birthday', 'stephani', 'tang', '1234qwer', 'alfred', 'ball', '98765432', 'maria', 'sexual', 'maxima', '77777777', 'sampson', 'buckeye', 'highland', 'kristin', 'seminole', 'reaper', 'bassman', 'nugget', 'lucifer', 'airforce', 'nasty', 'watson', 'warlock', '2121', 'philip', 'always', 'dodge', 'chrissy', 'burger', 'bird', 'snatch', 'missy', 'pink', 'gang', 'maddie', 'holmes', 'huskers', 'piglet', 'photo', 'joanne', 'hamilton', 'dodger', 'paladin', 'christy', 'chubby', 'buckeyes', 'hamlet', 'abcdefgh', 'bigfoot', 'sunday', 'manson', 'goldfish', 'garden', 'deftones', 'icecream', 'blondie', 'spartan', 'julie', 'harold', 'charger', 'brandi', 'stormy', 'sherry', 'pleasure', 'juventus', 'rodney', 'galaxy', 'holland', 'escort', 'zxcvb', 'planet', 'jerome', 'wesley', 'blues', 'song', 'peace', 'david1', 'ncc1701e', '1966', '51505150', 'cavalier', 'gambit', 'karen', 'sidney', 'ripper', 'oicu812', 'jamie', 'sister', 'marie', 'martha', 'nylons', 'aardvark', 'nadine', 'minnie', 'whiskey', 'bing', 'plastic', 'anal', 'babylon5', 'chang', 'savannah', 'loser', 'racecar', 'insane', 'yankees1', 'mememe', 'hansolo', 'chiefs', 'fredfred', 'freak', 'frog', 'salmon', 'concrete', 'yvonne', 'zxcv', 'shamrock', 'atlantis', 'warren', 'wordpass', 'julian', 'mariah', 'rommel', '1010', 'harris', 'predator', 'sylvia', 'massive', 'cats', 'sammy1', 'mister', 'stud', 'marathon', 'rubber', 'ding', 'trunks', 'desire', 'montreal', 'justme', 'faster', 'kathleen', 'irish', '1999', 'bertha', 'jessica1', 'alpine', 'sammie', 'diamonds', 'tristan', '00000', 'swinger', 'shan', 'stallion', 'pitbull', 'letmein2', 'roberto', 'ready', 'april', 'palmer', 'ming', 'shadow1', 'audrey', 'chong', 'clitoris', 'wang', 'shirley', 'fuckers', 'jackoff', 'bluesky', 'sundance', 'renegade', 'hollywoo', '151515', 'bernard', 'wolfman', 'soldier', 'picture', 'pierre', 'ling', 'goddess', 'manager', 'nikita', 'sweety', 'titans', 'hang', 'fang', 'ficken', 'niners', 'bottom', 'bubble', 'hello123', 'ibanez', 'webster', 'sweetpea', 'stocking', '323232', 'tornado', 'lindsey', 'content', 'bruce', 'buck', 'aragorn', 'griffin', 'chen', 'campbell', 'trojan', 'christop', 'newman', 'wayne', 'tina', 'rockstar', 'father', 'geronimo', 'pascal', 'crimson', 'brooks', 'hector', 'penny', 'anna', 'google', 'camera', 'chandler', 'fatcat', 'lovelove', 'cody', 'cunts', 'waters', 'stimpy', 'finger', 'cindy', 'wheels', 'viper1', 'latin', 'robin', 'greenday', '987654321', 'creampie', 'brendan', 'hiphop', 'willy', 'snapper', 'funtime', 'duck', 'trombone', 'adult', 'cotton', 'cookies', 'kaiser', 'mulder', 'westham', 'latino', 'jeep', 'ravens', 'aurora', 'drizzt', 'madness', 'energy', 'kinky', '314159', 'sophia', 'stefan', 'slick', 'rocker', '55555555', 'freeman', 'french', 'mongoose', 'speed', 'dddddd', 'hong', 'henry', 'hungry', 'yang', 'catdog', 'cheng', 'ghost', 'gogogo', 'randy', 'tottenha', 'curious', 'butterfl', 'mission', 'january', 'singer', 'sherman', 'shark', 'techno', 'lancer', 'lalala', 'autumn', 'chichi', 'orion', 'trixie', 'clifford', 'delta', 'bobbob', 'bomber', 'holden', 'kang', 'kiss', '1968', 'spunky', 'liquid', 'mary', 'beagle', 'granny', 'network', 'bond', 'kkkkkk', 'millie', '1973', 'biggie', 'beetle', 'teacher', 'susan', 'toronto', 'anakin', 'genius', 'dream', 'cocks', 'dang', 'bush', 'karate', 'snakes', 'bangkok', 'callie', 'fuckyou2', 'pacific', 'daytona', 'kelsey', 'infantry', 'skywalke', 'foster', 'felix', 'sailing', 'raistlin', 'vanhalen', 'huang', 'herbert', 'jacob', 'blackie', 'tarzan', 'strider', 'sherlock', 'lang', 'gong', 'sang', 'dietcoke', 'ultimate', 'tree', 'shai', 'sprite', 'ting', 'artist', 'chai', 'chao', 'devil', 'python', 'ninja', 'misty', 'ytrewq', 'sweetie', 'superfly', '456789', 'tian', 'jing', 'jesus1', 'freedom1', 'dian', 'drpepper', 'potter', 'chou', 'darren', 'hobbit', 'violet', 'yong', 'shen', 'phillip', 'maurice', 'gloria', 'nolimit', 'mylove', 'biscuit', 'yahoo', 'shasta', 'sex4me', 'smoker', 'smile', 'pebbles', 'pics', 'philly', 'tong', 'tintin', 'lesbians', 'marlin', 'cactus', 'frank1', 'tttttt', 'chun', 'danni', 'emerald', 'showme', 'pirates', 'lian', 'dogg', 'colleen', 'xiao', 'xian', 'tazman', 'tanker', 'patton', 'toshiba', 'richie', 'alberto', 'gotcha', 'graham', 'dillon', 'rang', 'emily', 'keng', 'jazz', 'bigguy', 'yuan', 'woman', 'tomtom', 'marion', 'greg', 'chaos', 'fossil', 'flight', 'racerx', 'tuan', 'creamy', 'boss', 'bobo', 'musicman', 'warcraft', 'window', 'blade', 'shuang', 'sheila', 'shun', 'lick', 'jian', 'microsoft', 'rong', 'allen', 'feng', 'getsome', 'sally', 'quality', 'kennedy', 'morrison', '1977', 'beng', 'wwwwww', 'yoyoyo', 'zhang', 'seng', 'teddy', 'joanna', 'andreas', 'harder', 'luke', 'qazxsw', 'qian', 'cong', 'chuan', 'deng', 'nang', 'boeing', 'keeper', 'western', 'isabelle', '1963', 'subaru', 'sheng', 'thuglife', 'teng', 'jiong', 'miao', 'martina', 'mang', 'maniac', 'pussie', 'tracey', 'a1b2c3', 'clayton', 'zhou', 'zhuang', 'xing', 'stonecol', 'snow', 'spyder', 'liang', 'jiang', 'memphis', 'regina', 'ceng', 'magic1', 'logitech', 'chuang', 'dark', 'million', 'blow', 'sesame', 'shao', 'poison', 'titty', 'terry', 'kuan', 'kuai', 'kyle', 'mian', 'guan', 'hamster', 'guai', 'ferret', 'florence', 'geng', 'duan', 'pang', 'maiden', 'quan', 'velvet', 'nong', 'neng', 'nookie', 'buttons', 'bian', 'bingo', 'biao', 'zhong', 'zeng', 'xiong', 'zhun', 'ying', 'zong', 'xuan', 'zang', '0.0.000', 'suan', 'shei', 'shui', 'sharks', 'shang', 'shua', 'small', 'peng', 'pian', 'piao', 'liao', 'meng', 'miami', 'reng', 'guang', 'cang', 'change', 'ruan', 'diao', 'luan', 'lucas', 'qing', 'chui', 'chuo', 'cuan', 'nuan', 'ning', 'heng', 'huan', 'kansas', 'muscle', 'monroe', 'weng', 'whitney', '1passwor', 'bluemoon', 'zhui', 'zhua', 'xiang', 'zheng', 'zhen', 'zhei', 'zhao', 'zhan', 'yomama', 'zhai', 'zhuo', 'zuan', 'tarheel', 'shou', 'shuo', 'tiao', 'lady', 'leonard', 'leng', 'kuang', 'jiao', '13579', 'basket', 'qiao', 'qiong', 'qiang', 'chuai', 'nian', 'niao', 'niang', 'huai', '22222222', 'bianca', 'zhuan', 'zhuai', 'shuan', 'shuai', 'stardust', 'jumper', 'margaret', 'archie', '66666666', 'charlott', 'forget', 'qwertz', 'bones', 'history', 'milton', 'waterloo', '2002', 'stuff', '11223344', 'office', 'oldman', 'preston', 'trains', 'murray', 'vertigo', '246810', 'black1', 'swallow', 'smiles', 'standard', 'alexandr', 'parrot', 'luther', 'user', 'nicolas', '1976', 'surfing', 'pioneer', 'pete', 'masters', 'apple1', 'asdasd', 'auburn', 'hannibal', 'frontier', 'panama', 'lucy', 'buffy', 'brianna', 'welcome1', 'vette', 'blue22', 'shemale', '111222', 'baggins', 'groovy', 'global', 'turner', '181818', '1979', 'blades', 'spanking', 'life', 'byteme', 'lobster', 'collins', 'dawg', 'hilton', 'japanese', '1970', '1964', '2424', 'polo', 'markus', 'coco', 'deedee', 'mikey', '1972', '171717', '1701', 'strip', 'jersey', 'green1', 'capital', 'sasha', 'sadie', 'putter', 'vader', 'seven7', 'lester', 'marcel', 'banshee', 'grendel', 'gilbert', 'dicks', 'dead', 'hidden', 'iloveu', '1980', 'sound', 'ledzep', 'michel', '147258', 'female', 'bugger', 'buffett', 'bryan', 'hell', 'kristina', 'molson', '2020', 'wookie', 'sprint', 'thanks', 'jericho', '102030', 'grace', 'fuckin', 'mandy', 'ranger1', 'trebor', 'deepthroat', 'bonehead', 'molly1', 'mirage', 'models', '1984', '2468', 'stuart', 'showtime', 'squirrel', 'pentium', 'mario', 'anime', 'gator', 'powder', 'twister', 'connect', 'neptune', 'bruno', 'butts', 'engine', 'eatshit', 'mustangs', 'woody1', 'shogun', 'septembe', 'pooh', 'jimbo', 'roger', 'annie', 'bacon', 'center', 'russian', 'sabine', 'damien', 'mollie', 'voyeur', '2525', '363636', 'leonardo', 'camel', 'chair', 'germany', 'giant', 'qqqq', 'nudist', 'bone', 'sleepy', 'tequila', 'megan', 'fighter', 'garrett', 'dominic', 'obiwan', 'makaveli', 'vacation', 'walnut', '1974', 'ladybug', 'cantona', 'ccbill', 'satan', 'rusty1', 'passwor1', 'columbia', 'napoleon', 'dusty', 'kissme', 'motorola', 'william1', '1967', 'zzzz', 'skater', 'smut', 'play', 'matthew1', 'robinson', 'valley', 'coolio', 'dagger', 'boner', 'bull', 'horndog', 'jason1', 'blake', 'penguins', 'rescue', 'griffey', '8j4ye3uz', 'californ', 'champs', 'qwertyuiop', 'portland', 'queen', 'colt45', 'boat', 'xxxxxxx', 'xanadu', 'tacoma', 'mason', 'carpet', 'gggggg', 'safety', 'palace', 'italia', 'stevie', 'picturs', 'picasso', 'thongs', 'tempest', 'ricardo', 'roberts', 'asd123', 'hairy', 'foxtrot', 'gary', 'nimrod', 'hotboy', '343434', '1111111', 'asdfghjkl', 'goose', 'overlord', 'blood', 'wood', 'stranger', '454545', 'shaolin', 'sooners', 'socrates', 'spiderman', 'peanuts', 'maxine', 'rogers', '13131313', 'andrew1', 'filthy', 'donnie', 'ohyeah', 'africa', 'national', 'kenny', 'keith', 'monique', 'intrepid', 'jasmin', 'pickles', 'assass', 'fright', 'potato', 'darwin', 'hhhhhh', 'kingdom', 'weezer', '424242', 'pepsi1', 'throat', 'romeo', 'gerard', 'looker', 'puppy', 'butch', 'monika', 'suzanne', 'sweets', 'temple', 'laurie', 'josh', 'megadeth', 'analsex', 'nymets', 'ddddddd', 'bigballs', 'support', 'stick', 'today', 'down', 'oakland', 'oooooo', 'qweasd', 'chucky', 'bridge', 'carrot', 'chargers', 'discover', 'dookie', 'condor', 'night', 'butler', 'hoover', 'horny1', 'isabella', 'sunrise', 'sinner', 'jojo', 'megapass', 'martini', 'assfuck', 'grateful', 'ffffff', 'abigail', 'esther', 'mushroom', 'janice', 'jamaica', 'wright', 'sims', 'space', 'there', 'timmy', '7654321', '77777', 'cccccc', 'gizmodo', 'roxanne', 'ralph', 'tractor', 'cristina', 'dance', 'mypass', 'hongkong', 'helena', '1975', 'blue123', 'pissing', 'thomas1', 'redred', 'rich', 'basketball', 'attack', 'cash', 'satan666', 'drunk', 'dixie', 'dublin', 'bollox', 'kingkong', 'katrina', 'miles', '1971', '22222', '272727', 'sexx', 'penelope', 'thompson', 'anything', 'bbbb', 'battle', 'grizzly', 'passat', 'porter', 'tracy', 'defiant', 'bowler', 'knickers', 'monitor', 'wisdom', 'wild', 'slappy', 'thor', 'letsgo', 'robert1', 'feet', 'rush', 'brownie', 'hudson', '098765', 'playing', 'playtime', 'lightnin', 'melvin', 'atomic', 'bart', 'hawk', 'goku', 'glory', 'llllll', 'qwaszx', 'cosmos', 'bosco', 'knights', 'bentley', 'beast', 'slapshot', 'lewis', 'assword', 'frosty', 'gillian', 'sara', 'dumbass', 'mallard', 'dddd', 'deanna', 'elwood', 'wally', '159357', 'titleist', 'angelo', 'aussie', 'guest', 'golfing', 'doobie', 'loveit', 'chloe', 'elliott', 'werewolf', 'vipers', 'janine', '1965', 'blabla', 'surf', 'sucking', 'tardis', 'serena', 'shelley', 'thegame', 'legion', 'rebels', 'fernando', 'fast', 'gerald', 'sarah1', 'double', 'onelove', 'loulou', 'toto', 'crash', 'blackcat', '0007', 'tacobell', 'soccer1', 'jedi', 'manuel', 'method', 'river', 'chase', 'ludwig', 'poopie', 'derrick', 'boob', 'breast', 'kittycat', 'isabel', 'belly', 'pikachu', 'thunder1', 'thankyou', 'jose', 'celeste', 'celtics', 'frances', 'frogger', 'scoobydo', 'sabbath', 'coltrane', 'budman', 'willis', 'jackal', 'bigger', 'zzzzz', 'silvia', 'sooner', 'licking', 'gopher', 'geheim', 'lonestar', 'primus', 'pooper', 'newpass', 'brasil', 'heather1', 'husker', 'element', 'moomoo', 'beefcake', 'zzzzzzzz', 'tammy', 'shitty', 'smokin', 'personal', 'jjjj', 'anthony1', 'anubis', 'backup', 'gorilla', 'fuckface', 'painter', 'lowrider', 'punkrock', 'traffic', 'claude', 'daniela', 'dale', 'delta1', 'nancy', 'boys', 'easy', 'kissing', 'kelley', 'wendy', 'theresa', 'amazon', 'alan', 'fatass', 'dodgeram', 'dingdong', 'malcolm', 'qqqqqqqq', 'breasts', 'boots', 'honda1', 'spidey', 'poker', 'temp', 'johnjohn', 'miguel', '147852', 'archer', 'asshole1', 'dogdog', 'tricky', 'crusader', 'weather', 'syracuse', 'spankme', 'speaker', 'meridian', 'amadeus', 'back', 'harley1', 'falcons', 'dorothy', 'turkey50', 'kenwood', 'keyboard', 'ilovesex', '1978', 'blackman', 'shazam', 'shalom', 'lickit', 'jimbob', 'richmond', 'roller', 'carson', 'check', 'fatman', 'funny', 'garbage', 'sandiego', 'loving', 'magnus', 'cooldude', 'clover', 'mobile', 'bell', 'payton', 'plumber', 'texas1', 'tool', 'topper', 'jenna', 'mariners', 'rebel', 'harmony', 'caliente', 'celica', 'fletcher', 'german', 'diana', 'oxford', 'osiris', 'orgasm', 'punkin', 'porsche9', 'tuesday', 'close', 'breeze', 'bossman', 'kangaroo', 'billie', 'latinas', 'judith', 'astros', 'scruffy', 'donna', 'qwertyu', 'davis', 'hearts', 'kathy', 'jammer', 'java', 'springer', 'rhonda', 'ricky', '1122', 'goodtime', 'chelsea1', 'freckles', 'flyboy', 'doodle', 'city', 'nebraska', 'bootie', 'kicker', 'webmaster', 'vulcan', 'iverson', '191919', 'blueeyes', 'stoner', '321321', 'farside', 'rugby', 'director', 'pussy69', 'power1', 'bobbie', 'hershey', 'hermes', 'monopoly', 'west', 'birdman', 'blessed', 'blackjac', 'southern', 'peterpan', 'thumbs', 'lawyer', 'melinda', 'fingers', 'fuckyou1', 'rrrrrr', 'a1b2c3d4', 'coke', 'nicola', 'bohica', 'heart', 'elvis1', 'kids', 'blacky', 'stories', 'sentinel', 'snake1', 'phoebe', 'jesse', 'richard1', '1234abcd', 'guardian', 'candyman', 'fisting', 'scarlet', 'dildo', 'pancho', 'mandingo', 'lucky7', 'condom', 'munchkin', 'billyboy', 'summer1', 'student', 'sword', 'skiing', 'sergio', 'site', 'sony', 'thong', 'rootbeer', 'assassin', 'cassidy', 'frederic', 'fffff', 'fitness', 'giovanni', 'scarlett', 'durango', 'postal', 'achilles', 'dawn', 'dylan', 'kisses', 'warriors', 'imagine', 'plymouth', 'topdog', 'asterix', 'hallo', 'cameltoe', 'fuckfuck', 'bridget', 'eeeeee', 'mouth', 'weird', 'will', 'sithlord', 'sommer', 'toby', 'theking', 'juliet', 'avenger', 'backdoor', 'goodbye', 'chevrole', 'faith', 'lorraine', 'trance', 'cosworth', 'brad', 'houses', 'homers', 'eternity', 'kingpin', 'verbatim', 'incubus', '1961', 'blond', 'zaphod', 'shiloh', 'spurs', 'station', 'jennie', 'maynard', 'mighty', 'aliens', 'hank', 'charly', 'running', 'dogman', 'omega1', 'printer', 'aggies', 'chocolate', 'deadhead', 'hope', 'javier', 'bitch1', 'stone55', 'pineappl', 'thekid', 'lizzie', 'rockets', 'ashton', 'camels', 'formula', 'forrest', 'rosemary', 'oracle', 'rain', 'pussey', 'porkchop', 'abcde', 'clancy', 'nellie', 'mystic', 'inferno', 'blackdog', 'steve1', 'pauline', 'alexander', 'alice', 'alfa', 'grumpy', 'flames', 'scream', 'lonely', 'puffy', 'proxy', 'valhalla', 'unreal', 'cynthia', 'herbie', 'engage', 'yyyyyy', '010101', 'solomon', 'pistol', 'melody', 'celeb', 'flying', 'gggg', 'santiago', 'scottie', 'oakley', 'portugal', 'a12345', 'newbie', 'mmmm', 'venus', '1qazxsw2', 'beverly', 'zorro', 'work', 'writer', 'stripper', 'sebastia', 'spread', 'phil', 'tobias', 'links', 'members', 'metal', '1221', 'andre', '565656', 'funfun', 'trojans', 'again', 'cyber', 'hurrican', 'moneys', '1x2zkg8w', 'zeus', 'thing', 'tomato', 'lion', 'atlantic', 'celine', 'usa123', 'trans', 'account', 'aaaaaaa', 'homerun', 'hyperion', 'kevin1', 'blacks', '44444444', 'skittles', 'sean', 'hastings', 'fart', 'gangbang', 'fubar', 'sailboat', 'older', 'oilers', 'craig', 'conrad', 'church', 'damian', 'dean', 'broken', 'buster1', 'hithere', 'immortal', 'sticks', 'pilot', 'peters', 'lexmark', 'jerkoff', 'maryland', 'anders', 'cheers', 'possum', 'columbus', 'cutter', 'muppet', 'beautiful', 'stolen', 'swordfish', 'sport', 'sonic', 'peter1', 'jethro', 'rockon', 'asdfghj', 'pass123', 'paper', 'pornos', 'ncc1701a', 'bootys', 'buttman', 'bonjour', 'escape', '1960', 'becky', 'bears', '362436', 'spartans', 'tinman', 'threesom', 'lemons', 'maxmax', '1414', 'bbbbb', 'camelot', 'chad', 'chewie', 'gogo', 'fusion', 'saint', 'dilligaf', 'nopass', 'myself', 'hustler', 'hunter1', 'whitey', 'beast1', 'yesyes', 'spank', 'smudge', 'pinkfloy', 'patriot', 'lespaul', 'annette', 'hammers', 'catalina', 'finish', 'formula1', 'sausage', 'scooter1', 'orioles', 'oscar1', 'over', 'colombia', 'cramps', 'natural', 'eating', 'exotic', 'iguana', 'bella', 'suckers', 'strong', 'sheena', 'start', 'slave', 'pearl', 'topcat', 'lancelot', 'angelica', 'magelan', 'racer', 'ramona', 'crunch', 'british', 'button', 'eileen', 'steph', '456123', 'skinny', 'seeking', 'rockhard', 'chief', 'filter', 'first', 'freaks', 'sakura', 'pacman', 'poontang', 'dalton', 'newlife', 'homer1', 'klingon', 'watcher', 'walleye', 'tasha', 'tasty', 'sinatra', 'starship', 'steel', 'starbuck', 'poncho', 'amber1', 'gonzo', 'grover', 'catherin', 'carol', 'candle', 'firefly', 'goblin', 'scotch', 'diver', 'usmc', 'huskies', 'eleven', 'kentucky', 'kitkat', 'israel', 'beckham', 'bicycle', 'yourmom', 'studio', 'tara', '33333333', 'shane', 'splash', 'jimmy1', 'reality', '12344321', 'caitlin', 'focus', 'sapphire', 'mailman', 'raiders1', 'clark', 'ddddd', 'hopper', 'excalibu', 'more', 'wilbur', 'illini', 'imperial', 'phillips', 'lansing', 'maxx', 'gothic', 'golfball', 'carlton', 'camille', 'facial', 'front242', 'macdaddy', 'qwer1234', 'vectra', 'cowboys1', 'crazy1', 'dannyboy', 'jane', 'betty', 'benny', 'bennett', 'leader', 'martinez', 'aquarius', 'barkley', 'hayden', 'caught', 'franky', 'ffff', 'floyd', 'sassy', 'pppp', 'pppppppp', 'prodigy', 'clarence', 'noodle', 'eatpussy', 'vortex', 'wanking', 'beatrice', 'billy1', 'siemens', 'pedro', 'phillies', 'research', 'groups', 'carolyn', 'chevy1', 'cccc', 'fritz', 'gggggggg', 'doughboy', 'dracula', 'nurses', 'loco', 'madrid', 'lollipop', 'trout', 'utopia', 'chrono', 'cooler', 'conner', 'nevada', 'wibble', 'werner', 'summit', 'marco', 'marilyn', '1225', 'babies', 'capone', 'fugazi', 'panda', 'mama', 'qazwsxed', 'puppies', 'triton', '9876', 'command', 'nnnnnn', 'ernest', 'momoney', 'iforgot', 'wolfie', 'studly', 'shawn', 'renee', 'alien', 'hamburg', '81fukkc', '741852', 'catman', 'china', 'forgot', 'gagging', 'scott1', 'drew', 'oregon', 'qweqwe', 'train', 'crazybab', 'daniel1', 'cutlass', 'brothers', 'holes', 'heidi', 'mothers', 'music1', 'what', 'walrus', '1957', 'bigtime', 'bike', 'xtreme', 'simba', 'ssss', 'rookie', 'angie', 'bathing', 'fresh', 'sanchez', 'rotten', 'maestro', 'luis', 'look', 'turbo1', '99999', 'butthole', 'hhhh', 'elijah', 'monty', 'bender', 'yoda', 'shania', 'shock', 'phish', 'thecat', 'rightnow', 'reagan', 'baddog', 'asia', 'greatone', 'gateway1', 'randall', 'abstr', 'napster', 'brian1', 'bogart', 'high', 'hitler', 'emma', 'kill', 'weaver', 'wildfire', 'jackson1', 'isaiah', '1981', 'belinda', 'beaner', 'yoyo', '0.0.0.000', 'super1', 'select', 'snuggles', 'slutty', 'some', 'phoenix1', 'technics', 'toon', 'raven1', 'rayray', '123789', '1066', 'albion', 'greens', 'fashion', 'gesperrt', 'santana', 'paint', 'powell', 'credit', 'darling', 'mystery', 'bowser', 'bottle', 'brucelee', 'hehehe', 'kelly1', 'mojo', '1998', 'bikini', 'woofwoof', 'yyyy', 'strap', 'sites', 'spears', 'theodore', 'julius', 'richards', 'amelia', 'central', 'f**k', 'nyjets', 'punisher', 'username', 'vanilla', 'twisted', 'bryant', 'brent', 'bunghole', 'here', 'elizabeth', 'erica', 'kimber', 'viagra', 'veritas', 'pony', 'pool', 'titts', 'labtec', 'lifetime', 'jenny1', 'masterbate', 'mayhem', 'redbull', 'govols', 'gremlin', '505050', 'gmoney', 'rupert', 'rovers', 'diamond1', 'lorenzo', 'trident', 'abnormal', 'davidson', 'deskjet', 'cuddles', 'nice', 'bristol', 'karina', 'milano', 'vh5150', 'jarhead', '1982', 'bigbird', 'bizkit', 'sixers', 'slider', 'star69', 'starfish', 'penetration', 'tommy1', 'john316', 'meghan', 'michaela', 'market', 'grant', 'caligula', 'carl', 'flicks', 'films', 'madden', 'railroad', 'cosmo', 'cthulhu', 'bradford', 'br0d3r', 'military', 'bearbear', 'swedish', 'spawn', 'patrick1', 'polly', 'these', 'todd', 'reds', 'anarchy', 'groove', 'franco', 'fuckher', 'oooo', 'tyrone', 'vegas', 'airbus', 'cobra1', 'christine', 'clips', 'delete', 'duster', 'kitty1', 'mouse1', 'monkeys', 'jazzman', '1919', '262626', 'swinging', 'stroke', 'stocks', 'sting', 'pippen', 'labrador', 'jordan1', 'justdoit', 'meatball', 'females', 'saturday', 'park', 'vector', 'cooter', 'defender', 'desert', 'demon', 'nike', 'bubbas', 'bonkers', 'english', 'kahuna', 'wildman', '4121', 'sirius', 'static', 'piercing', 'terror', 'teenage', 'leelee', 'marissa', 'microsof', 'mechanic', 'robotech', 'rated', 'hailey', 'chaser', 'sanders', 'salsero', 'nuts', 'macross', 'quantum', 'rachael', 'tsunami', 'universe', 'daddy1', 'cruise', 'nguyen', 'newpass6', 'nudes', 'hellyeah', 'vernon', '1959', 'zaq12wsx', 'striker', 'sixty', 'steele', 'spice', 'spectrum', 'smegma', 'thumb', 'jjjjjjjj', 'mellow', 'astrid', 'cancun', 'cartoon', 'sabres', 'samiam', 'pants', 'oranges', 'oklahoma', 'lust', 'coleman', 'denali', 'nude', 'noodles', 'buzz', 'brest', 'hooter', 'mmmmmmmm', 'warthog', 'bloody', 'blueblue', 'zappa', 'wolverine', 'sniffing', 'lance', 'jean', 'jjjjj', 'harper', 'calico', 'freee', 'rover', 'door', 'pooter', 'closeup', 'bonsai', 'evelyn', 'emily1', 'kathryn', 'keystone', 'iiii', '1955', 'yzerman', 'theboss', 'tolkien', 'jill', 'megaman', 'rasta', 'bbbbbbbb', 'bean', 'handsome', 'hal9000', 'goofy', 'gringo', 'gofish', 'gizmo1', 'samsam', 'scuba', 'onlyme', 'tttttttt', 'corrado', 'clown', 'clapton', 'deborah', 'boris', 'bulls', 'vivian', 'jayhawk', 'bethany', 'wwww', 'sharky', 'seeker', 'ssssssss', 'somethin', 'pillow', 'thesims', 'lighter', 'lkjhgf', 'melissa1', 'marcius2', 'barry', 'guiness', 'gymnast', 'casey1', 'goalie', 'godsmack', 'doug', 'lolo', 'rangers1', 'poppy', 'abby', 'clemson', 'clipper', 'deeznuts', 'nobody', 'holly1', 'elliot', 'eeee', 'kingston', 'miriam', 'belle', 'yosemite', 'sucked', 'sex123', 'sexy69', 'pic\'s', 'tommyboy', 'lamont', 'meat', 'masterbating', 'marianne', 'marc', 'gretzky', 'happyday', 'frisco', 'scratch', 'orchid', 'orange1', 'manchest', 'quincy', 'unbelievable', 'aberdeen', 'dawson', 'nathalie', 'ne1469', 'boxing', 'hill', 'korn', 'intercourse', '161616', '1985', 'ziggy', 'supersta', 'stoney', 'senior', 'amature', 'barber', 'babyboy', 'bcfields', 'goliath', 'hack', 'hardrock', 'children', 'frodo', 'scout', 'scrappy', 'rosie', 'qazqaz', 'tracker', 'active', 'craving', 'commando', 'cohiba', 'deep', 'cyclone', 'dana', 'bubba69', 'katie1', 'mpegs', 'vsegda', 'jade', 'irish1', 'better', 'sexy1', 'sinclair', 'smelly', 'squerting', 'lions', 'jokers', 'jeanette', 'julia', 'jojojo', 'meathead', 'ashley1', 'groucho', 'cheetah', 'champ', 'firefox', 'gandalf1', 'packer', 'magnolia', 'love69', 'tyler1', 'typhoon', 'tundra', 'bobby1', 'kenworth', 'village', 'volley', 'beth', 'wolf359', '0420', '000007', 'swimmer', 'skydive', 'smokes', 'patty', 'peugeot', 'pompey', 'legolas', 'kristy', 'redhot', 'rodman', 'redalert', 'having', 'grapes', '4runner', 'carrera', 'floppy', 'dollars', 'ou8122', 'quattro', 'adams', 'cloud9', 'davids', 'nofear', 'busty', 'homemade', 'mmmmm', 'whisper', 'vermont', 'webmaste', 'wives', 'insertion', 'jayjay', 'philips', 'phone', 'topher', 'tongue', 'temptress', 'midget', 'ripken', 'havefun', 'gretchen', 'canon', 'celebrity', 'five', 'getting', 'ghetto', 'direct', 'otto', 'ragnarok', 'trinidad', 'usnavy', 'conover', 'cruiser', 'dalshe', 'nicole1', 'buzzard', 'hottest', 'kingfish', 'misfit', 'moore', 'milfnew', 'warlord', 'wassup', 'bigsexy', 'blackhaw', 'zippy', 'shearer', 'tights', 'thursday', 'kungfu', 'labia', 'journey', 'meatloaf', 'marlene', 'rider', 'area51', 'batman1', 'bananas', '636363', 'cancel', 'ggggg', 'paradox', 'mack', 'lynn', 'queens', 'adults', 'aikido', 'cigars', 'nova', 'hoosier', 'eeyore', 'moose1', 'warez', 'interacial', 'streaming', '313131', 'pertinant', 'pool6123', 'mayday', 'rivers', 'revenge', 'animated', 'banker', 'baddest', 'gordon24', 'ccccc', 'fortune', 'fantasies', 'touching', 'aisan', 'deadman', 'homepage', 'ejaculation', 'whocares', 'iscool', 'jamesbon', '1956', '1pussy', 'womam', 'sweden', 'skidoo', 'spock', 'sssss', 'petra', 'pepper1', 'pinhead', 'micron', 'allsop', 'amsterda', 'army', 'aside', 'gunnar', '666999', 'chip', 'foot', 'fowler', 'february', 'face', 'fletch', 'george1', 'sapper', 'science', 'sasha1', 'luckydog', 'lover1', 'magick', 'popopo', 'public', 'ultima', 'derek', 'cypress', 'booker', 'businessbabe', 'brandon1', 'edwards', 'experience', 'vulva', 'vvvv', 'jabroni', 'bigbear', 'yummy', '010203', 'searay', 'secret1', 'showing', 'sinbad', 'sexxxx', 'soleil', 'software', 'piccolo', 'thirteen', 'leopard', 'legacy', 'jensen', 'justine', 'memorex', 'marisa', 'mathew', 'redwing', 'rasputin', '134679', 'anfield', 'greenbay', 'gore', 'catcat', 'feather', 'scanner', 'pa55word', 'contortionist', 'danzig', 'daisy1', 'hores', 'erik', 'exodus', 'vinnie', 'iiiiii', 'zero', '1001', 'subway', 'tank', 'second', 'snapple', 'sneakers', 'sonyfuck', 'picks', 'poodle', 'test1234', 'their', 'llll', 'junebug', 'june', 'marker', 'mellon', 'ronaldo', 'roadkill', 'amanda1', 'asdfjkl', 'beaches', 'greene', 'great1', 'cheerleaers', 'force', 'doitnow', 'ozzy', 'madeline', 'radio', 'tyson', 'christian', 'daphne', 'boxster', 'brighton', 'housewifes', 'emmanuel', 'emerson', 'kkkk', 'mnbvcx', 'moocow', 'vides', 'wagner', 'janet', '1717', 'bigmoney', 'blonds', '1000', 'storys', 'stereo', '4545', '420247', 'seductive', 'sexygirl', 'lesbean', 'live', 'justin1', '124578', 'animals', 'balance', 'hansen', 'cabbage', 'canadian', 'gangbanged', 'dodge1', 'dimas', 'lori', 'loud', 'malaka', 'puss', 'probes', 'adriana', 'coolman', 'crawford', 'dante', 'nacked', 'hotpussy', 'erotica', 'kool', 'mirror', 'wearing', 'implants', 'intruder', 'bigass', 'zenith', 'woohoo', 'womans', 'tanya', 'tango', 'stacy', 'pisces', 'laguna', 'krystal', 'maxell', 'andyod22', 'barcelon', 'chainsaw', 'chickens', 'flash1', 'downtown', 'orgasms', 'magicman', 'profit', 'pusyy', 'pothead', 'coconut', 'chuckie', 'contact', 'clevelan', 'designer', 'builder', 'budweise', 'hotshot', 'horizon', 'hole', 'experienced', 'mondeo', 'wifes', '1962', 'strange', 'stumpy', 'smiths', 'sparks', 'slacker', 'piper', 'pitchers', 'passwords', 'laptop', 'jeremiah', 'allmine', 'alliance', 'bbbbbbb', 'asscock', 'halflife', 'grandma', 'hayley', '88888', 'cecilia', 'chacha', 'saratoga', 'sandy1', 'santos', 'doogie', 'number', 'positive', 'qwert40', 'transexual', 'crow', 'close-up', 'darrell', 'bonita', 'ib6ub9', 'volvo', 'jacob1', 'iiiii', 'beastie', 'sunnyday', 'stoned', 'sonics', 'starfire', 'snapon', 'pictuers', 'pepe', 'testing1', 'tiberius', 'lisalisa', 'lesbain', 'litle', 'retard', 'ripple', 'austin1', 'badgirl', 'golfgolf', 'flounder', 'garage', 'royals', 'dragoon', 'dickie', 'passwor', 'ocean', 'majestic', 'poppop', 'trailers', 'dammit', 'nokia', 'bobobo', 'br549', 'emmitt', 'knock', 'minime', 'mikemike', 'whitesox', '1954', '3232', '353535', 'seamus', 'solo', 'sparkle', 'sluttey', 'pictere', 'titten', 'lback', '1024', 'angelina', 'goodluck', 'charlton', 'fingerig', 'gallaries', 'goat', 'ruby', 'passme', 'oasis', 'lockerroom', 'logan1', 'rainman', 'twins', 'treasure', 'absolutely', 'club', 'custom', 'cyclops', 'nipper', 'bucket', 'homepage-', 'hhhhh', 'momsuck', 'indain', '2345', 'beerbeer', 'bimmer', 'susanne', 'stunner', 'stevens', '456456', 'shell', 'sheba', 'tootsie', 'tiny', 'testerer', 'reefer', 'really', '1012', 'harcore', 'gollum', '545454', 'chico', 'caveman', 'carole', 'fordf150', 'fishes', 'gaymen', 'saleen', 'doodoo', 'pa55w0rd', 'looney', 'presto', 'qqqqq', 'cigar', 'bogey', 'brewer', 'helloo', 'dutch', 'kamikaze', 'monte', 'wasser', 'vietnam', 'visa', 'japanees', '0123', 'swords', 'slapper', 'peach', 'jump', 'marvel', 'masterbaiting', 'march', 'redwood', 'rolling', '1005', 'ametuer', 'chiks', 'cathy', 'callaway', 'fucing', 'sadie1', 'panasoni', 'mamas', 'race', 'rambo', 'unknown', 'absolut', 'deacon', 'dallas1', 'housewife', 'kristi', 'keywest', 'kirsten', 'kipper', 'morning', 'wings', 'idiot', '18436572', '1515', 'beating', 'zxczxc', 'sullivan', '303030', 'shaman', 'sparrow', 'terrapin', 'jeffery', 'masturbation', 'mick', 'redfish', '1492', 'angus', 'barrett', 'goirish', 'hardcock', 'felicia', 'forfun', 'galary', 'freeporn', 'duchess', 'olivier', 'lotus', 'pornographic', 'ramses', 'purdue', 'traveler', 'crave', 'brando', 'enter1', 'killme', 'moneyman', 'welder', 'windsor', 'wifey', 'indon', 'yyyyy', 'stretch', 'taylor1', '4417', 'shopping', 'picher', 'pickup', 'thumbnils', 'johnboy', 'jets', 'jess', 'maureen', 'anne', 'ameteur', 'amateurs', 'apollo13', 'hambone', 'goldwing', '5050', 'charley', 'sally1', 'doghouse', 'padres', 'pounding', 'quest', 'truelove', 'underdog', 'trader', 'crack', 'climber', 'bolitas', 'bravo', 'hohoho', 'model', 'italian', 'beanie', 'beretta', 'wrestlin', 'stroker', 'tabitha', 'sherwood', 'sexyman', 'jewels', 'johannes', 'mets', 'marcos', 'rhino', 'bdsm', 'balloons', 'goodman', 'grils', 'happy123', 'flamingo', 'games', 'route66', 'devo', 'dino', 'outkast', 'paintbal', 'magpie', 'llllllll', 'twilight', 'critter', 'christie', 'cupcake', 'nickel', 'bullseye', 'krista', 'knickerless', 'mimi', 'murder', 'videoes', 'binladen', 'xerxes', 'slim', 'slinky', 'pinky', 'peterson', 'thanatos', 'meister', 'menace', 'ripley', 'retired', 'albatros', 'balloon', 'bank', 'goten', '5551212', 'getsdown', 'donuts', 'divorce', 'nwo4life', 'lord', 'lost', 'underwear', 'tttt', 'comet', 'deer', 'damnit', 'dddddddd', 'deeznutz', 'nasty1', 'nonono', 'nina', 'enterprise', 'eeeee', 'misfit99', 'milkman', 'vvvvvv', 'isaac', '1818', 'blueboy', 'beans', 'bigbutt', 'wyatt', 'tech', 'solution', 'poetry', 'toolman', 'laurel', 'juggalo', 'jetski', 'meredith', 'barefoot', '50spanks', 'gobears', 'scandinavian', 'original', 'truman', 'cubbies', 'nitram', 'briana', 'ebony', 'kings', 'warner', 'bilbo', 'yumyum', 'zzzzzzz', 'stylus', '321654', 'shannon1', 'server', 'secure', 'silly', 'squash', 'starman', 'steeler', 'staples', 'phrases', 'techniques', 'laser', '135790', 'allan', 'barker', 'athens', 'cbr600', 'chemical', 'fester', 'gangsta', 'fucku2', 'freeze', 'game', 'salvador', 'droopy', 'objects', 'passwd', 'lllll', 'loaded', 'louis', 'manchester', 'losers', 'vedder', 'clit', 'chunky', 'darkman', 'damage', 'buckshot', 'buddah', 'boobed', 'henti', 'hillary', 'webber', 'winter1', 'ingrid', 'bigmike', 'beta', 'zidane', 'talon', 'slave1', 'pissoff', 'person', 'thegreat', 'living', 'lexus', 'matador', 'readers', 'riley', 'roberta', 'armani', 'ashlee', 'goldstar', '5656', 'cards', 'fmale', 'ferris', 'fuking', 'gaston', 'fucku', 'ggggggg', 'sauron', 'diggler', 'pacers', 'looser', 'pounded', 'premier', 'pulled', 'town', 'trisha', 'triangle', 'cornell', 'collin', 'cosmic', 'deeper', 'depeche', 'norway', 'bright', 'helmet', 'kristine', 'kendall', 'mustard', 'misty1', 'watch', 'jagger', 'bertie', 'berger', 'word', '3x7pxr', 'silver1', 'smoking', 'snowboar', 'sonny', 'paula', 'penetrating', 'photoes', 'lesbens', 'lambert', 'lindros', 'lillian', 'roadking', 'rockford', '1357', '143143', 'asasas', 'goodboy', '898989', 'chicago1', 'card', 'ferrari1', 'galeries', 'godfathe', 'gawker', 'gargoyle', 'gangster', 'rubble', 'rrrr', 'onetime', 'pussyman', 'pooppoop', 'trapper', 'twenty', 'abraham', 'cinder', 'company', 'newcastl', 'boricua', 'bunny1', 'boxer', 'hotred', 'hockey1', 'hooper', 'edward1', 'evan', 'kris', 'misery', 'moscow', 'milk', 'mortgage', 'bigtit', 'show', 'snoopdog', 'three', 'lionel', 'leanne', 'joshua1', 'july', '1230', 'assholes', 'cedric', 'fallen', 'farley', 'gene', 'frisky', 'sanity', 'script', 'divine', 'dharma', 'lucky13', 'property', 'tricia', 'akira', 'desiree', 'broadway', 'butterfly', 'hunt', 'hotbox', 'hootie', 'heat', 'howdy', 'earthlink', 'karma', 'kiteboy', 'motley', 'westwood', '1988', 'bert', 'blackbir', 'biggles', 'wrench', 'working', 'wrestle', 'slippery', 'pheonix', 'penny1', 'pianoman', 'tomorrow', 'thedude', 'jenn', 'jonjon', 'jones1', 'mattie', 'memory', 'micheal', 'roadrunn', 'arrow', 'attitude', 'azzer', 'seahawks', 'diehard', 'dotcom', 'lola', 'tunafish', 'chivas', 'cinnamon', 'clouds', 'deluxe', 'northern', 'nuclear', 'north', 'boom', 'boobie', 'hurley', 'krishna', 'momomo', 'modles', 'volume', '23232323', 'bluedog', 'wwwwwww', 'zerocool', 'yousuck', 'pluto', 'limewire', 'link', 'joung', 'marcia', 'awnyce', 'gonavy', 'haha', 'films+pic+galeries', 'fabian', 'francois', 'girsl', 'fuckthis', 'girfriend', 'rufus', 'drive', 'uncencored', 'a123456', 'airport', 'clay', 'chrisbln', 'combat', 'cygnus', 'cupoi', 'never', 'netscape', 'brett', 'hhhhhhhh', 'eagles1', 'elite', 'knockers', 'kendra', 'mommy', '1958', 'tazmania', 'shonuf', 'piano', 'pharmacy', 'thedog', 'lips', 'jillian', 'jenkins', 'midway', 'arsenal1', 'anaconda', 'australi', 'gromit', 'gotohell', '787878', '66666', 'carmex2', 'camber', 'gator1', 'ginger1', 'fuzzy', 'seadoo', 'dorian', 'lovesex', 'rancid', 'uuuuuu', '911911', 'nature', 'bulldog1', 'helen', 'health', 'heater', 'higgins', 'kirk', 'monalisa', 'mmmmmmm', 'whiteout', 'virtual', 'ventura', 'jamie1', 'japanes', 'james007', '2727', '2469', 'blam', 'bitchass', 'believe', 'zephyr', 'stiffy', 'sweet1', 'silent', 'southpar', 'spectre', 'tigger1', 'tekken', 'lenny', 'lakota', 'lionking', 'jjjjjjj', 'medical', 'megatron', '1369', 'hawaiian', 'gymnastic', 'golfer1', 'gunners', '7779311', '515151', 'famous', 'glass', 'screen', 'rudy', 'royal', 'sanfran', 'drake', 'optimus', 'panther1', 'love1', 'mail', 'maggie1', 'pudding', 'venice', 'aaron1', 'delphi', 'niceass', 'bounce', 'busted', 'house1', 'killer1', 'miracle', 'momo', 'musashi', 'jammin', '2003', '234567', 'wp2003wp', 'submit', 'silence', 'sssssss', 'state', 'spikes', 'sleeper', 'passwort', 'toledo', 'kume', 'media', 'meme', 'medusa', 'mantis', 'remote', 'reading', 'reebok', '1017', 'artemis', 'hampton', 'harry1', 'cafc91', 'fettish', 'friendly', 'oceans', 'oooooooo', 'mango', 'ppppp', 'trainer', 'troy', 'uuuu', '909090', 'cross', 'death1', 'news', 'bullfrog', 'hokies', 'holyshit', 'eeeeeee', 'mitch', 'jasmine1', '&', '&', 'sergeant', 'spinner', 'leon', 'jockey', 'records', 'right', 'babyblue', 'hans', 'gooner', '474747', 'cheeks', 'cars', 'candice', 'fight', 'glow', 'pass1234', 'parola', 'okokok', 'pablo', 'magical', 'major', 'ramsey', 'poseidon', '989898', 'confused', 'circle', 'crusher', 'cubswin', 'nnnn', 'hollywood', 'erin', 'kotaku', 'milo', 'mittens', 'whatsup', 'vvvvv', 'iomega', 'insertions', 'bengals', 'bermuda', 'biit', 'yellow1', '012345', 'spike1', 'south', 'sowhat', 'pitures', 'peacock', 'pecker', 'theend', 'juliette', 'jimmie', 'romance', 'augusta', 'hayabusa', 'hawkeyes', 'castro', 'florian', 'geoffrey', 'dolly', 'lulu', 'qaz123', 'usarmy', 'twinkle', 'cloud', 'chuckles', 'cold', 'hounddog', 'hover', 'hothot', 'europa', 'ernie', 'kenshin', 'kojak', 'mikey1', 'water1', '196969', 'because', 'wraith', 'zebra', 'wwwww', '33333', 'simon1', 'spider1', 'snuffy', 'philippe', 'thunderb', 'teddy1', 'lesley', 'marino13', 'maria1', 'redline', 'renault', 'aloha', 'antoine', 'handyman', 'cerberus', 'gamecock', 'gobucks', 'freesex', 'duffman', 'ooooo', 'papa', 'nuggets', 'magician', 'longbow', 'preacher', 'porno1', 'county', 'chrysler', 'contains', 'dalejr', 'darius', 'darlene', 'dell', 'navy', 'buffy1', 'hedgehog', 'hoosiers', 'honey1', 'hott', 'heyhey', 'europe', 'dutchess', 'everest', 'wareagle', 'ihateyou', 'sunflowe', '3434', 'senators', 'shag', 'spoon', 'sonoma', 'stalker', 'poochie', 'terminal', 'terefon', 'laurence', 'maradona', 'maryann', 'marty', 'roman', '1007', '142536', 'alibaba', 'america1', 'bartman', 'astro', 'goth', 'century', 'chicken1', 'cheater', 'four', 'ghost1', 'passpass', 'oral', 'r2d2c3po', 'civic', 'cicero', 'myxworld', 'kkkkk', 'missouri', 'wishbone', 'infiniti', 'jameson', '1a2b3c', '1qwerty', 'wonderboy', 'skip', 'shojou', 'stanford', 'sparky1', 'smeghead', 'poiuy', 'titanium', 'torres', 'lantern', 'jelly', 'jeanne', 'meier', '1213', 'bayern', 'basset', 'gsxr750', 'cattle', 'charlene', 'fishing1', 'fullmoon', 'gilles', 'dima', 'obelix', 'popo', 'prissy', 'ramrod', 'unique', 'absolute', 'bummer', 'hotone', 'dynasty', 'entry', 'konyor', 'missy1', 'moses', '282828', 'yeah', 'xyz123', 'stop', '426hemi', '404040', 'seinfeld', 'simmons', 'pingpong', 'lazarus', 'matthews', 'marine1', 'manning', 'recovery', '12345a', 'beamer', 'babyface', 'greece', 'gustav', '7007', 'charity', 'camilla', 'ccccccc', 'faggot', 'foxy', 'frozen', 'gladiato', 'duckie', 'dogfood', 'paranoid', 'packers1', 'longjohn', 'radical', 'tuna', 'clarinet', 'claudio', 'circus', 'danny1', 'novell', 'nights', 'bonbon', 'kashmir', 'kiki', 'mortimer', 'modelsne', 'moondog', 'monaco', 'vladimir', 'insert', '1953', 'zxc123', 'supreme', '3131', 'sexxx', 'selena', 'softail', 'poipoi', 'pong', 'together', 'mars', 'martin1', 'rogue', 'alone', 'avalanch', 'audia4', '55bgates', 'cccccccc', 'chick', 'came11', 'figaro', 'geneva', 'dogboy', 'dnsadm', 'dipshit', 'paradigm', 'othello', 'operator', 'officer', 'malone', 'post', 'rafael', 'valencia', 'tripod', 'choice', 'chopin', 'coucou', 'coach', 'cocksuck', 'common', 'creature', 'borussia', 'book', 'browning', 'heritage', 'hiziad', 'homerj', 'eight', 'earth', 'millions', 'mullet', 'whisky', 'jacques', 'store', '4242', 'speedo', 'starcraf', 'skylar', 'spaceman', 'piggy', 'pierce', 'tiger2', 'legos', 'lala', 'jezebel', 'judy', 'joker1', 'mazda', 'barton', 'baker', '727272', 'chester1', 'fishman', 'food', 'rrrrrrrr', 'sandwich', 'dundee', 'lumber', 'magazine', 'radar', 'ppppppp', 'tranny', 'aaliyah', 'admiral', 'comics', 'cleo', 'delight', 'buttfuck', 'homeboy', 'eternal', 'kilroy', 'kellie', 'khan', 'violin', 'wingman', 'walmart', 'bigblue', 'blaze', 'beemer', 'beowulf', 'bigfish', 'yyyyyyy', 'woodie', 'yeahbaby', '0123456', 'tbone', 'style', 'syzygy', 'starter', 'lemon', 'linda1', 'merlot', 'mexican', '11235813', 'anita', 'banner', 'bangbang', 'badman', 'barfly', 'grease', 'carla', 'charles1', 'ffffffff', 'screw', 'doberman', 'diane', 'dogshit', 'overkill', 'counter', 'coolguy', 'claymore', 'demons', 'demo', 'nomore', 'normal', 'brewster', 'hhhhhhh', 'hondas', 'iamgod', 'enterme', 'everett', 'electron', 'eastside', 'kayla', 'minimoni', 'mybaby', 'wildbill', 'wildcard', 'ipswich', '200000', 'bearcat', 'zigzag', 'yyyyyyyy', 'xander', 'sweetnes', '369369', 'skyler', 'skywalker', 'pigeon', 'peyton', 'tipper', 'lilly', 'asdf123', 'alphabet', 'asdzxc', 'babybaby', 'banane', 'barnes', 'guyver', 'graphics', 'grand', 'chinook', 'florida1', 'flexible', 'fuckinside', 'otis', 'ursitesux', 'tototo', 'trust', 'tower', 'adam12', 'christma', 'corey', 'chrome', 'buddie', 'bombers', 'bunker', 'hippie', 'keegan', 'misfits', 'vickie', '292929', 'woofer', 'wwwwwwww', 'stubby', 'sheep', 'secrets', 'sparta', 'stang', 'spud', 'sporty', 'pinball', 'jorge', 'just4fun', 'johanna', 'maxxxx', 'rebecca1', 'gunther', 'fatima', 'fffffff', 'freeway', 'garion', 'score', 'rrrrr', 'sancho', 'outback', 'maggot', 'puddin', 'trial', 'adrienne', '987456', 'colton', 'clyde', 'brain', 'brains', 'hoops', 'eleanor', 'dwayne', 'kirby', 'mydick', 'villa', '19691969', 'bigcat', 'becker', 'shiner', 'silverad', 'spanish', 'templar', 'lamer', 'juicy', 'marsha', 'mike1', 'maximum', 'rhiannon', 'real', '1223', '10101010', 'arrows', 'andres', 'alucard', 'baldwin', 'baron', 'avenue', 'ashleigh', 'haggis', 'channel', 'cheech', 'safari', 'ross', 'dog123', 'orion1', 'paloma', 'qwerasdf', 'presiden', 'vegitto', 'trees', '969696', 'adonis', 'colonel', 'cookie1', 'newyork1', 'brigitte', 'buddyboy', 'hellos', 'heineken', 'dwight', 'eraser', 'kerstin', 'motion', 'moritz', 'millwall', 'visual', 'jaybird', '1983', 'beautifu', 'bitter', 'yvette', 'zodiac', 'steven1', 'sinister', 'slammer', 'smashing', 'slick1', 'sponge', 'teddybea', 'theater', 'this', 'ticklish', 'lipstick', 'jonny', 'massage', 'mann', 'reynolds', 'ring', '1211', 'amazing', 'aptiva', 'applepie', 'bailey1', 'guitar1', 'chanel', 'canyon', 'gagged', 'fuckme1', 'rough', 'digital1', 'dinosaur', 'punk', '98765', '90210', 'clowns', 'cubs', 'daniels', 'deejay', 'nigga', 'naruto', 'boxcar', 'icehouse', 'hotties', 'electra', 'kent', 'widget', 'india', 'insanity', '1986', '2004', 'best', 'bluefish', 'bingo1', '*****', 'stratus', 'strength', 'sultan', 'storm1', '44444', '4200', 'sentnece', 'season', 'sexyboy', 'sigma', 'smokie', 'spam', 'point', 'pippo', 'ticket', 'temppass', 'joel', 'manman', 'medicine', '1022', 'anton', 'almond', 'bacchus', 'aztnm', 'axio', 'awful', 'bamboo', 'hakr', 'gregor', 'hahahaha', '5678', 'casanova', 'caprice', 'camero1', 'fellow', 'fountain', 'dupont', 'dolphin1', 'dianne', 'paddle', 'magnet', 'qwert1', 'pyon', 'porsche1', 'tripper', 'vampires', 'coming', 'noway', 'burrito', 'bozo', 'highheel', 'hughes', 'hookem', 'eddie1', 'ellie', 'entropy', 'kkkkkkkk', 'kkkkkkk', 'illinois', 'jacobs', '1945', '1951', '24680', '21212121', '100000', 'stonecold', 'taco', 'subzero', 'sharp', 'sexxxy', 'skolko', 'shanna', 'skyhawk', 'spurs1', 'sputnik', 'piazza', 'testpass', 'letter', 'lane', 'kurt', 'jiggaman', 'matilda', '1224', 'harvard', 'hannah1', '525252', '4ever', 'carbon', 'chef', 'federico', 'ghosts', 'gina', 'scorpio1', 'rt6ytere', 'madison1', 'loki', 'raquel', 'promise', 'coolness', 'christina', 'coldbeer', 'citadel', 'brittney', 'highway', 'evil', 'monarch', 'morgan1', 'washingt', '1997', 'bella1', 'berry', 'yaya', 'yolanda', 'superb', 'taxman', 'studman', 'stephanie', '3636', 'sherri', 'sheriff', 'shepherd', 'poland', 'pizzas', 'tiffany1', 'toilet', 'latina', 'lassie', 'larry1', 'joseph1', 'mephisto', 'meagan', 'marian', 'reptile', 'rico', 'razor', '1013', 'barron', 'hammer1', 'gypsy', 'grande', 'carroll', 'camper', 'chippy', 'cat123', 'call', 'chimera', 'fiesta', 'glock', 'glenn', 'domain', 'dieter', 'dragonba', 'onetwo', 'nygiants', 'odessa', 'password2', 'louie', 'quartz', 'prowler', 'prophet', 'towers', 'ultra', 'cocker', 'corleone', 'dakota1', 'cumm', 'nnnnnnn', 'natalia', 'boxers', 'hugo', 'heynow', 'hollow', 'iceberg', 'elvira', 'kittykat', 'kate', 'kitchen', 'wasabi', 'vikings1', 'impact', 'beerman', 'string', 'sleep', 'splinter', 'snoopy1', 'pipeline', 'pocket', 'legs', 'maple', 'mickey1', 'manuela', 'mermaid', 'micro', 'meowmeow', 'redbird', 'alisha', 'baura', 'battery', 'grass', 'chevys', 'chestnut', 'caravan', 'carina', 'charmed', 'fraser', 'frogman', 'diving', 'dogger', 'draven', 'drifter', 'oatmeal', 'paris1', 'longdong', 'quant4307s', 'rachel1', 'vegitta', 'cole', 'cobras', 'corsair', 'dadada', 'noelle', 'mylife', 'nine', 'bowwow', 'body', 'hotrats', 'eastwood', 'moonligh', 'modena', 'wave', 'illusion', 'iiiiiii', 'jayhawks', 'birgit', 'zone', 'sutton', 'susana', 'swingers', 'shocker', 'shrimp', 'sexgod', 'squall', 'stefanie', 'squeeze', 'soul', 'patrice', 'poiu', 'players', 'tigers1', 'toejam', 'tickler', 'line', 'julie1', 'jimbo1', 'jefferso', 'juanita', 'michael2', 'rodeo', 'robot', '1023', 'annie1', 'bball', 'guess', 'happy2', 'charter', 'farm', 'flasher', 'falcon1', 'fiction', 'fastball', 'gadget', 'scrabble', 'diaper', 'dirtbike', 'dinner', 'oliver1', 'partner', 'paco', 'lucille', 'macman', 'poopy', 'popper', 'postman', 'ttttttt', 'ursula', 'acura', 'cowboy1', 'conan', 'daewoo', 'cyrus', 'customer', 'nation', 'nemrac58', 'nnnnn', 'nextel', 'bolton', 'bobdylan', 'hopeless', 'eureka', 'extra', 'kimmie', 'kcj9wx5n', 'killbill', 'musica', 'volkswag', 'wage', 'windmill', 'wert', 'vintage', 'iloveyou1', 'itsme', 'bessie', 'zippo', '311311', 'starligh', 'smokey1', 'spot', 'snappy', 'soulmate', 'plasma', 'thelma', 'tonight', 'krusty', 'just4me', 'mcdonald', 'marius', 'rochelle', 'rebel1', '1123', 'alfredo', 'aubrey', 'audi', 'chantal', 'fick', 'goaway', 'roses', 'sales', 'rusty2', 'dirt', 'dogbone', 'doofus', 'ooooooo', 'oblivion', 'mankind', 'luck', 'mahler', 'lllllll', 'pumper', 'puck', 'pulsar', 'valkyrie', 'tupac', 'compass', 'concorde', 'costello', 'cougars', 'delaware', 'niceguy', 'nocturne', 'bob123', 'boating', 'bronze', 'hopkins', 'herewego', 'hewlett', 'houhou', 'hubert', 'earnhard', 'eeeeeeee', 'keller', 'mingus', 'mobydick', 'venture', 'verizon', 'imation', '1950', '1948', '1949', '223344', 'bigbig', 'blossom', 'zack', 'wowwow', 'sissy', 'skinner', 'spiker', 'square', 'snooker', 'sluggo', 'player1', 'junk', 'jeannie', 'jsbach', 'jumbo', 'jewel', 'medic', 'robins', 'reddevil', 'reckless', '123456a', '1125', '1031', 'beacon', 'astra', 'gumby', 'hammond', 'hassan', '757575', '585858', 'chillin', 'fuck1', 'sander', 'lowell', 'radiohea', 'upyours', 'trek', 'courage', 'coolcool', 'classics', 'choochoo', 'darryl', 'nikki1', 'nitro', 'bugs', 'boytoy', 'ellen', 'excite', 'kirsty', 'kane', 'wingnut', 'wireless', 'icu812', '1master', 'beatle', 'bigblock', 'blanca', 'wolfen', 'summer99', 'sugar1', 'tartar', 'sexysexy', 'senna', 'sexman', 'sick', 'someone', 'soprano', 'pippin', 'platypus', 'pixies', 'telephon', 'land', 'laura1', 'laurent', 'rimmer', 'road', 'report', '1020', '12qwaszx', 'arturo', 'around', 'hamish', 'halifax', 'fishhead', 'forum', 'dododo', 'doit', 'outside', 'paramedi', 'lonesome', 'mandy1', 'twist', 'uuuuu', 'uranus', 'ttttt', 'butcher', 'bruce1', 'helper', 'hopeful', 'eduard', 'dusty1', 'kathy1', 'katherin', 'moonbeam', 'muscles', 'monster1', 'monkeybo', 'morton', 'windsurf', 'vvvvvvv', 'vivid', 'install', '1947', '187187', '1941', '1952', 'tatiana', 'susan1', '31415926', 'sinned', 'sexxy', 'senator', 'sebastian', 'shadows', 'smoothie', 'snowflak', 'playstat', 'playa', 'playboy1', 'toaster', 'jerry1', 'marie1', 'mason1', 'merlin1', 'roger1', 'roadster', '112358', '1121', 'andrea1', 'bacardi', 'auto', 'hardware', 'hardy', '789789', '5555555', 'captain1', 'flores', 'fergus', 'sascha', 'rrrrrrr', 'dome', 'onion', 'nutter', 'lololo', 'qqqqqqq', 'quick', 'undertak', 'uuuuuuuu', 'uuuuuuu', 'criminal', 'cobain', 'cindy1', 'coors', 'dani', 'descent', 'nimbus', 'nomad', 'nanook', 'norwich', 'bomb', 'bombay', 'broker', 'hookup', 'kiwi', 'winners', 'jackpot', '1a2b3c4d', '1776', 'beardog', 'bighead', 'blast', 'bird33', '0987', 'stress', 'shot', 'spooge', 'pelican', 'peepee', 'perry', 'pointer', 'titan', 'thedoors', 'jeremy1', 'annabell', 'altima', 'baba', 'hallie', 'hate', 'hardone', '5454', 'candace', 'catwoman', 'flip', 'faithful', 'finance', 'farmboy', 'farscape', 'genesis1', 'salomon', 'destroy', 'papers', 'option', 'page', 'loser1', 'lopez', 'r2d2', 'pumpkins', 'training', 'chriss', 'cumcum', 'ninjas', 'ninja1', 'hung', 'erika', 'eduardo', 'killers', 'miller1', 'islander', 'jamesbond', 'intel', 'jarvis', '19841984', '2626', 'bizzare', 'blue12', 'biker', 'yoyoma', 'sushi', 'styles', 'shitface', 'series', 'shanti', 'spanker', 'steffi', 'smart', 'sphinx', 'please1', 'paulie', 'pistons', 'tiburon', 'limited', 'maxwell1', 'mdogg', 'rockies', 'armstron', 'alexia', 'arlene', 'alejandr', 'arctic', 'banger', 'audio', 'asimov', 'augustus', 'grandpa', '753951', '4you', 'chilly', 'care1839', 'chapman', 'flyfish', 'fantasia', 'freefall', 'santa', 'sandrine', 'oreo', 'ohshit', 'macbeth', 'madcat', 'loveya', 'mallory', 'rage', 'quentin', 'qwerqwer', 'project', 'ramirez', 'colnago', 'citizen', 'chocha', 'cobalt', 'crystal1', 'dabears', 'nevets', 'nineinch', 'broncos1', 'helene', 'huge', 'edgar', 'epsilon', 'easter', 'kestrel', 'moron', 'virgil', 'winston1', 'warrior1', 'iiiiiiii', 'iloveyou2', '1616', 'beat', 'bettina', 'woowoo', 'zander', 'straight', 'shower', 'sloppy', 'specialk', 'tinkerbe', 'jellybea', 'reader', 'romero', 'redsox1', 'ride', '1215', '1112', 'annika', 'arcadia', 'answer', 'baggio', 'base', 'guido', '555666', 'carmel', 'cayman', 'cbr900rr', 'chips', 'gabriell', 'gertrude', 'glennwei', 'roxy', 'sausages', 'disco', 'pass1', 'luna', 'lovebug', 'macmac', 'queenie', 'puffin', 'vanguard', 'trip', 'trinitro', 'airwolf', 'abbott', 'aaa111', 'cocaine', 'cisco', 'cottage', 'dayton', 'deadly', 'datsun', 'bricks', 'bumper', 'eldorado', 'kidrock', 'wizard1', 'whiskers', 'wind', 'wildwood', 'istheman', 'interest', 'italy', '25802580', 'benoit', 'bigones', 'woodland', 'wolfpac', 'strawber', 'suicide', '3030', 'sheba1', 'sixpack', 'peace1', 'physics', 'pearson', 'tigger2', 'toad', 'megan1', 'meow', 'ringo', 'roll', 'amsterdam', '717171', '686868', '5424', 'catherine', 'canuck', 'football1', 'footjob', 'fulham', 'seagull', 'orgy', 'lobo', 'mancity', 'truth', 'trace', 'vancouve', 'vauxhall', 'acidburn', 'derf', 'myspace1', 'boozer', 'buttercu', 'howell', 'hola', 'easton', 'minemine', 'munch', 'jared', '1dragon', 'biology', 'bestbuy', 'bigpoppa', 'blackout', 'blowfish', 'bmw325', 'bigbob', 'stream', 'talisman', 'tazz', 'sundevil', '3333333', 'skate', 'shutup', 'shanghai', 'shop', 'spencer1', 'slowhand', 'polish', 'pinky1', 'tootie', 'thecrow', 'leroy', 'jonathon', 'jubilee', 'jingle', 'martine', 'matrix1', 'manowar', 'michaels', 'messiah', 'mclaren', 'resident', 'reilly', 'redbaron', 'rollins', 'romans', 'return', 'rivera', 'andromed', 'athlon', 'beach1', 'badgers', 'guitars', 'harald', 'harddick', 'gotribe', '6996', '7grout', '5wr2i7h8', '635241', 'chase1', 'carver', 'charlotte', 'fallout', 'fiddle', 'fredrick', 'fenris', 'francesc', 'fortuna', 'ferguson', 'fairlane', 'felipe', 'felix1', 'forward', 'gasman', 'frost', 'fucks', 'sahara', 'sassy1', 'dogpound', 'dogbert', 'divx1', 'manila', 'loretta', 'priest', 'pornporn', 'quasar', 'venom', '987987', 'access1', 'clippers', 'daylight', 'decker', 'daman', 'data', 'dentist', 'crusty', 'nathan1', 'nnnnnnnn', 'bruno1', 'bucks', 'brodie', 'budapest', 'kittens', 'kerouac', 'mother1', 'waldo1', 'wedding', 'whistler', 'whatwhat', 'wanderer', 'idontkno', '1942', '1946', 'bigdawg', 'bigpimp', 'zaqwsx', '414141', '3000gt', '434343', 'shoes', 'serpent', 'starr', 'smurf', 'pasword', 'tommie', 'thisisit', 'lake', 'john1', 'robotics', 'redeye', 'rebelz', '1011', 'alatam', 'asses', 'asians', 'bama', 'banzai', 'harvest', 'gonzalez', 'hair', 'hanson', '575757', '5329', 'cascade', 'chinese', 'fatty', 'fender1', 'flower2', 'funky', 'sambo', 'drummer1', 'dogcat', 'dottie', 'oedipus', 'osama', 'macleod', 'prozac', 'private1', 'rampage', 'punch', 'presley', 'concord', 'cook', 'cinema', 'cornwall', 'cleaner', 'christopher', 'ciccio', 'corinne', 'clutch', 'corvet07', 'daemon', 'bruiser', 'boiler', 'hjkl', 'eyes', 'egghead', 'expert', 'ethan', 'kasper', 'mordor', 'wasted', 'jamess', 'iverson3', 'bluesman', 'zouzou', '090909', '1002', 'switch', 'stone1', '4040', 'sisters', 'sexo', 'shawna', 'smith1', 'sperma', 'sneaky', 'polska', 'thewho', 'terminat', 'krypton', 'lawson', 'library', 'lekker', 'jules', 'johnson1', 'johann', 'justus', 'rockie', 'romano', 'aspire', 'bastards', 'goodie', 'cheese1', 'fenway', 'fishon', 'fishin', 'fuckoff1', 'girls1', 'sawyer', 'dolores', 'desmond', 'duane', 'doomsday', 'pornking', 'ramones', 'rabbits', 'transit', 'aaaaa1', 'clock', 'delilah', 'noel', 'boyz', 'bookworm', 'bongo', 'bunnies', 'brady', 'buceta', 'highbury', 'henry1', 'heels', 'eastern', 'krissy', 'mischief', 'mopar', 'ministry', 'vienna', 'weston', 'wildone', 'vodka', 'jayson', 'bigbooty', 'beavis1', 'betsy', 'xxxxxx1', 'yogibear', '000001', '0815', 'zulu', '420000', 'september', 'sigmar', 'sprout', 'stalin', 'peggy', 'patch', 'lkjhgfds', 'lagnaf', 'rolex', 'redfox', 'referee', '123123123', '1231', 'angus1', 'ariana', 'ballin', 'attila', 'hall', 'greedy', 'grunt', '747474', 'carpedie', 'cecile', 'caramel', 'foxylady', 'field', 'gatorade', 'gidget', 'futbol', 'frosch', 'saiyan', 'schmidt', 'drums', 'donner', 'doggy1', 'drum', 'doudou', 'pack', 'pain', 'nutmeg', 'quebec', 'valdepen', 'trash', 'triple', 'tosser', 'tuscl', 'track', 'comfort', 'choke', 'comein', 'cola', 'deputy', 'deadpool', 'bremen', 'borders', 'bronson', 'break', 'hotass', 'hotmail1', 'eskimo', 'eggman', 'koko', 'kieran', 'katrin', 'kordell1', 'komodo', 'mone', 'munich', 'vvvvvvvv', 'winger', 'jaeger', 'ivan', 'jackson5', '2222222', 'bergkamp', 'bennie', 'bigben', 'zanzibar', 'worm', 'xxx123', 'sunny1', '373737', 'services', 'sheridan', 'slater', 'slayer1', 'snoop', 'stacie', 'peachy', 'thecure', 'times', 'little1', 'jennaj', 'marquis', 'middle', 'rasta69', '1114', 'aries', 'havana', 'gratis', 'calgary', 'checkers', 'flanker', 'salope', 'dirty1', 'draco', 'dogface', 'luv2epus', 'rainbow6', 'qwerty123', 'umpire', 'turnip', 'vbnm', 'tucson', 'troll', 'aileen', 'codered', 'commande', 'damon', 'nana', 'neon', 'nico', 'nightwin', 'neil', 'boomer1', 'bushido', 'hotmail0', 'horace', 'enternow', 'kaitlyn', 'keepout', 'karen1', 'mindy', 'mnbv', 'viewsoni', 'volcom', 'wizards', 'wine', '1995', 'berkeley', 'bite', 'zach', 'woodstoc', 'tarpon', 'shinobi', 'starstar', 'phat', 'patience', 'patrol', 'toolbox', 'julien', 'johnny1', 'joebob', 'marble', 'riders', 'reflex', '120676', '1235', 'angelus', 'anthrax', 'atlas', 'hawks', 'grandam', 'harlem', 'hawaii50', 'gorgeous', '655321', 'cabron', 'challeng', 'callisto', 'firewall', 'firefire', 'fischer', 'flyer', 'flower1', 'factory', 'federal', 'gambler', 'frodo1', 'funk', 'sand', 'sam123', 'scania', 'dingo', 'papito', 'passmast', 'olive', 'palermo', 'ou8123', 'lock', 'ranch', 'pride', 'randy1', 'twiggy', 'travis1', 'transfer', 'treetop', 'addict', 'admin1', '963852', 'aceace', 'clarissa', 'cliff', 'cirrus', 'clifton', 'colin', 'bobdole', 'bonner', 'bogus', 'bonjovi', 'bootsy', 'boater', 'elway7', 'edison', 'kelvin', 'kenny1', 'moonshin', 'montag', 'moreno', 'wayne1', 'white1', 'jazzy', 'jakejake', '1994', '1991', '2828', 'blunt', 'bluejays', 'beau', 'belmont', 'worthy', 'systems', 'sensei', 'southpark', 'stan', 'peeper', 'pharao', 'pigpen', 'tomahawk', 'teensex', 'leedsutd', 'larkin', 'jermaine', 'jeepster', 'jimjim', 'josephin', 'melons', 'marlon', 'matthias', 'marriage', 'robocop', '1003', '1027', 'antelope', 'azsxdc', 'gordo', 'hazard', 'granada', '8989', '7894', 'ceasar', 'cabernet', 'cheshire', 'california', 'chelle', 'candy1', 'fergie', 'fanny', 'fidelio', 'giorgio', 'fuckhead', 'ruth', 'sanford', 'diego', 'dominion', 'devon', 'panic', 'longer', 'mackie', 'qawsed', 'trucking', 'twelve', 'chloe1', 'coral', 'daddyo', 'nostromo', 'boyboy', 'booster', 'bucky', 'honolulu', 'esquire', 'dynamite', 'motor', 'mollydog', 'wilder', 'windows1', 'waffle', 'wallet', 'warning', 'virus', 'washburn', 'wealth', 'vincent1', 'jabber', 'jaguars', 'javelin', 'irishman', 'idefix', 'bigdog1', 'blue42', 'blanked', 'blue32', 'biteme1', 'bearcats', 'blaine', 'yessir', 'sylveste', 'team', 'stephan', 'sunfire', 'tbird', 'stryker', '3ip76k2', 'sevens', 'sheldon', 'pilgrim', 'tenchi', 'titman', 'leeds', 'lithium', 'lander', 'linkin', 'landon', 'marijuan', 'mariner', 'markie', 'midnite', 'reddwarf', '1129', '123asd', '12312312', 'allstar', 'albany', 'asdf12', 'antonia', 'aspen', 'hardball', 'goldfing', '7734', '49ers', 'carlo', 'chambers', 'cable', 'carnage', 'callum', 'carlos1', 'fitter', 'fandango', 'festival', 'flame', 'gofast', 'gamma', 'fucmy69', 'scrapper', 'dogwood', 'django', 'magneto', 'loose', 'premium', 'addison', '9999999', 'abc1234', 'cromwell', 'newyear', 'nichole', 'bookie', 'burns', 'bounty', 'brown1', 'bologna', 'earl', 'entrance', 'elway', 'killjoy', 'kerry', 'keenan', 'kick', 'klondike', 'mini', 'mouser', 'mohammed', 'wayer', 'impreza', 'irene', 'insomnia', '24682468', '2580', '24242424', 'billbill', 'bellaco', 'blessing', 'blues1', 'bedford', 'blanco', 'blunts', 'stinks', 'teaser', 'streets', 'sf49ers', 'shovel', 'solitude', 'spikey', 'sonia', 'pimpdadd', 'timeout', 'toffee', 'lefty', 'johndoe', 'johndeer', 'mega', 'manolo', 'mentor', 'margie', 'ratman', 'ridge', 'record', 'rhodes', 'robin1', '1124', '1210', '1028', '1226', 'another', 'babylove', 'barbados', 'harbor', 'gramma', '646464', 'carpente', 'chaos1', 'fishbone', 'fireblad', 'glasgow', 'frogs', 'scissors', 'screamer', 'salem', 'scuba1', 'ducks', 'driven', 'doggies', 'dicky', 'donovan', 'obsidian', 'rams', 'progress', 'tottenham', 'aikman', 'comanche', 'corolla', 'clarke', 'conway', 'cumslut', 'cyborg', 'dancing', 'boston1', 'bong', 'houdini', 'helmut', 'elvisp', 'edge', 'keksa12', 'misha', 'monty1', 'monsters', 'wetter', 'watford', 'wiseguy', 'veronika', 'visitor', 'janelle', '1989', '1987', '20202020', 'biatch', 'beezer', 'bigguns', 'blueball', 'bitchy', 'wyoming', 'yankees2', 'wrestler', 'stupid1', 'sealteam', 'sidekick', 'simple1', 'smackdow', 'sporting', 'spiral', 'smeller', 'sperm', 'plato', 'tophat', 'test2', 'theatre', 'thick', 'toomuch', 'leigh', 'jello', 'jewish', 'junkie', 'maxim', 'maxime', 'meadow', 'remingto', 'roofer', '124038', '1018', '1269', '1227', '123457', 'arkansas', 'alberta', 'aramis', 'andersen', 'beaker', 'barcelona', 'baltimor', 'googoo', 'goochi', '852456', '4711', 'catcher', 'carman', 'champ1', 'chess', 'fortress', 'fishfish', 'firefigh', 'geezer', 'rsalinas', 'samuel1', 'saigon', 'scooby1', 'doors', 'dick1', 'devin', 'doom', 'dirk', 'doris', 'dontknow', 'load', 'magpies', 'manfred', 'raleigh', 'vader1', 'universa', 'tulips', 'defense', 'mygirl', 'burn', 'bowtie', 'bowman', 'holycow', 'heinrich', 'honeys', 'enforcer', 'katherine', 'minerva', 'wheeler', 'witch', 'waterboy', 'jaime', 'irving', '1992', '23skidoo', 'bimbo', 'blue11', 'birddog', 'woodman', 'womble', 'zildjian', '030303', 'stinker', 'stoppedby', 'sexybabe', 'speakers', 'slugger', 'spotty', 'smoke1', 'polopolo', 'perfect1', 'things', 'torpedo', 'tender', 'thrasher', 'lakeside', 'lilith', 'jimmys', 'jerk', 'junior1', 'marsh', 'masamune', 'rice', 'root', '1214', 'april1', 'allgood', 'bambi', 'grinch', '767676', '5252', 'cherries', 'chipmunk', 'cezer121', 'carnival', 'capecod', 'finder', 'flint', 'fearless', 'goats', 'funstuff', 'gideon', 'savior', 'seabee', 'sandro', 'schalke', 'salasana', 'disney1', 'duckman', 'options', 'pancake', 'pantera1', 'malice', 'lookin', 'love123', 'lloyd', 'qwert123', 'puppet', 'prayers', 'union', 'tracer', 'crap', 'creation', 'cwoui', 'nascar24', 'hookers', 'hollie', 'hewitt', 'estrella', 'erection', 'ernesto', 'ericsson', 'edthom', 'kaylee', 'kokoko', 'kokomo', 'kimball', 'morales', 'mooses', 'monk', 'walton', 'weekend', 'inter', 'internal', '1michael', '1993', '19781978', '25252525', 'worker', 'summers', 'surgery', 'shibby', 'shamus', 'skibum', 'sheepdog', 'sex69', 'spliff', 'slipper', 'spoons', 'spanner', 'snowbird', 'slow', 'toriamos', 'temp123', 'tennesse', 'lakers1', 'jomama', 'julio', 'mazdarx7', 'rosario', 'recon', 'riddle', 'room', 'revolver', '1025', '1101', 'barney1', 'babycake', 'baylor', 'gotham', 'gravity', 'hallowee', 'hancock', '616161', '515000', 'caca', 'cannabis', 'castor', 'chilli', 'fdsa', 'getout', 'fuck69', 'gators1', 'sail', 'sable', 'rumble', 'dolemite', 'dork', 'dickens', 'duffer', 'dodgers1', 'painting', 'onions', 'logger', 'lorena', 'lookout', 'magic32', 'port', 'poon', 'prime', 'twat', 'coventry', 'citroen', 'christmas', 'civicsi', 'cocksucker', 'coochie', 'compaq1', 'nancy1', 'buzzer', 'boulder', 'butkus', 'bungle', 'hogtied', 'honor', 'hero', 'hotgirls', 'hilary', 'heidi1', 'eggplant', 'mustang6', 'mortal', 'monkey12', 'wapapapa', 'wendy1', 'volleyba', 'vibrate', 'vicky', 'bledsoe', 'blink', 'birthday4', 'woof', 'xxxxx1', 'talk', 'stephen1', 'suburban', 'stock', 'tabatha', 'sheeba', 'start1', 'soccer10', 'something', 'starcraft', 'soccer12', 'peanut1', 'plastics', 'penthous', 'peterbil', 'tools', 'tetsuo', 'torino', 'tennis1', 'termite', 'ladder', 'last', 'lemmein', 'lakewood', 'jughead', 'melrose', 'megane', 'reginald', 'redone', 'request', 'angela1', 'alive', 'alissa', 'goodgirl', 'gonzo1', 'golden1', 'gotyoass', '656565', '626262', 'capricor', 'chains', 'calvin1', 'foolish', 'fallon', 'getmoney', 'godfather', 'gabber', 'gilligan', 'runaway', 'salami', 'dummy', 'dungeon', 'dudedude', 'dumb', 'dope', 'opus', 'paragon', 'oxygen', 'panhead', 'pasadena', 'opendoor', 'odyssey', 'magellan', 'lottie', 'printing', 'pressure', 'prince1', 'trustme', 'christa', 'court', 'davies', 'neville', 'nono', 'bread', 'buffet', 'hound', 'kajak', 'killkill', 'mona', 'moto', 'mildred', 'winner1', 'vixen', 'whiteboy', 'versace', 'winona', 'voyager1', 'instant', 'indy', 'jackjack', 'bigal', 'beech', 'biggun', 'blake1', 'blue99', 'big1', 'woods', 'synergy', 'success1', '336699', 'sixty9', 'shark1', 'skin', 'simba1', 'sharpe', 'sebring', 'spongebo', 'spunk', 'springs', 'sliver', 'phialpha', 'password9', 'pizza1', 'plane', 'perkins', 'pookey', 'tickling', 'lexingky', 'lawman', 'joe123', 'jolly', 'mike123', 'romeo1', 'redheads', 'reserve', 'apple123', 'alanis', 'ariane', 'antony', 'backbone', 'aviation', 'band', 'hand', 'green123', 'haley', 'carlitos', 'byebye', 'cartman1', 'camden', 'chewy', 'camaross', 'favorite6', 'forumwp', 'franks', 'ginscoot', 'fruity', 'sabrina1', 'devil666', 'doughnut', 'pantie', 'oldone', 'paintball', 'lumina', 'rainbow1', 'prosper', 'total', 'true', 'umbrella', 'ajax', '951753', 'achtung', 'abc12345', 'compact', 'color', 'corn', 'complete', 'christi', 'closer', 'corndog', 'deerhunt', 'darklord', 'dank', 'nimitz', 'brandy1', 'bowl', 'breanna', 'holidays', 'hetfield', 'holein1', 'hillbill', 'hugetits', 'east', 'evolutio', 'kenobi', 'whiplash', 'waldo', 'wg8e3wjf', 'wing', 'istanbul', 'invis', '1996', 'benton', 'bigjohn', 'bluebell', 'beef', 'beater', 'benji', 'bluejay', 'xyzzy', 'wrestling', 'storage', 'superior', 'suckdick', 'taichi', 'stellar', 'stephane', 'shaker', 'skirt', 'seymour', 'semper', 'splurge', 'squeak', 'pearls', 'playball', 'pitch', 'phyllis', 'pooky', 'piss', 'tomas', 'titfuck', 'joemama', 'johnny5', 'marcello', 'marjorie', 'married', 'maxi', 'rhubarb', 'rockwell', 'ratboy', 'reload', 'rooney', 'redd', '1029', '1030', '1220', 'anchor', 'bbking', 'baritone', 'gryphon', 'gone', '57chevy', '494949', 'celeron', 'fishy', 'gladiator', 'fucker1', 'roswell', 'dougie', 'downer', 'dicker', 'diva', 'domingo', 'donjuan', 'nympho', 'omar', 'praise', 'racers', 'trick', 'trauma', 'truck1', 'trample', 'acer', 'corwin', 'cricket1', 'clemente', 'climax', 'denmark', 'cuervo', 'notnow', 'nittany', 'neutron', 'native', 'bosco1', 'buffa', 'breaker', 'hello2', 'hydro', 'estelle', 'exchange', 'explore', 'kisskiss', 'kittys', 'kristian', 'montecar', 'modem', 'mississi', 'mooney', 'weiner', 'washington', '20012001', 'bigdick1', 'bibi', 'benfica', 'yahoo1', 'striper', 'tabasco', 'supra', '383838', '456654', 'seneca', 'serious', 'shuttle', 'socks', 'stanton', 'penguin1', 'pathfind', 'testibil', 'thethe', 'listen', 'lightning', 'lighting', 'jeter2', 'marma', 'mark1', 'metoo', 'republic', 'rollin', 'redleg', 'redbone', 'redskin', 'rocco', '1245', 'armand', 'anthony7', 'altoids', 'andrews', 'barley', 'away', 'asswipe', 'bauhaus', 'bbbbbb1', 'gohome', 'harrier', 'golfpro', 'goldeney', '818181', '6666666', '5000', '5rxypn', 'cameron1', 'calling', 'checker', 'calibra', 'fields', 'freefree', 'faith1', 'fist', 'fdm7ed', 'finally', 'giraffe', 'glasses', 'giggles', 'fringe', 'gate', 'georgie', 'scamper', 'rrpass1', 'screwyou', 'duffy', 'deville', 'dimples', 'pacino', 'ontario', 'passthie', 'oberon', 'quest1', 'postov1000', 'puppydog', 'puffer', 'raining', 'protect', 'qwerty7', 'trey', 'tribe', 'ulysses', 'tribal', 'adam25', 'a1234567', 'compton', 'collie', 'cleopatr', 'contract', 'davide', 'norris', 'namaste', 'myrtle', 'buffalo1', 'bonovox', 'buckley', 'bukkake', 'burning', 'burner', 'bordeaux', 'burly', 'hun999', 'emilie', 'elmo', 'enters', 'enrique', 'keisha', 'mohawk', 'willard', 'vgirl', 'whale', 'vince', 'jayden', 'jarrett', '1812', '1943', '222333', 'bigjim', 'bigd', 'zoom', 'wordup', 'ziggy1', 'yahooo', 'workout', 'young1', 'written', 'xmas', 'zzzzzz1', 'surfer1', 'strife', 'sunlight', 'tasha1', 'skunk', 'shauna', 'seth', 'soft', 'sprinter', 'peaches1', 'planes', 'pinetree', 'plum', 'pimping', 'theforce', 'thedon', 'toocool', 'leeann', 'laddie', 'list', 'lkjh', 'lara', 'joke', 'jupiter1', 'mckenzie', 'matty', 'rene', 'redrose', '1200', '102938', 'annmarie', 'alexa', 'antares', 'austin31', 'ground', 'goose1', '737373', '78945612', '789987', '6464', 'calimero', 'caster', 'casper1', 'cement', 'chevrolet', 'chessie', 'caddy', 'chill', 'child', 'canucks', 'feeling', 'favorite', 'fellatio', 'f00tball', 'francine', 'gateway2', 'gigi', 'gamecube', 'giovanna', 'rugby1', 'scheisse', 'dshade', 'dudes', 'dixie1', 'owen', 'offshore', 'olympia', 'lucas1', 'macaroni', 'manga', 'pringles', 'puff', 'tribble', 'trouble1', 'ussy', 'core', 'clint', 'coolhand', 'colonial', 'colt', 'debra', 'darthvad', 'dealer', 'cygnusx1', 'natalie1', 'newark', 'husband', 'hiking', 'errors', 'eighteen', 'elcamino', 'emmett', 'emilia', 'koolaid', 'knight1', 'murphy1', 'volcano', 'idunno', '2005', '2233', 'block', 'benito', 'blueberr', 'biguns', 'yamahar1', 'zapper', 'zorro1', '0911', '3006', 'sixsix', 'shopper', 'siobhan', 'sextoy', 'stafford', 'snowboard', 'speedway', 'sounds', 'pokey', 'peabody', 'playboy2', 'titi', 'think', 'toast', 'toonarmy', 'lister', 'lambda', 'joecool', 'jonas', 'joyce', 'juniper', 'mercer', 'max123', 'manny', 'massimo', 'mariposa', 'met2002', 'reggae', 'ricky1', '1236', '1228', '1016', 'all4one', 'arianna', 'baberuth', 'asgard', 'gonzales', '484848', '5683', '6669', 'catnip', 'chiquita', 'charisma', 'capslock', 'cashmone', 'chat', 'figure', 'galant', 'frenchy', 'gizmodo1', 'girlies', 'gabby', 'garner', 'screwy', 'doubled', 'divers', 'dte4uw', 'done', 'dragonfl', 'maker', 'locks', 'rachelle', 'treble', 'twinkie', 'trailer', 'tropical', 'acid', 'crescent', 'cooking', 'cococo', 'cory', 'dabomb', 'daffy', 'dandfa', 'cyrano', 'nathanie', 'briggs', 'boners', 'helium', 'horton', 'hoffman', 'hellas', 'espresso', 'emperor', 'killa', 'kikimora', 'wanda', 'w4g8at', 'verona', 'ilikeit', 'iforget', '1944', '20002000', 'birthday1', 'beatles1', 'blue1', 'bigdicks', 'beethove', 'blacklab', 'blazers', 'benny1', 'woodwork', '0069', '0101', 'taffy', 'susie', 'survivor', 'swim', 'stokes', '4567', 'shodan', 'spoiled', 'steffen', 'pissed', 'pavlov', 'pinnacle', 'place', 'petunia', 'terrell', 'thirty', 'toni', 'tito', 'teenie', 'lemonade', 'lily', 'lillie', 'lalakers', 'lebowski', 'lalalala', 'ladyboy', 'jeeper', 'joyjoy', 'mercury1', 'mantle', 'mannn', 'rocknrol', 'riversid', 'reeves', '123aaa', '11112222', '121314', '1021', '1004', '1120', 'allen1', 'ambers', 'amstel', 'ambrose', 'alice1', 'alleycat', 'allegro', 'ambrosia', 'alley', 'australia', 'hatred', 'gspot', 'graves', 'goodsex', 'hattrick', 'harpoon', '878787', '8inches', '4wwvte', 'cassandr', 'charlie123', 'case', 'chavez', 'fighting', 'gabriela', 'gatsby', 'fudge', 'gerry', 'generic', 'gareth', 'fuckme2', 'samm', 'sage', 'seadog', 'satchmo', 'scxakv', 'santafe', 'dipper', 'dingle', 'dizzy', 'outoutout', 'madmad', 'london1', 'qbg26i', 'pussy123', 'randolph', 'vaughn', 'tzpvaw', 'vamp', 'comedy', 'comp', 'cowgirl', 'coldplay', 'dawgs', 'delaney', 'nt5d27', 'novifarm', 'needles', 'notredam', 'newness', 'mykids', 'bryan1', 'bouncer', 'hihihi', 'honeybee', 'iceman1', 'herring', 'horn', 'hook', 'hotlips', 'dynamo', 'klaus', 'kittie', 'kappa', 'kahlua', 'muffy', 'mizzou', 'mohamed', 'musical', 'wannabe', 'wednesda', 'whatup', 'weller', 'waterfal', 'willy1', 'invest', 'blanche', 'bear1', 'billabon', 'youknow', 'zelda', 'yyyyyy1', 'zachary1', '01234567', '070462', 'zurich', 'superstar', 'storms', 'tail', 'stiletto', 'strat', '427900', 'sigmachi', 'shelter', 'shells', 'sexy123', 'smile1', 'sophie1', 'stefano', 'stayout', 'somerset', 'smithers', 'playmate', 'pinkfloyd', 'phish1', 'payday', 'thebear', 'telefon', 'laetitia', 'kswbdu', 'larson', 'jetta', 'jerky', 'melina', 'metro', 'revoluti', 'retire', 'respect', '1216', '1201', '1204', '1222', '1115', 'archange', 'barry1', 'handball', '676767', 'chandra', 'chewbacc', 'flesh', 'furball', 'gocubs', 'fruit', 'fullback', 'gman', 'gentle', 'dunbar', 'dewalt', 'dominiqu', 'diver1', 'dhip6a', 'olemiss', 'ollie', 'mandrake', 'mangos', 'pretzel', 'pusssy', 'tripleh', 'valdez', 'vagabond', 'clean', 'comment', 'crew', 'clovis', 'deaths', 'dandan', 'csfbr5yy', 'deadspin', 'darrel', 'ninguna', 'noah', 'ncc74656', 'bootsie', 'bp2002', 'bourbon', 'brennan', 'bumble', 'books', 'hose', 'heyyou', 'houston1', 'hemlock', 'hippo', 'hornets', 'hurricane', 'horseman', 'hogan', 'excess', 'extensa', 'muffin1', 'virginie', 'werdna', 'idontknow', 'info', 'iron', 'jack1', '1bitch', '151nxjmt', 'bendover', 'bmwbmw', 'bills', 'zaq123', 'wxcvbn', 'surprise', 'supernov', 'tahoe', 'talbot', 'simona', 'shakur', 'sexyone', 'seviyi', 'sonja', 'smart1', 'speed1', 'pepito', 'phantom1', 'playoffs', 'terry1', 'terrier', 'laser1', 'lite', 'lancia', 'johngalt', 'jenjen', 'jolene', 'midori', 'message', 'maserati', 'matteo', 'mental', 'miami1', 'riffraff', 'ronald1', 'reason', 'rhythm', '1218', '1026', '123987', '1015', '1103', 'armada', 'architec', 'austria', 'gotmilk', 'hawkins', 'gray', 'camila', 'camp', 'cambridg', 'charge', 'camero', 'flex', 'foreplay', 'getoff', 'glacier', 'glotest', 'froggie', 'gerbil', 'rugger', 'sanity72', 'salesman', 'donna1', 'dreaming', 'deutsch', 'orchard', 'oyster', 'palmtree', 'ophelia', 'pajero', 'm5wkqf', 'magenta', 'luckyone', 'treefrog', 'vantage', 'usmarine', 'tyvugq', 'uptown', 'abacab', 'aaaaaa1', 'advance', 'chuck1', 'delmar', 'darkange', 'cyclones', 'nate', 'navajo', 'nope', 'border', 'bubba123', 'building', 'iawgk2', 'hrfzlz', 'dylan1', 'enrico', 'encore', 'emilio', 'eclipse1', 'killian', 'kayleigh', 'mutant', 'mizuno', 'mustang2', 'video1', 'viewer', 'weed420', 'whales', 'jaguar1', 'insight', '1990', '159159', '1love', 'bliss', 'bears1', 'bigtruck', 'binder', 'bigboss', 'blitz', 'xqgann', 'yeahyeah', 'zeke', 'zardoz', 'stickman', 'table', '3825', 'signal', 'sentra', 'side', 'shiva', 'skipper1', 'singapor', 'southpaw', 'sonora', 'squid', 'slamdunk', 'slimjim', 'placid', 'photon', 'placebo', 'pearl1', 'test12', 'therock1', 'tiger123', 'leinad', 'legman', 'jeepers', 'joeblow', 'mccarthy', 'mike23', 'redcar', 'rhinos', 'rjw7x4', '1102', '13576479', '112211', 'alcohol', 'gwju3g', 'greywolf', '7bgiqk', '7878', '535353', '4snz9g', 'candyass', 'cccccc1', 'carola', 'catfight', 'cali', 'fister', 'fosters', 'finland', 'frankie1', 'gizzmo', 'fuller', 'royalty', 'rugrat', 'sandie', 'rudolf', 'dooley', 'dive', 'doreen', 'dodo', 'drop', 'oemdlg', 'out3xf', 'paddy', 'opennow', 'puppy1', 'qazwsxedc', 'pregnant', 'quinn', 'ramjet', 'under', 'uncle', 'abraxas', 'corner', 'creed', 'cocoa', 'crown', 'cows', 'cn42qj', 'dancer1', 'death666', 'damned', 'nudity', 'negative', 'nimda2k', 'buick', 'bobb', 'braves1', 'brook', 'henrik', 'higher', 'hooligan', 'dust', 'everlast', 'karachi', 'mortis', 'mulligan', 'monies', 'motocros', 'wally1', 'weapon', 'waterman', 'view', 'willie1', 'vicki', 'inspiron', '1test', '2929', 'bigblack', 'xytfu7', 'yackwin', 'zaq1xsw2', 'yy5rbfsc', '100100', '0660', 'tahiti', 'takehana', 'talks', '332211', '3535', 'sedona', 'seawolf', 'skydiver', 'shine', 'spleen', 'slash', 'spjfet', 'special1', 'spooner', 'slimshad', 'sopranos', 'spock1', 'penis1', 'patches1', 'terri', 'thierry', 'thething', 'toohot', 'large', 'limpone', 'johnnie', 'mash4077', 'matchbox', 'masterp', 'maxdog', 'ribbit', 'reed', 'rita', 'rockin', 'redhat', 'rising', '1113', '14789632', '1331', 'allday', 'aladin', 'andrey', 'amethyst', 'ariel', 'anytime', 'baseball1', 'athome', 'basil', 'goofy1', 'greenman', 'gustavo', 'goofball', 'ha8fyp', 'goodday', '778899', 'charon', 'chappy', 'castillo', 'caracas', 'cardiff', 'capitals', 'canada1', 'cajun', 'catter', 'freddy1', 'favorite2', 'frazier', 'forme', 'follow', 'forsaken', 'feelgood', 'gavin', 'gfxqx686', 'garlic', 'sarge', 'saskia', 'sanjose', 'russ', 'salsa', 'dilbert1', 'dukeduke', 'downhill', 'longhair', 'loop', 'locutus', 'lockdown', 'malachi', 'mamacita', 'lolipop', 'rainyday', 'pumpkin1', 'punker', 'prospect', 'rambo1', 'rainbows', 'quake', 'twin', 'trinity1', 'trooper1', 'aimee', 'citation', 'coolcat', 'crappy', 'default', 'dental', 'deniro', 'd9ungl', 'daddys', 'napoli', 'nautica', 'nermal', 'bukowski', 'brick', 'bubbles1', 'bogota', 'board', 'branch', 'breath', 'buds', 'hulk', 'humphrey', 'hitachi', 'evans', 'ender', 'export', 'kikiki', 'kcchiefs', 'kram', 'morticia', 'montrose', 'mongo', 'waqw3p', 'wizzard', 'visited', 'whdbtp', 'whkzyc', 'image', '154ugeiu', '1fuck', 'binky', 'blind', 'bigred1', 'blubber', 'benz', 'becky1', 'year2005', 'wonderfu', 'wooden', 'xrated', '0001', 'tampabay', 'survey', 'tammy1', 'stuffer', '3mpz4r', '3000', '3some', 'selina', 'sierra1', 'shampoo', 'silk', 'shyshy', 'slapnuts', 'standby', 'spartan1', 'sprocket', 'sometime', 'stanley1', 'poker1', 'plus', 'thought', 'theshit', 'torture', 'thinking', 'lavalamp', 'light1', 'laserjet', 'jediknig', 'jjjjj1', 'jocelyn', 'mazda626', 'menthol', 'maximo', 'margaux', 'medic1', 'release', 'richter', 'rhino1', 'roach', 'renate', 'repair', 'reveal', '1209', '1234321', 'amigos', 'apricot', 'alexandra', 'asdfgh1', 'hairball', 'hatter', 'graduate', 'grimace', '7xm5rq', '6789', 'cartoons', 'capcom', 'cheesy', 'cashflow', 'carrots', 'camping', 'fanatic', 'fool', 'format', 'fleming', 'girlie', 'glover', 'gilmore', 'gardner', 'safeway', 'ruthie', 'dogfart', 'dondon', 'diapers', 'outsider', 'odin', 'opiate', 'lollol', 'love12', 'loomis', 'mallrats', 'prague', 'primetime21', 'pugsley', 'program', 'r29hqq', 'touch', 'valleywa', 'airman', 'abcdefg1', 'darkone', 'cummer', 'dempsey', 'damn', 'nadia', 'natedogg', 'nineball', 'ndeyl5', 'natchez', 'newone', 'normandy', 'nicetits', 'buddy123', 'buddys', 'homely', 'husky', 'iceland', 'hr3ytm', 'highlife', 'holla', 'earthlin', 'exeter', 'eatmenow', 'kimkim', 'karine', 'k2trix', 'kernel', 'kirkland', 'money123', 'moonman', 'miles1', 'mufasa', 'mousey', 'wilma', 'wilhelm', 'whites', 'warhamme', 'instinct', 'jackass1', '2277', '20spanks', 'blobby', 'blair', 'blinky', 'bikers', 'blackjack', 'becca', 'blue23', 'xman', 'wyvern', '085tzzqi', 'zxzxzx', 'zsmj2v', 'suede', 't26gn4', 'sugars', 'sylvie', 'tantra', 'swoosh', 'swiss', '4226', '4271', '321123', '383pdjvl', 'shoe', 'shane1', 'shelby1', 'spades', 'spain', 'smother', 'soup', 'sparhawk', 'pisser', 'photo1', 'pebble', 'phones', 'peavey', 'picnic', 'pavement', 'terra', 'thistle', 'tokyo', 'therapy', 'lives', 'linden', 'kronos', 'lilbit', 'linux', 'johnston', 'material', 'melanie1', 'marbles', 'redlight', 'reno', 'recall', '1208', '1138', '1008', 'alchemy', 'aolsucks', 'alexalex', 'atticus', 'auditt', 'ballet', 'b929ezzh', 'goodyear', 'hanna', 'griffith', 'gubber', '863abgsg', '7474', '797979', '464646', '543210', '4zqauf', '4949', 'ch5nmk', 'carlito', 'chewey', 'carebear', 'caleb', 'checkmat', 'cheddar', 'chachi', 'fever', 'forgetit', 'fine', 'forlife', 'giants1', 'gates', 'getit', 'gamble', 'gerhard', 'galileo', 'g3ujwg', 'ganja', 'rufus1', 'rushmore', 'scouts', 'discus', 'dudeman', 'olympus', 'oscars', 'osprey', 'madcow', 'locust', 'loyola', 'mammoth', 'proton', 'rabbit1', 'question', 'ptfe3xxp', 'pwxd5x', 'purple1', 'punkass', 'prophecy', 'uyxnyd', 'tyson1', 'aircraft', 'access99', 'abcabc', 'cocktail', 'colts', 'civilwar', 'cleveland', 'claudia1', 'contour', 'clement', 'dddddd1', 'cypher', 'denied', 'dapzu455', 'dagmar', 'daisydog', 'name', 'noles', 'butters', 'buford', 'hoochie', 'hotel', 'hoser', 'eddy', 'ellis', 'eldiablo', 'kingrich', 'mudvayne', 'motown', 'mp8o6d', 'wife', 'vipergts', 'italiano', 'innocent', '2055', '2211', 'beavers', 'bloke', 'blade1', 'yamato', 'zooropa', 'yqlgr667', '050505', 'zxcvbnm1', 'zw6syj', 'suckcock', 'tango1', 'swing', 'stern', 'stephens', 'swampy', 'susanna', 'tammie', '445566', '333666', '380zliki', 'sexpot', 'sexylady', 'sixtynin', 'sickboy', 'spiffy', 'sleeping', 'skylark', 'sparkles', 'slam', 'pintail', 'phreak', 'places', 'teller', 'timtim', 'tires', 'thighs', 'left', 'latex', 'llamas', 'letsdoit', 'lkjhg', 'landmark', 'letters', 'lizzard', 'marlins', 'marauder', 'metal1', 'manu', 'register', 'righton', '1127', 'alain', 'alcat', 'amigo', 'basebal1', 'azertyui', 'attract', 'azrael', 'hamper', 'gotenks', 'golfgti', 'gutter', 'hawkwind', 'h2slca', 'harman', 'grace1', '6chid8', '789654', 'canine', 'casio', 'cazzo', 'chamber', 'cbr900', 'cabrio', 'calypso', 'capetown', 'feline', 'flathead', 'fisherma', 'flipmode', 'fungus', 'goal', 'g9zns4', 'full', 'giggle', 'gabriel1', 'fuck123', 'saffron', 'dogmeat', 'dreamcas', 'dirtydog', 'dunlop', 'douche', 'dresden', 'dickdick', 'destiny1', 'pappy', 'oaktree', 'lydia', 'luft4', 'puta', 'prayer', 'ramada', 'trumpet1', 'vcradq', 'tulip', 'tracy71', 'tycoon', 'aaaaaaa1', 'conquest', 'click', 'chitown', 'corps', 'creepers', 'constant', 'couples', 'code', 'cornhole', 'danman', 'dada', 'density', 'd9ebk7', 'cummins', 'darth', 'cute', 'nash', 'nirvana1', 'nixon', 'norbert', 'nestle', 'brenda1', 'bonanza', 'bundy', 'buddies', 'hotspur', 'heavy', 'horror', 'hufmqw', 'electro', 'erasure', 'enough', 'elisabet', 'etvww4', 'ewyuza', 'eric1', 'kinder', 'kenken', 'kismet', 'klaatu', 'musician', 'milamber', 'willi', 'waiting', 'isacs155', 'igor', '1million', '1letmein', 'x35v8l', 'yogi', 'ywvxpz', 'xngwoj', 'zippy1', '020202', '****', 'stonewal', 'sweeney', 'story', 'sentry', 'sexsexsex', 'spence', 'sonysony', 'smirnoff', 'star12', 'solace', 'sledge', 'states', 'snyder', 'star1', 'paxton', 'pentagon', 'pkxe62', 'pilot1', 'pommes', 'paulpaul', 'plants', 'tical', 'tictac', 'toes', 'lighthou', 'lemans', 'kubrick', 'letmein22', 'letmesee', 'jys6wz', 'jonesy', 'jjjjjj1', 'jigga', 'joelle', 'mate', 'merchant', 'redstorm', 'riley1', 'rosa', 'relief', '14141414', '1126', 'allison1', 'badboy1', 'asthma', 'auggie', 'basement', 'hartley', 'hartford', 'hardwood', 'gumbo', '616913', '57np39', '56qhxs', '4mnveh', 'cake', 'forbes', 'fatluvr69', 'fqkw5m', 'fidelity', 'feathers', 'fresno', 'godiva', 'gecko', 'gladys', 'gibson1', 'gogators', 'fridge', 'general1', 'saxman', 'rowing', 'sammys', 'scotts', 'scout1', 'sasasa', 'samoht', 'dragon69', 'ducky', 'dragonball', 'driller', 'p3wqaw', 'nurse', 'papillon', 'oneone', 'openit', 'optimist', 'longshot', 'portia', 'rapier', 'pussy2', 'ralphie', 'tuxedo', 'ulrike', 'undertow', 'trenton', 'copenhag', 'come', 'delldell', 'culinary', 'deltas', 'mytime', 'nicky', 'nickie', 'noname', 'noles1', 'bucker', 'bopper', 'bullock', 'burnout', 'bryce', 'hedges', 'ibilltes', 'hihje863', 'hitter', 'ekim', 'espana', 'eatme69', 'elpaso', 'envelope', 'express1', 'eeeeee1', 'eatme1', 'karaoke', 'kara', 'mustang5', 'misses', 'wellingt', 'willem', 'waterski', 'webcam', 'jasons', 'infinite', 'iloveyou!', 'jakarta', 'belair', 'bigdad', 'beerme', 'yoshi', 'yinyang', 'zimmer', 'x24ik3', '063dyjuy', '0000007', 'ztmfcq', 'stopit', 'stooges', 'survival', 'stockton', 'symow8', 'strato', '2hot4u', 'ship', 'simons', 'skins', 'shakes', 'sex1', 'shield', 'snacks', 'softtail', 'slimed123', 'pizzaman', 'pipe', 'pitt', 'pathetic', 'pinto', 'tigercat', 'tonton', 'lager', 'lizzy', 'juju', 'john123', 'jennings', 'josiah', 'jesse1', 'jordon', 'jingles', 'martian', 'mario1', 'rootedit', 'rochard', 'redwine', 'requiem', 'riverrat', 'rats', '1117', '1014', '1205', 'althea', 'allie', 'amor', 'amiga', 'alpina', 'alert', 'atreides', 'banana1', 'bahamut', 'hart', 'golfman', 'happines', '7uftyx', '5432', '5353', '5151', '4747', 'byron', 'chatham', 'chadwick', 'cherie', 'foxfire', 'ffvdj474', 'freaked', 'foreskin', 'gayboy', 'gggggg1', 'glenda', 'gameover', 'glitter', 'funny1', 'scoobydoo', 'scroll', 'rudolph', 'saddle', 'saxophon', 'dingbat', 'digimon', 'omicron', 'parsons', 'ohio', 'panda1', 'loloxx', 'macintos', 'lululu', 'lollypop', 'racer1', 'queen1', 'qwertzui', 'prick', 'upnfmc', 'tyrant', 'trout1', '9skw5g', 'aceman', 'adelaide', 'acls2h', 'aaabbb', 'acapulco', 'aggie', 'comcast', 'craft', 'crissy', 'cloudy', 'cq2kph', 'custer', 'd6o8pm', 'cybersex', 'davecole', 'darian', 'crumbs', 'daisey', 'davedave', 'dasani', 'needle', 'mzepab', 'myporn', 'narnia', 'nineteen', 'booger1', 'bravo1', 'budgie', 'btnjey', 'highlander', 'hotel6', 'humbug', 'edwin', 'ewtosi', 'kristin1', 'kobe', 'knuckles', 'keith1', 'katarina', 'muff', 'muschi', 'montana1', 'wingchun', 'wiggle', 'whatthe', 'walking', 'watching', 'vette1', 'vols', 'virago', 'intj3a', 'ishmael', 'intern', 'jachin', 'illmatic', '199999', '2010', 'beck', 'blender', 'bigpenis', 'bengal', 'blue1234', 'your', 'zaqxsw', 'xray', 'xxxxxxx1', 'zebras', 'yanks', 'worlds', 'tadpole', 'stripes', 'svetlana', '3737', '4343', '3728', '4444444', '368ejhih', 'solar', 'sonne', 'smalls', 'sniffer', 'sonata', 'squirts', 'pitcher', 'playstation', 'pktmxr', 'pescator', 'points', 'texaco', 'lesbos', 'lilian', 'l8v53x', 'jo9k2jw2', 'jimbeam', 'josie', 'jimi', 'jupiter2', 'jurassic', 'marines1', 'maya', 'rocket1', 'ringer', '14725836', '12345679', '1219', '123098', '1233', 'alessand', 'althor', 'angelika', 'arch', 'armando', 'alpha123', 'basher', 'barefeet', 'balboa', 'bbbbb1', 'banks', 'badabing', 'harriet', 'gopack', 'golfnut', 'gsxr1000', 'gregory1', '766rglqy', '8520', '753159', '8dihc6', '69camaro', '666777', 'cheeba', 'chino', 'calendar', 'cheeky', 'camel1', 'fishcake', 'falling', 'flubber', 'giuseppe', 'gianni', 'gloves', 'gnasher23', 'frisbee', 'fuzzy1', 'fuzzball', 'sauce', 'save13tx', 'schatz', 'russell1', 'sandra1', 'scrotum', 'scumbag', 'sabre', 'samdog', 'dripping', 'dragon12', 'dragster', 'paige', 'orwell', 'mainland', 'lunatic', 'lonnie', 'lotion', 'maine', 'maddux', 'qn632o', 'poophead', 'rapper', 'porn4life', 'producer', 'rapunzel', 'tracks', 'velocity', 'vanessa1', 'ulrich', 'trueblue', 'vampire1', 'abacus', '902100', 'crispy', 'corky', 'crane', 'chooch', 'd6wnro', 'cutie', 'deal', 'dabulls', 'dehpye', 'navyseal', 'njqcw4', 'nownow', 'nigger1', 'nightowl', 'nonenone', 'nightmar', 'bustle', 'buddy2', 'boingo', 'bugman', 'bulletin', 'bosshog', 'bowie', 'hybrid', 'hillside', 'hilltop', 'hotlegs', 'honesty', 'hzze929b', 'hhhhh1', 'hellohel', 'eloise', 'evilone', 'edgewise', 'e5pftu', 'eded', 'embalmer', 'excalibur', 'elefant', 'kenzie', 'karl', 'karin', 'killah', 'kleenex', 'mouses', 'mounta1n', 'motors', 'mutley', 'muffdive', 'vivitron', 'winfield', 'wednesday', 'w00t88', 'iloveit', 'jarjar', 'incest', 'indycar', '17171717', '1664', '17011701', '222777', '2663', 'beelch', 'benben', 'yitbos', 'yyyyy1', 'yasmin', 'zapata', 'zzzzz1', 'stooge', 'tangerin', 'taztaz', 'stewart1', 'summer69', 'sweetness', 'system1', 'surveyor', 'stirling', '3qvqod', '3way', '456321', 'sizzle', 'simhrq', 'shrink', 'shawnee', 'someday', 'sparty', 'ssptx452', 'sphere', 'spark', 'slammed', 'sober', 'persian', 'peppers', 'ploppy', 'pn5jvw', 'poobear', 'pianos', 'plaster', 'testme', 'tiff', 'thriller', 'larissa', 'lennox', 'jewell', 'master12', 'messier', 'rockey', '1229', '1217', '1478', '1009', 'anastasi', 'almighty', 'amonra', 'aragon', 'argentin', 'albino', 'azazel', 'grinder', '6uldv8', '83y6pv', '8888888', '4tlved', '515051', 'carsten', 'changes', 'flanders', 'flyers88', 'ffffff1', 'firehawk', 'foreman', 'firedog', 'flashman', 'ggggg1', 'gerber', 'godspeed', 'galway', 'giveitup', 'funtimes', 'gohan', 'giveme', 'geryfe', 'frenchie', 'sayang', 'rudeboy', 'savanna', 'sandals', 'devine', 'dougal', 'drag0n', 'dga9la', 'disaster', 'desktop', 'only', 'onlyone', 'otter', 'pandas', 'mafia', 'lombard', 'luckys', 'lovejoy', 'lovelife', 'manders', 'product', 'qqh92r', 'qcmfd454', 'pork', 'radar1', 'punani', 'ptbdhw', 'turtles', 'undertaker', 'trs8f7', 'tramp', 'ugejvp', 'abba', '911turbo', 'acdc', 'abcd123', 'clever', 'corina', 'cristian', 'create', 'crash1', 'colony', 'crosby', 'delboy', 'daniele', 'davinci', 'daughter', 'notebook', 'niki', 'nitrox', 'borabora', 'bonzai', 'budd', 'brisbane', 'hotter', 'heeled', 'heroes', 'hooyah', 'hotgirl', 'i62gbq', 'horse1', 'hills', 'hpk2qc', 'epvjb6', 'echo', 'korean', 'kristie', 'mnbvc', 'mohammad', 'mind', 'mommy1', 'munster', 'wade', 'wiccan', 'wanted', 'jacket', '2369', 'bettyboo', 'blondy', 'bismark', 'beanbag', 'bjhgfi', 'blackice', 'yvtte545', 'ynot', 'yess', 'zlzfrh', 'wolvie', '007bond', '******', 'tailgate', 'tanya1', 'sxhq65', 'stinky1', '3234412', '3ki42x', 'seville', 'shimmer', 'sheryl', 'sienna', 'shitshit', 'skillet', 'seaman', 'sooners1', 'solaris', 'smartass', 'pastor', 'pasta', 'pedros', 'pennywis', 'pfloyd', 'tobydog', 'thetruth', 'lethal', 'letme1n', 'leland', 'jenifer', 'mario66', 'micky', 'rocky2', 'rewq', 'ripped', 'reindeer', '1128', '1207', '1104', '1432', 'aprilia', 'allstate', 'alyson', 'bagels', 'basic', 'baggies', 'barb', 'barrage', 'greatest', 'gomez', 'guru', 'guard', '72d5tn', '606060', '4wcqjn', 'caldwell', 'chance1', 'catalog', 'faust', 'film', 'flange', 'fran', 'fartman', 'geil', 'gbhcf2', 'fussball', 'glen', 'fuaqz4', 'gameboy', 'garnet', 'geneviev', 'rotary', 'seahawk', 'russel', 'saab', 'seal', 'samadams', 'devlt4', 'ditto', 'drevil', 'drinker', 'deuce', 'dipstick', 'donut', 'octopus', 'ottawa', 'losangel', 'loverman', 'porky', 'q9umoz', 'rapture', 'pump', 'pussy4me', 'university', 'triplex', 'ue8fpw', 'trent', 'trophy', 'turbos', 'troubles', 'agent', 'aaa340', 'churchil', 'crazyman', 'consult', 'creepy', 'craven', 'class', 'cutiepie', 'ddddd1', 'dejavu', 'cuxldv', 'nettie', 'nbvibt', 'nikon', 'niko', 'norwood', 'nascar1', 'nolan', 'bubba2', 'boobear', 'boogers', 'buff', 'bullwink', 'bully', 'bulldawg', 'horsemen', 'escalade', 'editor', 'eagle2', 'dynamic', 'ella', 'efyreg', 'edition', 'kidney', 'minnesot', 'mogwai', 'morrow', 'msnxbi', 'moonlight', 'mwq6qlzo', 'wars', 'werder', 'verygood', 'voodoo1', 'wheel', 'iiiiii1', '159951', '1624', '1911a1', '2244', 'bellagio', 'bedlam', 'belkin', 'bill1', 'woodrow', 'xirt2k', 'worship', '??????', 'tanaka', 'swift', 'susieq', 'sundown', 'sukebe', 'tales', 'swifty', '2fast4u', 'senate', 'sexe', 'sickness', 'shroom', 'shaun', 'seaweed', 'skeeter1', 'status', 'snicker', 'sorrow', 'spanky1', 'spook', 'patti', 'phaedrus', 'pilots', 'pinch', 'peddler', 'theo', 'thumper1', 'tessie', 'tiger7', 'tmjxn151', 'thematri', 'l2g7k3', 'letmeinn', 'lazy', 'jeffjeff', 'joan', 'johnmish', 'mantra', 'mariana', 'mike69', 'marshal', 'mart', 'mazda6', 'riptide', 'robots', 'rental', '1107', '1130', '142857', '11001001', '1134', 'armored', 'alvin', 'alec', 'allnight', 'alright', 'amatuers', 'bartok', 'attorney', 'astral', 'baboon', 'bahamas', 'balls1', 'bassoon', 'hcleeb', 'happyman', 'granite', 'graywolf', 'golf1', 'gomets', '8vjzus', '7890', '789123', '8uiazp', '5757', '474jdvff', '551scasi', '50cent', 'camaro1', 'cherry1', 'chemist', 'final', 'firenze', 'fishtank', 'farrell', 'freewill', 'glendale', 'frogfrog', 'gerhardt', 'ganesh', 'same', 'scirocco', 'devilman', 'doodles', 'dinger', 'okinawa', 'olympic', 'nursing', 'orpheus', 'ohmygod', 'paisley', 'pallmall', 'null', 'lounge', 'lunchbox', 'manhatta', 'mahalo', 'mandarin', 'qwqwqw', 'qguvyt', 'pxx3eftp', 'president', 'rambler', 'puzzle', 'poppy1', 'turk182', 'trotter', 'vdlxuc', 'trish', 'tugboat', 'valiant', 'tracie', 'uwrl7c', 'chris123', 'coaster', 'cmfnpu', 'decimal', 'debbie1', 'dandy', 'daedalus', 'dede', 'natasha1', 'nissan1', 'nancy123', 'nevermin', 'napalm', 'newcastle', 'boats', 'branden', 'britt', 'bonghit', 'hester', 'ibxnsm', 'hhhhhh1', 'holger', 'durham', 'edmonton', 'erwin', 'equinox', 'dvader', 'kimmy', 'knulla', 'mustafa', 'monsoon', 'mistral', 'morgana', 'monica1', 'mojave', 'month', 'monterey', 'mrbill', 'vkaxcs', 'victor1', 'wacker', 'wendell', 'violator', 'vfdhif', 'wilson1', 'wavpzt', 'verena', 'wildstar', 'winter99', 'iqzzt580', 'jarrod', 'imback', '1914', '19741974', '1monkey', '1q2w3e4r5t', '2500', '2255', 'blank', 'bigshow', 'bigbucks', 'blackcoc', 'zoomer', 'wtcacq', 'wobble', 'xmen', 'xjznq5', 'yesterda', 'yhwnqc', 'zzzxxx', 'streak', '393939', '2fchbg', 'skinhead', 'skilled', 'shakira', 'shaft', 'shadow12', 'seaside', 'sigrid', 'sinful', 'silicon', 'smk7366', 'snapshot', 'sniper1', 'soccer11', 'staff', 'slap', 'smutty', 'peepers', 'pleasant', 'plokij', 'pdiddy', 'pimpdaddy', 'thrust', 'terran', 'topaz', 'today1', 'lionhear', 'littlema', 'lauren1', 'lincoln1', 'lgnu9d', 'laughing', 'juneau', 'methos', 'medina', 'merlyn', 'rogue1', 'romulus', 'redshift', '1202', '1469', '12locked', 'arizona1', 'alfarome', 'al9agd', 'aol123', 'altec', 'apollo1', 'arse', 'baker1', 'bbb747', 'bach', 'axeman', 'astro1', 'hawthorn', 'goodfell', 'hawks1', 'gstring', 'hannes', '8543852', '868686', '4ng62t', '554uzpad', '5401', '567890', '5232', 'catfood', 'frame', 'flow', 'fire1', 'flipflop', 'fffff1', 'fozzie', 'fluff', 'garrison', 'fzappa', 'furious', 'round', 'rustydog', 'sandberg', 'scarab', 'satin', 'ruger', 'samsung1', 'destin', 'diablo2', 'dreamer1', 'detectiv', 'dominick', 'doqvq3', 'drywall', 'paladin1', 'papabear', 'offroad', 'panasonic', 'nyyankee', 'luetdi', 'qcfmtz', 'pyf8ah', 'puddles', 'privacy', 'rainer', 'pussyeat', 'ralph1', 'princeto', 'trivia', 'trewq', 'tri5a3', 'advent', '9898', 'agyvorc', 'clarkie', 'coach1', 'courier', 'contest', 'christo', 'corinna', 'chowder', 'concept', 'climbing', 'cyzkhw', 'davidb', 'dad2ownu', 'days', 'daredevi', 'de7mdf', 'nose', 'necklace', 'nazgul', 'booboo1', 'broad', 'bonzo', 'brenna', 'boot', 'butch1', 'huskers1', 'hgfdsa', 'hornyman', 'elmer', 'elektra', 'england1', 'elodie', 'kermit1', 'knife', 'kaboom', 'minute', 'modern', 'motherfucker', 'morten', 'mocha', 'monday1', 'morgoth', 'ward', 'weewee', 'weenie', 'walters', 'vorlon', 'website', 'wahoo', 'ilovegod', 'insider', 'jayman', '1911', '1dallas', '1900', '1ranger', '201jedlz', '2501', '1qaz', 'bertram', 'bignuts', 'bigbad', 'beebee', 'billows', 'belize', 'bebe', 'wvj5np', 'wu4etd', 'yamaha1', 'wrinkle5', 'zebra1', 'yankee1', 'zoomzoom', '09876543', '0311', '?????', 'stjabn', 'tainted', '3tmnej', 'shoot', 'skooter', 'skelter', 'sixteen', 'starlite', 'smack', 'spice1', 'stacey1', 'smithy', 'perrin', 'pollux', 'peternorth', 'pixie', 'paulina', 'piston', 'pick', 'poets', 'pine', 'toons', 'tooth', 'topspin', 'kugm7b', 'legends', 'jeepjeep', 'juliana', 'joystick', 'junkmail', 'jojojojo', 'jonboy', 'judge', 'midland', 'meteor', 'mccabe', 'matter', 'mayfair', 'meeting', 'merrill', 'raul', 'riches', 'reznor', 'rockrock', 'reboot', 'reject', 'robyn', 'renee1', 'roadway', 'rasta220', '1411', '1478963', '1019', 'archery', 'allman', 'andyandy', 'barks', 'bagpuss', 'auckland', 'gooseman', 'hazmat', 'gucci', 'guns', 'grammy', 'happydog', 'greek', '7kbe9d', '7676', '6bjvpe', '5lyedn', '5858', '5291', 'charlie2', 'chas', 'c7lrwu', 'candys', 'chateau', 'ccccc1', 'cardinals', 'fear', 'fihdfv', 'fortune12', 'gocats', 'gaelic', 'fwsadn', 'godboy', 'gldmeo', 'fx3tuo', 'fubar1', 'garland', 'generals', 'gforce', 'rxmtkp', 'rulz', 'sairam', 'dunhill', 'division', 'dogggg', 'detect', 'details', 'doll', 'drinks', 'ozlq6qwm', 'ov3ajy', 'lockout', 'makayla', 'macgyver', 'mallorca', 'loves', 'prima', 'pvjegu', 'qhxbij', 'raphael', 'prelude1', 'totoro', 'tusymo', 'trousers', 'tunnel', 'valeria', 'tulane', 'turtle1', 'tracy1', 'aerosmit', 'abbey1', 'address', 'clticic', 'clueless', 'cooper1', 'comets', 'collect', 'corbin', 'delpiero', 'derick', 'cyprus', 'dante1', 'dave1', 'nounours', 'neal', 'nexus6', 'nero', 'nogard', 'norfolk', 'brent1', 'booyah', 'bootleg', 'buckaroo', 'bulls23', 'bulls1', 'booper', 'heretic', 'icecube', 'hellno', 'hounds', 'honeydew', 'hooters1', 'hoes', 'howie', 'hevnm4', 'hugohugo', 'eighty', 'epson', 'evangeli', 'eeeee1', 'eyphed']

	module.exports = passwords;
};


modules['helpcenter/primary/models/drilldown'] = function(require, exports, module, modules) {
module.exports = Backbone.Model.extend({
    idAttribute: 'id',

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @param  {Object}
     * @param  {Object}
     */
    initialize: function(models, options) {
      this.fieldId = options.fieldId;
      this.endpoint = options.endpoint;
      this.count = options.count;
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @return {Obejct}
     */
    url: function() {
        if (this.count == 1) {
            return _Payload.portalpath + 'api/v1/' + this.endpoint  + '/fields/' + this.fieldId + '?include=*';
        } else {
            return _Payload.portalpath + 'api/v1/' + this.endpoint  + '/fields/?include=*&ids=' + this.fieldId;
        }

    }
});
};


modules['helpcenter/primary/views/survey'] = function(require, exports, module, modules) {
module.exports = Backbone.View.extend({
    el: $('#survey'),

    events: {
        'change .rating--input': 'change'
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Displays good/bad message
     */
    change: function() {
        if ($('#rating--good').is(':checked')) {
            $('.survey-title--bad').removeClass('is-visible');
            $('.survey-title--good').addClass('is-visible');
        } 
        else if ($('#rating--bad').is(':checked')) {
            $('.survey-title--good').removeClass('is-visible');
            $('.survey-title--bad').addClass('is-visible');
        }
    }
});

};


modules['helpcenter/primary/plugins/froala/fullscreen'] = function(require, exports, module, modules) {
/*!
 * froala_editor v2.3.4 (https://www.froala.com/wysiwyg-editor)
 * License https://froala.com/wysiwyg-editor/terms/
 * Copyright 2014-2016 Froala Labs
 */

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof module === 'object' && module.exports) {
        // Node/CommonJS
        module.exports = function( root, jQuery ) {
            if ( jQuery === undefined ) {
                // require('jQuery') returns a factory that requires window to
                // build a jQuery instance, we normalize how we use modules
                // that require this pattern but the window provided is a noop
                // if it's defined (how jquery works)
                if ( typeof window !== 'undefined' ) {
                    jQuery = require('jquery');
                }
                else {
                    jQuery = require('jquery')(root);
                }
            }
            factory(jQuery);
            return jQuery;
        };
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {

  'use strict';

  $.FE.PLUGINS.fullscreen = function (editor) {
    var old_scroll;

    /**
     * Check if fullscreen mode is active.
     */
    function isActive () {
      return editor.$box.hasClass('fr-fullscreen');
    }

    /**
     * Turn fullscreen on.
     */
    var $placeholder;
    var height;
    var max_height;
    function _on () {
      old_scroll = $(editor.o_win).scrollTop();
      editor.$box.toggleClass('fr-fullscreen');
      $('body').toggleClass('fr-fullscreen');
      $placeholder = $('<div style="display: none;"></div>');
      editor.$box.after($placeholder);

      if (editor.helpers.isMobile()) {
        editor.$tb.data('parent', editor.$tb.parent());
        editor.$tb.prependTo(editor.$box);
        if (editor.$tb.data('sticky-dummy')) {
          editor.$tb.after(editor.$tb.data('sticky-dummy'));
        }
      }

      height = editor.opts.height;
      max_height = editor.opts.heightMax;

      editor.opts.height = editor.o_win.innerHeight - (editor.opts.toolbarInline ? 0 : editor.$tb.outerHeight());
      editor.opts.heightMax = null;
      editor.size.refresh();

      if (editor.opts.toolbarInline) editor.toolbar.showInline();

      var $parent_node = editor.$box.parent();
      while (!$parent_node.is('body')) {
        // @modified: Added position:relative
        $parent_node
          .data('z-index', $parent_node.css('z-index'))
          .css({'position': 'relative', 'z-index': '9990'});
        $parent_node = $parent_node.parent();
      }

      editor.events.trigger('charCounter.update');
      editor.$win.trigger('scroll');
    }

    /**
     * Turn fullscreen off.
     */
    function _off () {
      editor.$box.toggleClass('fr-fullscreen');
      $('body').toggleClass('fr-fullscreen');

      editor.$tb.prependTo(editor.$tb.data('parent'));
      if (editor.$tb.data('sticky-dummy')) {
        editor.$tb.after(editor.$tb.data('sticky-dummy'));
      }

      editor.opts.height = height;
      editor.opts.heightMax = max_height;
      editor.size.refresh();

      $(editor.o_win).scrollTop(old_scroll)

      if (editor.opts.toolbarInline) editor.toolbar.showInline();

      editor.events.trigger('charCounter.update');

      if (editor.opts.toolbarSticky) {
        if (editor.opts.toolbarStickyOffset) {
          if (editor.opts.toolbarBottom) {
            editor.$tb
              .css('bottom', editor.opts.toolbarStickyOffset)
              .data('bottom', editor.opts.toolbarStickyOffset);
          }
          else {
            editor.$tb
              .css('top', editor.opts.toolbarStickyOffset)
              .data('top', editor.opts.toolbarStickyOffset);
          }
        }
      }

      var $parent_node = editor.$box.parent();
      while (!$parent_node.is('body')) {
        if ($parent_node.data('z-index')) {
          $parent_node.css('z-index', '');
          if ($parent_node.css('z-index') != $parent_node.data('z-index')) {
            $parent_node.css('z-index', $parent_node.data('z-index'));
          }
          $parent_node.removeData('z-index');
        }

        $parent_node = $parent_node.parent();
      }

      editor.$win.trigger('scroll');
    }

    /**
     * Exec fullscreen.
     */
    function toggle () {
      if (!isActive()) {
        _on();
      }
      else {
        _off();
      }

      refresh(editor.$tb.find('.fr-command[data-cmd="fullscreen"]'));
    }

    function refresh ($btn) {
      var active = isActive();

      $btn.toggleClass('fr-active', active);
      $btn.find('> *').replaceWith(!active ? editor.icon.create('fullscreen') : editor.icon.create('fullscreenCompress'));
    }

    function _init () {
      if (!editor.$wp) return false;

      editor.events.$on($(editor.o_win), 'resize', function () {
        if (isActive()) {
          _off();
          _on();
        }
      });

      editor.events.on('toolbar.hide', function () {
        if (isActive() && editor.helpers.isMobile()) return false;
      })
    }

    return {
      _init: _init,
      toggle: toggle,
      refresh: refresh,
      isActive: isActive
    }
  }

  // Register the font size command.
  $.FE.RegisterCommand('fullscreen', {
    title: 'Fullscreen',
    undo: false,
    focus: false,
    forcedRefresh: true,
    callback: function () {
      this.fullscreen.toggle();
    },
    refresh: function ($btn) {
      this.fullscreen.refresh($btn);
    },
    plugin: 'fullscreen'
  })

  // Add the font size icon.
  $.FE.DefineIcon('fullscreen', {
    NAME: 'expand'
  });
  $.FE.DefineIcon('fullscreenCompress', {
    NAME: 'compress'
  });

}));

};


modules['helpcenter/primary/services/counts'] = function(require, exports, module, modules) {
/**
 * @description Stores total count of search results in a global variable
 * @type {Number}
 */
var totalCount = 0

var counts = exports = module.exports = {}

counts.set = function  (count) {
	totalCount = count
}

counts.get = function  (count) {
	return totalCount
}
};


modules['helpcenter/primary/plugins/froala/paragraph_format'] = function(require, exports, module, modules) {
/*!
 * froala_editor v2.3.4 (https://www.froala.com/wysiwyg-editor)
 * License https://froala.com/wysiwyg-editor/terms/
 * Copyright 2014-2016 Froala Labs
 */

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof module === 'object' && module.exports) {
        // Node/CommonJS
        module.exports = function( root, jQuery ) {
            if ( jQuery === undefined ) {
                // require('jQuery') returns a factory that requires window to
                // build a jQuery instance, we normalize how we use modules
                // that require this pattern but the window provided is a noop
                // if it's defined (how jquery works)
                if ( typeof window !== 'undefined' ) {
                    jQuery = require('jquery');
                }
                else {
                    jQuery = require('jquery')(root);
                }
            }
            factory(jQuery);
            return jQuery;
        };
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {

  'use strict';

  $.extend($.FE.DEFAULTS, {
    paragraphFormat: {
      N: 'Normal',
      H1: 'Heading 1',
      H2: 'Heading 2',
      H3: 'Heading 3',
      H4: 'Heading 4',
      PRE: 'Code'
    },
    paragraphFormatSelection: false
  })

  $.FE.PLUGINS.paragraphFormat = function (editor) {

    /**
     * Style content inside LI when LI is selected.
     * This case happens only when the LI contains a nested list or when it has no block tag inside.
     */
    function _styleLiWithoutBlocks($li, val) {
      var defaultTag = editor.html.defaultTag();

      // If val is null or default tag already do nothing.
      if (val && val.toLowerCase() != defaultTag) {
        // Deal with nested lists.
        if ($li.find('ul, ol').length > 0) {
          var $el = $('<' + val + '>');
          $li.prepend($el);
          var node = editor.node.contents($li.get(0))[0];
          while (node && ['UL', 'OL'].indexOf(node.tagName) < 0) {
            var next_node = node.nextSibling;
            $el.append(node);
            node = next_node;
          }
        }

        // Wrap list content.
        else {
          $li.html('<' + val + '>' + $li.html() + '</' + val + '>');
        }
      }
    }

    /**
     * Style content inside LI.
     */
    function _styleLiWithBlocks($blk, val) {
      var defaultTag = editor.html.defaultTag();

      // Prepare a temp div.
      if (!val) val = 'div class="fr-temp-div" data-empty="true"';

      // In list we don't have P so just unwrap content.
      if (val.toLowerCase() == defaultTag) {
        $blk.replaceWith($blk.html());
      }

      // Replace the current block with the new one.
      else {
        $blk.replaceWith($('<' + val + '>').html($blk.html()));
      }
    }

    /**
     * Style content inside TD.
     */
    function _styleTdWithBlocks($blk, val) {
      var defaultTag = editor.html.defaultTag();

      // Prepare a temp div.
      if (!val) val = 'div class="fr-temp-div"' + (editor.node.isEmpty($blk.get(0), true) ? ' data-empty="true"' : '');

      // Return to the regular case. We don't use P inside TD/TH.
      if (val.toLowerCase() == defaultTag) {
        // If node is not empty, then add a BR.
        if (!editor.node.isEmpty($blk.get(0), true)) {
          $blk.append('<br/>');
        }

        $blk.replaceWith($blk.html());
      }

      // Replace with the new tag.
      else {
        $blk.replaceWith($('<' + val  + '>').html($blk.html()));
      }
    }

    /**
     * Basic style.
     */
    function _style($blk, val) {
      if (!val) val = 'div class="fr-temp-div"' + (editor.node.isEmpty($blk.get(0), true) ? ' data-empty="true"' : '');
      $blk.replaceWith($('<' + val  + ' ' + editor.node.attributes($blk.get(0)) + '>').html($blk.html()));
    }

    /**
     * Apply style.
     */
    function apply (val) {
      // Normal.
      if (val == 'N') val = editor.html.defaultTag();

      // Wrap.
      editor.selection.save();
      editor.html.wrap(true, true, true, true);
      editor.selection.restore();

      // Get blocks.
      var blocks = editor.selection.blocks();

      // Save selection to restore it later.
      editor.selection.save();

      editor.$el.find('pre').attr('skip', true);

      // Go through each block and apply style to it.
      for (var i = 0; i < blocks.length; i++) {
        if (blocks[i].tagName != val && !editor.node.isList(blocks[i])) {
          var $blk = $(blocks[i]);

          // Style the content inside LI when there is selection right in LI.
          if (blocks[i].tagName == 'LI') {
            _styleLiWithoutBlocks($blk, val);
          }

          // Style the content inside LI when we have other tag in LI.
          else if (blocks[i].parentNode.tagName == 'LI' && blocks[i]) {
            _styleLiWithBlocks($blk, val);
          }

          // Style the content inside TD/TH.
          else if (['TD', 'TH'].indexOf(blocks[i].parentNode.tagName) >= 0) {
            _styleTdWithBlocks($blk, val);
          }

          // Regular case.
          else {
            _style($blk, val);
          }
        }
      }

      // Join PRE together.
      editor.$el.find('pre:not([skip="true"]) + pre:not([skip="true"])').each(function () {
        $(this).prev().append('<br>' + $(this).html());
        $(this).remove();
      });
      editor.$el.find('pre').removeAttr('skip');

      // Unwrap temp divs.
      editor.html.unwrap();

      // Restore selection.
      editor.selection.restore();
    }

    function refreshOnShow($btn, $dropdown) {
      var blocks = editor.selection.blocks();

      if (blocks.length) {
        var blk = blocks[0];
        var tag = 'N';
        var default_tag = editor.html.defaultTag();
        if (blk.tagName.toLowerCase() != default_tag && blk != editor.$el.get(0)) {
          tag = blk.tagName;
        }

        $dropdown.find('.fr-command[data-param1="' + tag + '"]').addClass('fr-active');
      }
      else {
        $dropdown.find('.fr-command[data-param1="N"]').addClass('fr-active');
      }
    }

    function refresh ($btn) {
      if (editor.opts.paragraphFormatSelection) {
        var blocks = editor.selection.blocks();

        if (blocks.length) {
          var blk = blocks[0];
          var tag = 'N';
          var default_tag = editor.html.defaultTag();
          if (blk.tagName.toLowerCase() != default_tag && blk != editor.$el.get(0)) {
            tag = blk.tagName;
          }

          if (['LI', 'TD', 'TH'].indexOf(tag) >= 0) {
            tag = 'N';
          }

          $btn.find('> span').text(editor.opts.paragraphFormat[tag]);
        }
        else {
          $btn.find('> span').text(editor.opts.paragraphFormat.N);
        }
      }
    }

    return {
      apply: apply,
      refreshOnShow: refreshOnShow,
      refresh: refresh
    }
  }

  // Register the font size command.
  $.FE.RegisterCommand('paragraphFormat', {
    type: 'dropdown',
    displaySelection: function (editor) {
      return editor.opts.paragraphFormatSelection;
    },
    defaultSelection: 'Normal',
    displaySelectionWidth: 100,
    html: function () {
      var c = '<ul class="fr-dropdown-list">';
      var options =  this.opts.paragraphFormat;
      for (var val in options) {
        if (options.hasOwnProperty(val)) {
          var shortcut = this.shortcuts.get('paragraphFormat.' + val);
          if (shortcut) {
            shortcut = '<span class="fr-shortcut">' + shortcut + '</span>';
          }
          else {
            shortcut = '';
          }

          c += '<li><' + (val == 'N' ? this.html.defaultTag() || 'DIV' : val) + ' style="padding: 0 !important; margin: 0 !important;"><a class="fr-command" data-cmd="paragraphFormat" data-param1="' + val + '" title="' + this.language.translate(options[val]) + '">' + this.language.translate(options[val]) + '</a></' + (val == 'N' ? this.html.defaultTag() || 'DIV' : val) + '></li>';
        }
      }
      c += '</ul>';

      return c;
    },
    title: 'Paragraph Format',
    callback: function (cmd, val) {
      this.paragraphFormat.apply(val);
    },
    refresh: function ($btn) {
      this.paragraphFormat.refresh($btn);
    },
    refreshOnShow: function ($btn, $dropdown) {
      this.paragraphFormat.refreshOnShow($btn, $dropdown);
    },
    plugin: 'paragraphFormat'
  })

  // Add the font size icon.
  $.FE.DefineIcon('paragraphFormat', {
    NAME: 'paragraph'
  });

}));

};


modules['helpcenter/primary/data/timezones'] = function(require, exports, module, modules) {
var timezones = [
	{"value": "Pacific/Niue", "name": "(GMT-11:00) Niue"},
	{"value": "Pacific/Pago_Pago", "name": "(GMT-11:00) Pago Pago"},
	{"value": "Pacific/Honolulu", "name": "(GMT-10:00) Hawaii Time"},
	{"value": "Pacific/Rarotonga", "name": "(GMT-10:00) Rarotonga"},
	{"value": "Pacific/Tahiti", "name": "(GMT-10:00) Tahiti"},
	{"value": "Pacific/Marquesas", "name": "(GMT-09:30) Marquesas"},
	{"value": "America/Anchorage", "name": "(GMT-09:00) Alaska Time"},
	{"value": "Pacific/Gambier", "name": "(GMT-09:00) Gambier"},
	{"value": "America/Los_Angeles", "name": "(GMT-08:00) Pacific Time"},
	{"value": "America/Tijuana", "name": "(GMT-08:00) Pacific Time - Tijuana"},
	{"value": "America/Vancouver", "name": "(GMT-08:00) Pacific Time - Vancouver"},
	{"value": "America/Whitehorse", "name": "(GMT-08:00) Pacific Time - Whitehorse"},
	{"value": "Pacific/Pitcairn", "name": "(GMT-08:00) Pitcairn"},
	{"value": "America/Dawson_Creek", "name": "(GMT-07:00) Mountain Time - Dawson Creek"},
	{"value": "America/Denver", "name": "(GMT-07:00) Mountain Time"},
	{"value": "America/Edmonton", "name": "(GMT-07:00) Mountain Time - Edmonton"},
	{"value": "America/Hermosillo", "name": "(GMT-07:00) Mountain Time - Hermosillo"},
	{"value": "America/Mazatlan", "name": "(GMT-07:00) Mountain Time - Chihuahua, Mazatlan"},
	{"value": "America/Phoenix", "name": "(GMT-07:00) Mountain Time - Arizona"},
	{"value": "America/Yellowknife", "name": "(GMT-07:00) Mountain Time - Yellowknife"},
	{"value": "America/Belize", "name": "(GMT-06:00) Belize"},
	{"value": "America/Chicago", "name": "(GMT-06:00) Central Time"},
	{"value": "America/Costa_Rica", "name": "(GMT-06:00) Costa Rica"},
	{"value": "America/El_Salvador", "name": "(GMT-06:00) El Salvador"},
	{"value": "America/Guatemala", "name": "(GMT-06:00) Guatemala"},
	{"value": "America/Managua", "name": "(GMT-06:00) Managua"},
	{"value": "America/Mexico_City", "name": "(GMT-06:00) Central Time - Mexico City"},
	{"value": "America/Regina", "name": "(GMT-06:00) Central Time - Regina"},
	{"value": "America/Tegucigalpa", "name": "(GMT-06:00) Central Time - Tegucigalpa"},
	{"value": "America/Winnipeg", "name": "(GMT-06:00) Central Time - Winnipeg"},
	{"value": "Pacific/Galapagos", "name": "(GMT-06:00) Galapagos"},
	{"value": "America/Bogota", "name": "(GMT-05:00) Bogota"},
	{"value": "America/Cancun", "name": "(GMT-05:00) America Cancun"},
	{"value": "America/Cayman", "name": "(GMT-05:00) Cayman"},
	{"value": "America/Guayaquil", "name": "(GMT-05:00) Guayaquil"},
	{"value": "America/Havana", "name": "(GMT-05:00) Havana"},
	{"value": "America/Iqaluit", "name": "(GMT-05:00) Eastern Time - Iqaluit"},
	{"value": "America/Jamaica", "name": "(GMT-05:00) Jamaica"},
	{"value": "America/Lima", "name": "(GMT-05:00) Lima"},
	{"value": "America/Nassau", "name": "(GMT-05:00) Nassau"},
	{"value": "America/New_York", "name": "(GMT-05:00) Eastern Time"},
	{"value": "America/Panama", "name": "(GMT-05:00) Panama"},
	{"value": "America/Port-au-Prince", "name": "(GMT-05:00) Port-au-Prince"},
	{"value": "America/Rio_Branco", "name": "(GMT-05:00) Rio Branco"},
	{"value": "America/Toronto", "name": "(GMT-05:00) Eastern Time - Toronto"},
	{"value": "Pacific/Easter", "name": "(GMT-05:00) Easter Island"},
	{"value": "America/Caracas", "name": "(GMT-04:30) Caracas"},
	{"value": "America/Asuncion", "name": "(GMT-03:00) Asuncion"},
	{"value": "America/Barbados", "name": "(GMT-04:00) Barbados"},
	{"value": "America/Boa_Vista", "name": "(GMT-04:00) Boa Vista"},
	{"value": "America/Campo_Grande", "name": "(GMT-03:00) Campo Grande"},
	{"value": "America/Cuiaba", "name": "(GMT-03:00) Cuiaba"},
	{"value": "America/Curacao", "name": "(GMT-04:00) Curacao"},
	{"value": "America/Grand_Turk", "name": "(GMT-04:00) Grand Turk"},
	{"value": "America/Guyana", "name": "(GMT-04:00) Guyana"},
	{"value": "America/Halifax", "name": "(GMT-04:00) Atlantic Time - Halifax"},
	{"value": "America/La_Paz", "name": "(GMT-04:00) La Paz"},
	{"value": "America/Manaus", "name": "(GMT-04:00) Manaus"},
	{"value": "America/Martinique", "name": "(GMT-04:00) Martinique"},
	{"value": "America/Port_of_Spain", "name": "(GMT-04:00) Port of Spain"},
	{"value": "America/Porto_Velho", "name": "(GMT-04:00) Porto Velho"},
	{"value": "America/Puerto_Rico", "name": "(GMT-04:00) Puerto Rico"},
	{"value": "America/Santo_Domingo", "name": "(GMT-04:00) Santo Domingo"},
	{"value": "America/Thule", "name": "(GMT-04:00) Thule"},
	{"value": "Atlantic/Bermuda", "name": "(GMT-04:00) Bermuda"},
	{"value": "America/St_Johns", "name": "(GMT-03:30) Newfoundland Time - St. Johns"},
	{"value": "America/Araguaina", "name": "(GMT-03:00) Araguaina"},
	{"value": "America/Argentina/Buenos_Aires", "name": "(GMT-03:00) Buenos Aires"},
	{"value": "America/Bahia", "name": "(GMT-03:00) Salvador"},
	{"value": "America/Belem", "name": "(GMT-03:00) Belem"},
	{"value": "America/Cayenne", "name": "(GMT-03:00) Cayenne"},
	{"value": "America/Fortaleza", "name": "(GMT-03:00) Fortaleza"},
	{"value": "America/Godthab", "name": "(GMT-03:00) Godthab"},
	{"value": "America/Maceio", "name": "(GMT-03:00) Maceio"},
	{"value": "America/Miquelon", "name": "(GMT-03:00) Miquelon"},
	{"value": "America/Montevideo", "name": "(GMT-03:00) Montevideo"},
	{"value": "America/Paramaribo", "name": "(GMT-03:00) Paramaribo"},
	{"value": "America/Recife", "name": "(GMT-03:00) Recife"},
	{"value": "America/Santiago", "name": "(GMT-03:00) Santiago"},
	{"value": "America/Sao_Paulo", "name": "(GMT-02:00) Sao Paulo"},
	{"value": "Antarctica/Palmer", "name": "(GMT-03:00) Palmer"},
	{"value": "Antarctica/Rothera", "name": "(GMT-03:00) Rothera"},
	{"value": "Atlantic/Stanley", "name": "(GMT-03:00) Stanley"},
	{"value": "America/Noronha", "name": "(GMT-02:00) Noronha"},
	{"value": "Atlantic/South_Georgia", "name": "(GMT-02:00) South Georgia"},
	{"value": "America/Scoresbysund", "name": "(GMT-01:00) Scoresbysund"},
	{"value": "Atlantic/Azores", "name": "(GMT-01:00) Azores"},
	{"value": "Atlantic/Cape_Verde", "name": "(GMT-01:00) Cape Verde"},
	{"value": "Africa/Abidjan", "name": "(GMT+00:00) Abidjan"},
	{"value": "Africa/Accra", "name": "(GMT+00:00) Accra"},
	{"value": "Africa/Bissau", "name": "(GMT+00:00) Bissau"},
	{"value": "Africa/Casablanca", "name": "(GMT+00:00) Casablanca"},
	{"value": "Africa/El_Aaiun", "name": "(GMT+00:00) El Aaiun"},
	{"value": "Africa/Monrovia", "name": "(GMT+00:00) Monrovia"},
	{"value": "America/Danmarkshavn", "name": "(GMT+00:00) Danmarkshavn"},
	{"value": "Atlantic/Canary", "name": "(GMT+00:00) Canary Islands"},
	{"value": "Atlantic/Faroe", "name": "(GMT+00:00) Faeroe"},
	{"value": "Atlantic/Reykjavik", "name": "(GMT+00:00) Reykjavik"},
	{"value": "UTC", "name": "(GMT+00:00) UTC (no daylight saving)"},
	{"value": "Europe/Dublin", "name": "(GMT+00:00) Dublin"},
	{"value": "Europe/Lisbon", "name": "(GMT+00:00) Lisbon"},
	{"value": "Europe/London", "name": "(GMT+00:00) London"},
	{"value": "Africa/Algiers", "name": "(GMT+01:00) Algiers"},
	{"value": "Africa/Ceuta", "name": "(GMT+01:00) Ceuta"},
	{"value": "Africa/Lagos", "name": "(GMT+01:00) Lagos"},
	{"value": "Africa/Ndjamena", "name": "(GMT+01:00) Ndjamena"},
	{"value": "Africa/Tunis", "name": "(GMT+01:00) Tunis"},
	{"value": "Africa/Windhoek", "name": "(GMT+02:00) Windhoek"},
	{"value": "Europe/Amsterdam", "name": "(GMT+01:00) Amsterdam"},
	{"value": "Europe/Andorra", "name": "(GMT+01:00) Andorra"},
	{"value": "Europe/Belgrade", "name": "(GMT+01:00) Central European Time - Belgrade"},
	{"value": "Europe/Berlin", "name": "(GMT+01:00) Berlin"},
	{"value": "Europe/Brussels", "name": "(GMT+01:00) Brussels"},
	{"value": "Europe/Budapest", "name": "(GMT+01:00) Budapest"},
	{"value": "Europe/Copenhagen", "name": "(GMT+01:00) Copenhagen"},
	{"value": "Europe/Gibraltar", "name": "(GMT+01:00) Gibraltar"},
	{"value": "Europe/Luxembourg", "name": "(GMT+01:00) Luxembourg"},
	{"value": "Europe/Madrid", "name": "(GMT+01:00) Madrid"},
	{"value": "Europe/Malta", "name": "(GMT+01:00) Malta"},
	{"value": "Europe/Monaco", "name": "(GMT+01:00) Monaco"},
	{"value": "Europe/Oslo", "name": "(GMT+01:00) Oslo"},
	{"value": "Europe/Paris", "name": "(GMT+01:00) Paris"},
	{"value": "Europe/Prague", "name": "(GMT+01:00) Central European Time - Prague"},
	{"value": "Europe/Rome", "name": "(GMT+01:00) Rome"},
	{"value": "Europe/Stockholm", "name": "(GMT+01:00) Stockholm"},
	{"value": "Europe/Tirane", "name": "(GMT+01:00) Tirane"},
	{"value": "Europe/Vienna", "name": "(GMT+01:00) Vienna"},
	{"value": "Europe/Warsaw", "name": "(GMT+01:00) Warsaw"},
	{"value": "Europe/Zurich", "name": "(GMT+01:00) Zurich"},
	{"value": "Africa/Cairo", "name": "(GMT+02:00) Cairo"},
	{"value": "Africa/Johannesburg", "name": "(GMT+02:00) Johannesburg"},
	{"value": "Africa/Maputo", "name": "(GMT+02:00) Maputo"},
	{"value": "Africa/Tripoli", "name": "(GMT+02:00) Tripoli"},
	{"value": "Asia/Amman", "name": "(GMT+02:00) Amman"},
	{"value": "Asia/Beirut", "name": "(GMT+02:00) Beirut"},
	{"value": "Asia/Damascus", "name": "(GMT+02:00) Damascus"},
	{"value": "Asia/Gaza", "name": "(GMT+02:00) Gaza"},
	{"value": "Asia/Jerusalem", "name": "(GMT+02:00) Jerusalem"},
	{"value": "Asia/Nicosia", "name": "(GMT+02:00) Nicosia"},
	{"value": "Europe/Athens", "name": "(GMT+02:00) Athens"},
	{"value": "Europe/Bucharest", "name": "(GMT+02:00) Bucharest"},
	{"value": "Europe/Chisinau", "name": "(GMT+02:00) Chisinau"},
	{"value": "Europe/Helsinki", "name": "(GMT+02:00) Helsinki"},
	{"value": "Europe/Istanbul", "name": "(GMT+02:00) Istanbul"},
	{"value": "Europe/Kaliningrad", "name": "(GMT+02:00) Moscow-01 - Kaliningrad"},
	{"value": "Europe/Kiev", "name": "(GMT+02:00) Kiev"},
	{"value": "Europe/Riga", "name": "(GMT+02:00) Riga"},
	{"value": "Europe/Sofia", "name": "(GMT+02:00) Sofia"},
	{"value": "Europe/Tallinn", "name": "(GMT+02:00) Tallinn"},
	{"value": "Europe/Vilnius", "name": "(GMT+02:00) Vilnius"},
	{"value": "Africa/Khartoum", "name": "(GMT+03:00) Khartoum"},
	{"value": "Africa/Nairobi", "name": "(GMT+03:00) Nairobi"},
	{"value": "Antarctica/Syowa", "name": "(GMT+03:00) Syowa"},
	{"value": "Asia/Baghdad", "name": "(GMT+03:00) Baghdad"},
	{"value": "Asia/Qatar", "name": "(GMT+03:00) Qatar"},
	{"value": "Asia/Riyadh", "name": "(GMT+03:00) Riyadh"},
	{"value": "Europe/Minsk", "name": "(GMT+03:00) Minsk"},
	{"value": "Europe/Moscow", "name": "(GMT+03:00) Moscow+00 - Moscow"},
	{"value": "Asia/Tehran", "name": "(GMT+03:30) Tehran"},
	{"value": "Asia/Baku", "name": "(GMT+04:00) Baku"},
	{"value": "Asia/Dubai", "name": "(GMT+04:00) Dubai"},
	{"value": "Asia/Tbilisi", "name": "(GMT+04:00) Tbilisi"},
	{"value": "Asia/Yerevan", "name": "(GMT+04:00) Yerevan"},
	{"value": "Europe/Samara", "name": "(GMT+04:00) Moscow+01 - Samara"},
	{"value": "Indian/Mahe", "name": "(GMT+04:00) Mahe"},
	{"value": "Indian/Mauritius", "name": "(GMT+04:00) Mauritius"},
	{"value": "Indian/Reunion", "name": "(GMT+04:00) Reunion"},
	{"value": "Asia/Kabul", "name": "(GMT+04:30) Kabul"},
	{"value": "Antarctica/Mawson", "name": "(GMT+05:00) Mawson"},
	{"value": "Asia/Aqtau", "name": "(GMT+05:00) Aqtau"},
	{"value": "Asia/Aqtobe", "name": "(GMT+05:00) Aqtobe"},
	{"value": "Asia/Ashgabat", "name": "(GMT+05:00) Ashgabat"},
	{"value": "Asia/Dushanbe", "name": "(GMT+05:00) Dushanbe"},
	{"value": "Asia/Karachi", "name": "(GMT+05:00) Karachi"},
	{"value": "Asia/Tashkent", "name": "(GMT+05:00) Tashkent"},
	{"value": "Asia/Yekaterinburg", "name": "(GMT+05:00) Moscow+02 - Yekaterinburg"},
	{"value": "Indian/Kerguelen", "name": "(GMT+05:00) Kerguelen"},
	{"value": "Indian/Maldives", "name": "(GMT+05:00) Maldives"},
	{"value": "Asia/Kolkata", "name": "(GMT+05:30) India Standard Time"},
	{"value": "Asia/Colombo", "name": "(GMT+05:30) Colombo"},
	{"value": "Asia/Katmandu", "name": "(GMT+05:45) Katmandu"},
	{"value": "Antarctica/Vostok", "name": "(GMT+06:00) Vostok"},
	{"value": "Asia/Almaty", "name": "(GMT+06:00) Almaty"},
	{"value": "Asia/Bishkek", "name": "(GMT+06:00) Bishkek"},
	{"value": "Asia/Dhaka", "name": "(GMT+06:00) Dhaka"},
	{"value": "Asia/Omsk", "name": "(GMT+06:00) Moscow+03 - Omsk, Novosibirsk"},
	{"value": "Asia/Thimphu", "name": "(GMT+06:00) Thimphu"},
	{"value": "Indian/Chagos", "name": "(GMT+06:00) Chagos"},
	{"value": "Asia/Rangoon", "name": "(GMT+06:30) Rangoon"},
	{"value": "Indian/Cocos", "name": "(GMT+06:30) Cocos"},
	{"value": "Antarctica/Davis", "name": "(GMT+07:00) Davis"},
	{"value": "Asia/Bangkok", "name": "(GMT+07:00) Bangkok"},
	{"value": "Asia/Hovd", "name": "(GMT+07:00) Hovd"},
	{"value": "Asia/Jakarta", "name": "(GMT+07:00) Jakarta"},
	{"value": "Asia/Krasnoyarsk", "name": "(GMT+07:00) Moscow+04 - Krasnoyarsk"},
	{"value": "Asia/Saigon", "name": "(GMT+07:00) Hanoi"},
	{"value": "Indian/Christmas", "name": "(GMT+07:00) Christmas"},
	{"value": "Antarctica/Casey", "name": "(GMT+08:00) Casey"},
	{"value": "Asia/Brunei", "name": "(GMT+08:00) Brunei"},
	{"value": "Asia/Choibalsan", "name": "(GMT+08:00) Choibalsan"},
	{"value": "Asia/Hong_Kong", "name": "(GMT+08:00) Hong Kong"},
	{"value": "Asia/Irkutsk", "name": "(GMT+08:00) Moscow+05 - Irkutsk"},
	{"value": "Asia/Kuala_Lumpur", "name": "(GMT+08:00) Kuala Lumpur"},
	{"value": "Asia/Macau", "name": "(GMT+08:00) Macau"},
	{"value": "Asia/Makassar", "name": "(GMT+08:00) Makassar"},
	{"value": "Asia/Manila", "name": "(GMT+08:00) Manila"},
	{"value": "Asia/Shanghai", "name": "(GMT+08:00) China Time - Beijing"},
	{"value": "Asia/Singapore", "name": "(GMT+08:00) Singapore"},
	{"value": "Asia/Taipei", "name": "(GMT+08:00) Taipei"},
	{"value": "Asia/Ulaanbaatar", "name": "(GMT+08:00) Ulaanbaatar"},
	{"value": "Australia/Perth", "name": "(GMT+08:00) Western Time - Perth"},
	{"value": "Asia/Pyongyang", "name": "(GMT+08:30) Pyongyang"},
	{"value": "Asia/Dili", "name": "(GMT+09:00) Dili"},
	{"value": "Asia/Jayapura", "name": "(GMT+09:00) Jayapura"},
	{"value": "Asia/Seoul", "name": "(GMT+09:00) Seoul"},
	{"value": "Asia/Tokyo", "name": "(GMT+09:00) Tokyo"},
	{"value": "Asia/Yakutsk", "name": "(GMT+09:00) Moscow+06 - Yakutsk"},
	{"value": "Pacific/Palau", "name": "(GMT+09:00) Palau"},
	{"value": "Australia/Adelaide", "name": "(GMT+10:30) Central Time - Adelaide"},
	{"value": "Australia/Darwin", "name": "(GMT+09:30) Central Time - Darwin"},
	{"value": "Antarctica/DumontDUrville", "name": "(GMT+10:00) Dumont D'Urville"},
	{"value": "Asia/Magadan", "name": "(GMT+10:00) Moscow+08 - Magadan"},
	{"value": "Asia/Vladivostok", "name": "(GMT+10:00) Moscow+07 - Yuzhno-Sakhalinsk"},
	{"value": "Australia/Brisbane", "name": "(GMT+10:00) Eastern Time - Brisbane"},
	{"value": "Australia/Hobart", "name": "(GMT+11:00) Eastern Time - Hobart"},
	{"value": "Australia/Sydney", "name": "(GMT+11:00) Eastern Time - Melbourne, Sydney"},
	{"value": "Pacific/Chuuk", "name": "(GMT+10:00) Truk"},
	{"value": "Pacific/Guam", "name": "(GMT+10:00) Guam"},
	{"value": "Pacific/Port_Moresby", "name": "(GMT+10:00) Port Moresby"},
	{"value": "Pacific/Efate", "name": "(GMT+11:00) Efate"},
	{"value": "Pacific/Guadalcanal", "name": "(GMT+11:00) Guadalcanal"},
	{"value": "Pacific/Kosrae", "name": "(GMT+11:00) Kosrae"},
	{"value": "Pacific/Norfolk", "name": "(GMT+11:00) Norfolk"},
	{"value": "Pacific/Noumea", "name": "(GMT+11:00) Noumea"},
	{"value": "Pacific/Pohnpei", "name": "(GMT+11:00) Ponape"},
	{"value": "Asia/Kamchatka", "name": "(GMT+12:00) Moscow+09 - Petropavlovsk-Kamchatskiy"},
	{"value": "Pacific/Auckland", "name": "(GMT+13:00) Auckland"},
	{"value": "Pacific/Fiji", "name": "(GMT+12:00) Fiji"},
	{"value": "Pacific/Funafuti", "name": "(GMT+12:00) Funafuti"},
	{"value": "Pacific/Kwajalein", "name": "(GMT+12:00) Kwajalein"},
	{"value": "Pacific/Majuro", "name": "(GMT+12:00) Majuro"},
	{"value": "Pacific/Nauru", "name": "(GMT+12:00) Nauru"},
	{"value": "Pacific/Tarawa", "name": "(GMT+12:00) Tarawa"},
	{"value": "Pacific/Wake", "name": "(GMT+12:00) Wake"},
	{"value": "Pacific/Wallis", "name": "(GMT+12:00) Wallis"},
	{"value": "Pacific/Apia", "name": "(GMT+14:00) Apia"},
	{"value": "Pacific/Enderbury", "name": "(GMT+13:00) Enderbury"},
	{"value": "Pacific/Fakaofo", "name": "(GMT+13:00) Fakaofo"},
	{"value": "Pacific/Tongatapu", "name": "(GMT+13:00) Tongatapu"},
	{"value": "Pacific/Kiritimati", "name": "(GMT+14:00) Kiritimati"}
]

module.exports = timezones;
};


modules['helpcenter/primary/models/comment'] = function(require, exports, module, modules) {
module.exports = Backbone.Model.extend({
	update: false,

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @param  {Object}
     */
    initialize: function (options) {
    	this.articleId = options.articleId;
    	if (options.update) {
    		this.update = true;
    		this.commentId = options.commentId;
    	    this.idAttribute = 'commentId';
            this.moderate = '';
            if (options.publish) {
                this.moderate = '/publish';
            }
    	}
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @return {Object}
     */
    url: function() {
    	if (!this.update) {
    		return _Payload.portalpath + 'api/v1/articles/' + this.articleId + '/comments.json?include=*';
    	} else {
    		return _Payload.portalpath + 'api/v1/articles/' + this.articleId + '/comments/' + this.commentId + this.moderate + '.json?include=*';
    	}
    }
});
};


modules['helpcenter/primary/views/signin'] = function(require, exports, module, modules) {
module.exports = Backbone.View.extend({
	model: require('helpcenter/primary/models/session'),

	el: $('#login-container'),

	events: {
		'submit #signin': 'signin',
		'submit #otp': 'otp',
		'submit #change-password': 'changePassword'
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @author Bishwanath Jha <bishwanath.jha@kayako.com>
	 * @description Processes the signin  form
	 * @param  {Object} e
	 */
	signin: function(e) {
		e.preventDefault();
		var self = this;
		var email = $('#email').val();
		var password = $('#password').val();

		if (email !== '' && password !== '') {
			// Disable submit button and show loading animation
			$('#signin-submit').prop('disabled', true).addClass('button--loading');

			// Clear any existing cookie
			document.cookie = "novo_sessionid=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=" + window.location.hostname;

			// Send request
			self.model.fetch({
				headers: {
					'Authorization': 'Basic ' + self.safeBase64Encode(email + ':' + password),
					'X-CSRF': true,
					'X-Locale': _Payload.activelocale
				},
				success: function(model, response, xhr) {
					self.createSession(response.data.id, xhr.xhr.getResponseHeader('X-CSRF-Token'));
					self.validateRegistrationConsent(response.data.user);
				},
				error: function(model, xhr) {
					$('.alert-box--overlay').remove();

					// Password has expired
					if (xhr.status === 403 && xhr.responseJSON.errors[0].code === 'CREDENTIAL_EXPIRED') {
						self.renderPasswordForm(xhr, password, true);
					}
					// OTP is required
					else if (xhr.status === 403 && xhr.responseJSON.errors[0].code === 'OTP_EXPECTED') {
						// Remove login fields
						$('#login-title, #login-form, #login-footer').remove();

						// Show OTP form
						var otpTpl = _.template($('#tpl-otp').html());
						$('#login-container').append(otpTpl);
						$('#password').val(password);
						$('#auth_token').val(xhr.responseJSON.auth_token);
						$('#otp_code').focus();
					} else {
						// Hightlight input fields
						$('#email, #password').closest('.textfield__label').addClass('textfield__label--error');

						// Re-enable submit button
						$('#signin-submit').prop('disabled', false).removeClass('button--loading');

						// Show notification
						if (xhr.responseJSON.notifications || xhr.responseJSON.errors) {
							var tplAlertBar = _.template($('#tpl-alert').html());
							var alertBarHtml = tplAlertBar({notifications: xhr.responseJSON.notifications || xhr.responseJSON.errors });

							// Remove any existing alert bar
							$('.alert-box').remove();

							// Show error message
							$('#login-title').after(alertBarHtml);
						}
					}
				}
			});
		}
	},

  safeBase64Encode: function(str) {
		return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
			return String.fromCharCode('0x' + p1);
		}));
	},

	/**
	 * @author Bishwanath Jha <bishwanath.jha@kayako.com>
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Changes the password
	 * @param  {Object} e
	 */
	changePassword: function(e) {
		e.preventDefault();

		var self = this;
		var old_password = $('#old_password').val();
		var new_password = $('#new-password').val();
		var auth_token = $('#auth_token').val();

		if (auth_token === '' || old_password === '') {
			window.location.href = _Payload.url + 'login';
			return false;
		} else if (new_password !== '') {
			// Disable submit button and show loading animation
			$('#changepassword-submit').prop('disabled', true).addClass('button--loading');

			// Send password request
			$.ajax({
				url: _Payload.portalpath + 'api/v1/profile/password.json?include=*',
				type: 'PUT',
				data: { password: old_password, new_password: new_password },
				headers: {'X-Token': auth_token, 'X-CSRF': true},
				success: function(response, status, xhr) {
					self.createSession(response.session_id, xhr.getResponseHeader('X-CSRF-Token'));
					window.location.href = self.getReturnURL();
				},
				error: function(response) {
					// If we have received 401 which means AUTHENTICATION_FAILED,
					// token is in-valid or expire, redirect user to login page
					if (response.status === 401) {
						window.location.href = _Payload.url + 'login';
					} else {
						// Otherwise there is some error with password
						$('#new-password').closest('.textfield__label').addClass('textfield__label--error');

						// Enable submit button
						$('#changepassword-submit').prop('disabled', false).removeClass('button--loading');

						if (response.responseJSON.notifications || response.responseJSON.errors) {
							var tplAlertBar = _.template($('#tpl-alert').html());
							var alertBarHtml = tplAlertBar({ notifications: response.responseJSON.notifications || response.responseJSON.errors });

							// Remove any existing alert bar
							$('.alert-box').remove();

							// Show error
							$('#changepassword-title').after(alertBarHtml);
						}
					}
				}
			});
		}
	},

	/**
	 * @author Bishwanath Jha <bishwanath.jha@kayako.com>
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Processes OTP
	 * @param  {Object} e
	 */
	otp: function(e) {
		e.preventDefault();

		var self = this;
		var auth_token = $('#auth_token').val();
		var password = $('#password').val();
		var otp_code = $('#otp_code').val();

		if (auth_token === '' || password === '') {
			window.location.href = _Payload.url + 'login';
		} else if (otp_code !== '') {
			$('#otp-submit').prop('disabled', true).addClass('button--loading');

			self.model.fetch({
				headers: {
					'X-Token': auth_token,
					'X-OTP': otp_code,
					'X-CSRF': true,
					'X-Locale': _Payload.activelocale
				},
				success: function(model, response, xhr) {
					self.createSession(response.data.id, xhr.xhr.getResponseHeader('X-CSRF-Token'));
					self.validateRegistrationConsent(response.data.user);
				},
				error: function(model, xhr) {
					// Password has expired
					if (xhr.status === 403 && xhr.responseJSON.errors[0].code === 'CREDENTIAL_EXPIRED') {
						self.renderPasswordForm(xhr, password, false);
					} // Otherwise there are some error in data
					else {
						// Enable submit button
						$('#otp-submit').prop('disabled', false).removeClass('button--loading');
						$('#otp_code').closest('.textfield__label').addClass('textfield__label--error');
						if (xhr.responseJSON.notifications !== undefined) {
							// Show error notification
							var tpl_error = _.template($('#tpl-alert').html());
							var tpl_html = tpl_error({notifications: xhr.responseJSON.notifications});
							$('.alert-box').remove();
							$('#otp-title').after(tpl_html);
						}
					}
				}
			});
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Switches to Change password form
	 * @param  {Object} response
	 * @param  {String} password
	 */
	renderPasswordForm: function(response, password, noOTP) {
		var userType = response.getResponseHeader('X-User-Role-Type');
		var passwordTpl = _.template($('#tpl-changepassword').html());

		if (noOTP) {
			// Remove login fields
			$('#login-title, #login-form, #login-footer').remove();
		} else {
			// Remove OTP form
			$('#otp-container').remove();
		}

		// Render password template
		$('#login-container').append(passwordTpl({ userType: userType }));
		$('#auth_token').val(response.responseJSON.auth_token);
		$('#old_password').val(password);
	},

	/**
	 * @author Bishwanath Jha <bishwanath.jha@kayako.com>
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @param {String} sessionID
	 */
	createSession: function(sessionID, csrfToken) {
		// Set session cookie
		document.cookie = "novo_sessionid=" + sessionID + '; path=/; domain=' + window.location.hostname + '; secure=true';

		// Set CSRF code to localStorage
		if (typeof(Storage) !== 'undefined') {
		    localStorage.setItem('ko:core:csrf', '"' + csrfToken + '"');
		}
	},

	/**
	 * @author Loe Lobo <loe.lobo@crossover.com>
	 */
	validateRegistrationConsent: function(user) {
		var self = this;
		// If user is a customer we should ask for consent
		if (user.role.type === 'CUSTOMER') {
			$.ajax({
				url: _Payload.portalpath + 'api/v1/consent',
				type: 'GET',
				headers: {'X-CSRF-Token': localStorage.getItem('ko:core:csrf') ? localStorage.getItem('ko:core:csrf').replace(/\"/g, '') : '', 'Content-Type': 'application/json'},
				success: function(response, status, xhr) {
					var consent = $.grep(response.data, function(x){ return x.consent_type === 'REGISTRATION'; });
					if (response.data.length === 0 || consent.length === 0) {
						localStorage.setItem('ko:ask_register_consent', 'true');
					}
					
					// Redirect
					window.location.href = self.getReturnURL();
				},
				error: function(response) {
					// Redirect
					window.location.href = self.getReturnURL();	
				}
			});
		} else {
			window.location.href = self.getReturnURL();
		}
	},

	/**
	 * Returns URL to redirect to
	 * @author Andriy Lesyuk <andriy.lesyuk@kayako.com>
	 * @returns {string}
	 */
	getReturnURL: function() {
		var returnURL = this.getReferrer();
		if (returnURL === false) {
			return _Payload.url;
		} // May need to be removed, when PDM-8652 is implemented
		else if (returnURL.indexOf('oauth/') === 0) {
			return _Payload.path + returnURL;
		} else {
			return _Payload.url + returnURL;
		}
	},

	/**
	 * @author Bishwanath Jha <bishwanath.jha@kayako.com>
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Returns URL referrer
	 * @return vars
	 */
	getReferrer: function() {
		var returnUrl = false;
		var loginUrl = window.location.href;
		if (loginUrl.match('return=')) {
			returnUrl = loginUrl.split('return=/')[1];
			returnUrl = returnUrl.replace('&', '?');
		}

		return returnUrl;
	}
});

};


modules['helpcenter/primary/views/unsaved'] = function(require, exports, module, modules) {
module.exports = Backbone.View.extend({
    events: {
        'change .textfield, .textfield--heading, .file-input, .select': 'change',
        'click :input[type="submit"], .button--primary': 'submit'
    },

    unsaved: false,

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Show alert if there are unsaved changes
     */
    change: function() {
        unsaved = true;
        window.onbeforeunload = function () {
            if (unsaved) {
                return 'You have some unsaved edits.';
            }
        };
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Ignored alert if form is submitted
     */
    submit: function() {
        unsaved = false;
    }
});

};


modules['helpcenter/primary/views/manage/author'] = function(require, exports, module, modules) {
module.exports = Backbone.View.extend({
	model: require('helpcenter/primary/models/manage/author'),

	el: $('#aside--article'),
	timer: null,

	events: {
		'keyup #author'            : 'navigate',
		'keypress #author'         : 'fetch',
		'click .author-list__item' : 'set'
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Fetches users and generates dropdown list
	 * @param  {Object} e
	 */
	fetch: function(e) {
		// Do not trigger if enter is pressed
		if (e.keyCode !== 13) {
			var view = this;
			clearTimeout(this.timer);
			this.timer = setTimeout(function() {
				var name = $('#author').val().trim();
				if (name.length) {
					$('#author').addClass('textfield--loading');
				    new view.model({
					    'name' : name
					}).fetch({
					    success: function(response) {
					    	// Remove loading animation
					    	$('#author').removeClass('textfield--loading');

					    	if($('#author-list').length) {
					    		$('#author-list').empty();
					    	} else {
					    		$('#author-field').append($("<ul>", {id: "author-list", class: "author-list fadeInD"}));
					    	}
					    	var container = $('#author-list');
					    	var data = response.attributes.data;

					    	// Populate users' list
					    	if (data.length) {
					    		_.each(data, function (el, index) {
					    			container.append('<li class="author-list__item" data-id="' + el.id + '"><img class="avatar avatar--small u-floatleft u-mright" src="' + el.avatar + '/64">' + el.full_name + '</li>');
					    		});
					    		// Select the first item
					    		view.next();
					    		$(document).click(function() {
					    			view.remove();
					    		});
					    	} else {
					    		$('#author-list').append(_.template($('#tpl-noauthor').html()));
					    	}
					    },
					    error: function() {
					    	$('#author').removeClass('textfield--loading');
					    	$('#author-list').remove();
					    }
					});
				} else {
					// If input text is removed, hide list
					view.remove();
				}
			 }, 700);
		}
			
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Tiggers events on keyboard input 
	 * @param  {Object} e
	 */
	navigate: function(e) {
		switch (e.keyCode) {
			// up
			case 38:
			this.previous();
			break;
			// down
			case 40:
			this.next();
			break;
			// enter
			case 13:
			this.set();
			break;
			// del
			case 8:
			this.backspace(e);
			break;
			default:
			if (!$('#author').val().trim()) {
				$('#author-id').remove();
			}
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Selects next list item
	 */
	next: function() {
		var next = $('.author-list__item.is-selected').removeClass('is-selected').next('.author-list__item');
		if (next.length == 0) {
			next = $('#author-list').children('.author-list__item').first();
		}
		next.addClass('is-selected');
		this.scroll();
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Selects previous list item
	 */
	previous: function() {
		var prev = $('.author-list__item.is-selected').removeClass('is-selected').prev('.author-list__item');
		if (prev.length == 0) { 
			prev = $('#author-list').children().last();
		}
		prev.addClass('is-selected');
		this.scroll();
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Sets selected list item to input field
	 */
	set: function(e) {
		if (!$('#author-id').length) {
			$('<input>').attr({ type: 'hidden', id: 'author-id' }).insertBefore($('#author'));
		}
		if (e !== undefined) {
			// using mouse
			e.stopPropagation();
			$('#author').val($(e.target).text());
			$('#author-id').val($(e.target).data('id'));;
		} else {
			// using keyboard
			var selected = $('.author-list__item.is-selected');
			if (selected.length) {
				$('#author').val(selected.text());
				$('#author-id').val(selected.data('id'));;
			}
		}
		this.remove();
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Removes dropdown list
	 */
	remove: function() {
		$('#author-list').fadeOut(120, function() {
			$(this).remove();
		});
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Removes dropdown list if input is empty
	 */
	backspace: function(e) {
		if (!$('#author').val().trim()) {
			$('#author-id').remove();
			this.remove();
		} else {
			this.fetch(e);
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Auto-scrolls a long list if user navigates using keyboard
	 */
	scroll: function() {
		var selected = $('.author-list__item.is-selected');
		var list = $('#author-list');
		if (selected.length == 1) {
		    if (selected.position().top + selected.height() > list.height()) {
		        list.scrollTop(list.scrollTop() + selected.position().top + selected.height() - list.height());
		    } else if (selected.position().top < 0) {
		        list.scrollTop(list.scrollTop() + selected.position().top);
		    }
		}
	}
});
};


modules['helpcenter/primary/views/recaptcha'] = function(require, exports, module, modules) {
module.exports = Backbone.View.extend({

    events: {
        'submit #signup': 'validate',
        'submit #contact': 'validate'
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Checks if recaptcha is checked
     * @param  {Object} e
     */
    validate: function(e) {
        if ($('.g-recaptcha').length && grecaptcha.getResponse() == "") {
        	e.preventDefault();
        	$('label[for="captchas"]').addClass('textfield__label--error');
        	$('input[type="submit"]').prop('disabled', false);
        }
    }
});

};


modules['helpcenter/primary/plugins/froala/lists'] = function(require, exports, module, modules) {
/*!
 * froala_editor v2.3.4 (https://www.froala.com/wysiwyg-editor)
 * License https://froala.com/wysiwyg-editor/terms/
 * Copyright 2014-2016 Froala Labs
 */

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof module === 'object' && module.exports) {
        // Node/CommonJS
        module.exports = function( root, jQuery ) {
            if ( jQuery === undefined ) {
                // require('jQuery') returns a factory that requires window to
                // build a jQuery instance, we normalize how we use modules
                // that require this pattern but the window provided is a noop
                // if it's defined (how jquery works)
                if ( typeof window !== 'undefined' ) {
                    jQuery = require('jquery');
                }
                else {
                    jQuery = require('jquery')(root);
                }
            }
            factory(jQuery);
            return jQuery;
        };
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {

  'use strict';

  $.FE.PLUGINS.lists = function (editor) {
    function _openFlag(tag_name) {
      return '<span class="fr-open-' + tag_name.toLowerCase() + '"></span>';
    }

    function _closeFlag(tag_name) {
      return '<span class="fr-close-' + tag_name.toLowerCase() + '"></span>';
    }

    /**
     * Replace list type.
     */
    function _replace(blocks, tag_name) {
      var lists = [];
      for (var i = 0; i < blocks.length; i++) {
        var parent_node = blocks[i].parentNode;
        if (blocks[i].tagName == 'LI' && parent_node.tagName != tag_name && lists.indexOf(parent_node) < 0) {
          lists.push(parent_node);
        }
      }

      for (i = lists.length - 1; i >= 0; i--) {
        var $l = $(lists[i]);
        $l.replaceWith('<' + tag_name.toLowerCase() + '>' + $l.html() + '</' + tag_name.toLowerCase() + '>');
      }
    }

    /**
     * Format blocks.
     */
    function _format(blocks, tag_name) {
      _replace(blocks, tag_name);

      // Format those blocks that are not LI.
      var default_tag = editor.html.defaultTag();
      for (var i = 0; i < blocks.length; i++) {
        if (blocks[i].tagName != 'LI') {
          // Default tag.
          if (default_tag && blocks[i].tagName.toLowerCase() == default_tag) {
            $(blocks[i]).replaceWith('<' + tag_name + '><li' + editor.node.attributes(blocks[i]) + '>' + $(blocks[i]).html() + '</li></' + tag_name + '>');
          }
          else {
            $(blocks[i]).wrap('<' + tag_name + '><li></li></' + tag_name + '>');
          }
        }
      }

      editor.clean.lists();
    }

    /**
     * Unformat.
     */
    function _unformat(blocks) {
      var i;
      var j;

      // If there are LI that have parents selected, then remove them.
      for (i = blocks.length - 1; i >= 0; i--) {
        for (j = i - 1; j >= 0; j--) {
          if ($(blocks[j]).find(blocks[i]).length || blocks[j] == blocks[i]) {
            blocks.splice(i, 1);
            break;
          }
        }
      }

      // Unwrap remaining LI.
      var lists = [];
      for (i = 0; i < blocks.length; i++) {
        var $li = $(blocks[i]);
        var parent_node = blocks[i].parentNode;

        $li.before(_closeFlag(parent_node.tagName));

        // Nested case.
        if (parent_node.parentNode.tagName == 'LI') {
          $li.before(_closeFlag('LI'));
          $li.after(_openFlag('LI'));
        }
        else {
          // Append BR if the node is not empty.
          if (!editor.node.isEmpty($li.get(0), true) && $li.find(editor.html.blockTagsQuery()).length === 0) {
            $li.append('<br>');
          }
          $li.append(_openFlag('LI'));
          $li.prepend(_closeFlag('LI'));
        }

        $li.after(_openFlag(parent_node.tagName));

        // Nested case. We should look for an upper parent.
        if (parent_node.parentNode.tagName == 'LI') {
          parent_node = parent_node.parentNode.parentNode;
        }

        if (lists.indexOf(parent_node) < 0) {
          lists.push(parent_node);
        }
      }

      // Replace the open and close tags.
      for (i = 0; i < lists.length; i++) {
        var $l = $(lists[i]);
        var html = $l.html();
        html = html.replace(/<span class="fr-close-([a-z]*)"><\/span>/g, '</$1>');
        html = html.replace(/<span class="fr-open-([a-z]*)"><\/span>/g, '<$1>');
        $l.replaceWith(editor.node.openTagString($l.get(0)) + html + editor.node.closeTagString($l.get(0)));
      }

      // Clean empty lists.
      editor.$el.find('li:empty').remove();
      editor.$el.find('ul:empty, ol:empty').remove();

      editor.clean.lists();
      editor.html.wrap();
    }

    /**
     * Check if should unformat lists.
     */
    function _shouldUnformat(blocks, tag_name) {
      var do_unformat = true;
      for (var i = 0; i < blocks.length; i++) {
        // Something else than LI is selected.
        if (blocks[i].tagName != 'LI') {
          return false;
        }

        // There is a different kind of list selected. Replace is the appropiate action.
        if (blocks[i].parentNode.tagName != tag_name) {
          do_unformat = false;
        }
      }

      return do_unformat;
    }

    /**
     * Call the list actions.
     */
    function format(tag_name) {
      // Wrap.
      editor.selection.save();
      editor.html.wrap(true, true, true, true);
      editor.selection.restore();

      var blocks = editor.selection.blocks();

      // Normalize nodes by keeping the LI.
      // <li><h1>foo<h1></li> will return h1.
      for (var i = 0; i < blocks.length; i++) {
        if (blocks[i].tagName != 'LI' && blocks[i].parentNode.tagName == 'LI') {
          blocks[i] = blocks[i].parentNode;
        }
      }

      // Save selection so that we can play at wish.
      editor.selection.save();

      // Decide if to format or unformat list.
      if (_shouldUnformat(blocks, tag_name)) {
        _unformat(blocks);
      }
      else {
        _format(blocks, tag_name);
      }

      // Unwrap.
      editor.html.unwrap();

      // Restore the selection.
      editor.selection.restore();
    }

    /**
     * Refresh list buttons.
     */
    function refresh($btn, tag_name) {
      var $el = $(editor.selection.element());
      if ($el.get(0) != editor.$el.get(0)) {
        var li = $el.get(0);
        if (li.tagName != 'LI') {
          li = $el.parents('li').get(0);
        }

        if (li && li.parentNode.tagName == tag_name && editor.$el.get(0).contains(li.parentNode)) {
          $btn.addClass('fr-active');
        }
      }
    }

    /**
     * Indent selected list items.
     */
    function _indent (blocks) {
      editor.selection.save();
      for (var i = 0; i < blocks.length; i++) {
        // There should be a previous li.
        var prev_li = blocks[i].previousSibling;
        if (prev_li) {
          var nl = $(blocks[i]).find('> ul, > ol').get(0);

          // Current LI has a nested list.
          if (nl) {
            var $li = $('<li>').prependTo($(nl));
            var node = editor.node.contents(blocks[i])[0];
            while (node && !editor.node.isList(node)) {
              var tmp = node.nextSibling;
              $li.append(node);
              node = tmp;
            }

            $(prev_li).append($(nl));
            $(blocks[i]).remove();
          }
          else {
            var prev_nl = $(prev_li).find('> ul, > ol').get(0);
            if (prev_nl) {
              $(prev_nl).append($(blocks[i]));
            }
            else {
              var $new_nl = $('<' + blocks[i].parentNode.tagName + '>');
              $(prev_li).append($new_nl);
              $new_nl.append($(blocks[i]));
            }
          }
        }
      }

      editor.clean.lists();
      editor.selection.restore();
    }

    /**
     * Outdent selected list items.
     */
    function _outdent (blocks) {
      editor.selection.save();
      _unformat(blocks);
      editor.selection.restore();
    }

    /**
     * Hook into the indent/outdent events.
     */
    function _afterCommand (cmd) {
      if (cmd == 'indent' || cmd == 'outdent') {
        var do_indent = false;
        var blocks = editor.selection.blocks();
        var blks = [];
        for (var i = 0; i < blocks.length; i++) {
          if (blocks[i].tagName == 'LI') {
            do_indent = true;
            blks.push(blocks[i]);
          }
          else if (blocks[i].parentNode.tagName == 'LI') {
            do_indent = true;
            blks.push(blocks[i].parentNode);
          }
        }

        if (do_indent) {
          if (cmd == 'indent') _indent(blks);
          else _outdent(blks);
        }
      }
    }

    /**
     * Init.
     */
    function _init () {
      editor.events.on('commands.after', _afterCommand);

      // TAB key in lists.
      editor.events.on('keydown', function (e) {
        if (e.which == $.FE.KEYCODE.TAB) {
          var do_indent;
          var blocks = editor.selection.blocks();
          var blks = [];
          for (var i = 0; i < blocks.length; i++) {
            if (blocks[i].tagName == 'LI') {
              do_indent = true;
              blks.push(blocks[i]);
            }
            else if (blocks[i].parentNode.tagName == 'LI') {
              do_indent = true;
              blks.push(blocks[i].parentNode);
            }
          }

          if (do_indent) {
            e.preventDefault();
            e.stopPropagation();

            if (!e.shiftKey) _indent(blks);
            else _outdent(blks);

            return false;
          }
        }
      }, true);
    }

    return {
      _init: _init,
      format: format,
      refresh: refresh
    }
  }

  // Register the font size command.
  $.FE.RegisterCommand('formatUL', {
    title: 'Unordered List',
    refresh: function ($btn) {
      this.lists.refresh($btn, 'UL');
    },
    callback: function () {
      this.lists.format('UL');
    },
    plugin: 'lists'
  })

  // Register the font size command.
  $.FE.RegisterCommand('formatOL', {
    title: 'Ordered List',
    refresh: function ($btn) {
      this.lists.refresh($btn, 'OL');
    },
    callback: function () {
      this.lists.format('OL');
    },
    plugin: 'lists'
  })

  // Add the list icons.
  $.FE.DefineIcon('formatUL', {
    NAME: 'list-ul'
  });

  $.FE.DefineIcon('formatOL', {
    NAME: 'list-ol'
  });

}));

};


modules['helpcenter/primary/plugins/froala/font_family'] = function(require, exports, module, modules) {
/*!
 * froala_editor v2.3.4 (https://www.froala.com/wysiwyg-editor)
 * License https://froala.com/wysiwyg-editor/terms/
 * Copyright 2014-2016 Froala Labs
 */

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof module === 'object' && module.exports) {
        // Node/CommonJS
        module.exports = function( root, jQuery ) {
            if ( jQuery === undefined ) {
                // require('jQuery') returns a factory that requires window to
                // build a jQuery instance, we normalize how we use modules
                // that require this pattern but the window provided is a noop
                // if it's defined (how jquery works)
                if ( typeof window !== 'undefined' ) {
                    jQuery = require('jquery');
                }
                else {
                    jQuery = require('jquery')(root);
                }
            }
            factory(jQuery);
            return jQuery;
        };
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {

  'use strict';

  $.extend($.FE.DEFAULTS, {
    fontFamily: {
      'Arial,Helvetica,sans-serif': 'Arial',
      'Georgia,serif': 'Georgia',
      'Impact,Charcoal,sans-serif': 'Impact',
      'Tahoma,Geneva,sans-serif': 'Tahoma',
      'Times New Roman,Times,serif': 'Times New Roman',
      'Verdana,Geneva,sans-serif': 'Verdana'
    },
    fontFamilySelection: false,
    fontFamilyDefaultSelection: 'Font Family'
  })

  $.FE.PLUGINS.fontFamily = function (editor) {
    function apply (val) {
      editor.format.applyStyle('font-family', val);
    }

    function refreshOnShow($btn, $dropdown) {
      $dropdown.find('.fr-command.fr-active').removeClass('fr-active');
      $dropdown.find('.fr-command[data-param1="' + _getSelection() + '"]').addClass('fr-active');

      var $list = $dropdown.find('.fr-dropdown-list');
      var $active = $dropdown.find('.fr-active').parent();
      if ($active.length) {
        $list.parent().scrollTop($active.offset().top - $list.offset().top - ($list.parent().outerHeight() / 2 - $active.outerHeight() / 2));
      }
      else {
        $list.parent().scrollTop(0);
      }
    }

    function _getArray (val) {
      var font_array = val.replace(/(sans-serif|serif|monospace|cursive|fantasy)/gi, '').replace(/"|'| /g, '').split(',');

      return $.grep(font_array, function (txt) { return txt.length > 0 });
    }

    /**
     * Return first match position.
     */
    function _matches (array1, array2) {
      for (var i = 0; i < array1.length; i++) {
        for (var j = 0; j < array2.length; j++) {
          if (array1[i] == array2[j]) {
            return [i, j];
          }
        }
      }

      return null;
    }

    function _getSelection () {
      var val = $(editor.selection.element()).css('font-family');
      var font_array = _getArray(val);

      var font_matches = [];
      for (var key in editor.opts.fontFamily) {
        if (editor.opts.fontFamily.hasOwnProperty(key)) {
          var c_font_array = _getArray(key);

          var match = _matches(font_array, c_font_array);
          if (match) {
            font_matches.push([key, match]);
          }
        }
      }

      if (font_matches.length === 0) return null;

      // Sort matches by their position.
      // Times,Arial should be detected as being Times, not Arial.
      font_matches.sort(function (a, b) {
        var f_diff = a[1][0] - b[1][0];
        if (f_diff === 0) {
          return a[1][1] - b[1][1];
        }
        else {
          return f_diff;
        }
      });

      return font_matches[0][0];
    }

    function refresh ($btn) {
      if (editor.opts.fontFamilySelection) {
        var val = $(editor.selection.element()).css('font-family').replace(/(sans-serif|serif|monospace|cursive|fantasy)/gi, '').replace(/"|'|/g, '').split(',');

        $btn.find('> span').text(editor.opts.fontFamily[_getSelection()] || val[0] || editor.opts.fontFamilyDefaultSelection);
      }
    }

    return {
      apply: apply,
      refreshOnShow: refreshOnShow,
      refresh: refresh
    }
  }

  // Register the font size command.
  $.FE.RegisterCommand('fontFamily', {
    type: 'dropdown',
    displaySelection: function (editor) {
      return editor.opts.fontFamilySelection;
    },
    defaultSelection: function (editor) {
      return editor.opts.fontFamilyDefaultSelection;
    },
    displaySelectionWidth: 120,
    html: function () {
      var c = '<ul class="fr-dropdown-list">';
      var options = this.opts.fontFamily;
      for (var val in options) {
        if (options.hasOwnProperty(val)) {
          c += '<li><a class="fr-command" data-cmd="fontFamily" data-param1="' + val + '" style="font-family: ' + val + '" title="' + options[val] + '">' + options[val] + '</a></li>';
        }
      }
      c += '</ul>';

      return c;
    },
    title: 'Font Family',
    callback: function (cmd, val) {
      this.fontFamily.apply(val);
    },
    refresh: function ($btn) {
      this.fontFamily.refresh($btn);
    },
    refreshOnShow: function ($btn, $dropdown) {
      this.fontFamily.refreshOnShow($btn, $dropdown);
    },
    plugin: 'fontFamily'
  })

  // Add the font size icon.
  $.FE.DefineIcon('fontFamily', {
    NAME: 'font'
  });

}));

};


modules['helpcenter/primary/plugins/froala/code_view'] = function(require, exports, module, modules) {
/*!
 * froala_editor v2.3.4 (https://www.froala.com/wysiwyg-editor)
 * License https://froala.com/wysiwyg-editor/terms/
 * Copyright 2014-2016 Froala Labs
 */

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof module === 'object' && module.exports) {
        // Node/CommonJS
        module.exports = function( root, jQuery ) {
            if ( jQuery === undefined ) {
                // require('jQuery') returns a factory that requires window to
                // build a jQuery instance, we normalize how we use modules
                // that require this pattern but the window provided is a noop
                // if it's defined (how jquery works)
                if ( typeof window !== 'undefined' ) {
                    jQuery = require('jquery');
                }
                else {
                    jQuery = require('jquery')(root);
                }
            }
            factory(jQuery);
            return jQuery;
        };
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {

  'use strict';

  $.extend($.FE.DEFAULTS, {
    codeMirror: true,
    codeMirrorOptions: {
      lineNumbers: true,
      tabMode: 'indent',
      indentWithTabs: true,
      lineWrapping: true,
      mode: 'text/html',
      tabSize: 2
    },
    codeBeautifierOptions: {
      end_with_newline: true,
      indent_inner_html: true,
      extra_liners: ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'pre', 'ul', 'ol', 'table', 'dl'],
      brace_style: 'expand',
      indent_char: '\t',
      indent_size: 1,
      wrap_line_length: 0
    }
  })

  $.FE.PLUGINS.codeView = function (editor) {
    var $html_area;
    var code_mirror;

    /**
     * Check if code view is enabled.
     */
    function isActive () {
      return editor.$box.hasClass('fr-code-view');
    }

    function get () {
      if (code_mirror) {
        return code_mirror.getValue();
      } else {
        return $html_area.val();
      }
    }

    /**
     * Get back to edit mode.
     */
    function _showText ($btn) {
      var html = get();

      // Code mirror enabled.
      editor.html.set(html);

      // Blur the element.
      editor.$el.blur();

      // Toolbar no longer disabled.
      editor.$tb.find(' > .fr-command').not($btn).removeClass('fr-disabled');
      $btn.removeClass('fr-active');

      editor.events.focus(true);
      editor.placeholder.refresh();

      editor.undo.saveStep();
    }

    /**
     * Get to code mode.
     */
    function _showHTML ($btn) {
      if (!$html_area) {
        _initArea();

        // Enable code mirror.
        if (!code_mirror && editor.opts.codeMirror && typeof CodeMirror != 'undefined') {
          code_mirror = CodeMirror.fromTextArea($html_area.get(0), editor.opts.codeMirrorOptions);
        }
        else {
          editor.events.$on($html_area, 'keydown keyup change input', function () {
            if (!editor.opts.height) {
              if (!this.rows) {
                this.rows = 1;
              }

              // Textarea has no content anymore.
              if (this.value.length === 0) {
                this.rows = 1;
              }

              else {
                this.style.height = 'auto';

                // Decrease height in case text is deleted.
                while (this.rows > 1 && this.scrollHeight <= this.offsetHeight) {
                  this.rows -= 1;
                }

                // Increase textarea height.
                while (this.scrollHeight > this.offsetHeight && (!editor.opts.heightMax || this.offsetHeight < editor.opts.heightMax)) {
                  this.rows += 1;
                }
              }
            }
            else {
              this.rows = null;
            }
          });
        }
      }

      editor.undo.saveStep();

      // Clean white tags but ignore selection.
      editor.html.cleanEmptyTags();
      editor.html.cleanWhiteTags(true);

      // Blur the element.
      if (editor.core.hasFocus()) {
        if (!editor.core.isEmpty()) {
          editor.selection.save();
          editor.$el.find('.fr-marker[data-type="true"]:first').replaceWith('<span class="fr-tmp fr-sm">F</span>');
          editor.$el.find('.fr-marker[data-type="false"]:last').replaceWith('<span class="fr-tmp fr-em">F</span>');
        }
      }

      // Get HTML.
      var html = editor.html.get(false, true);
      editor.$el.find('span.fr-tmp').remove();

      if (editor.core.hasFocus()) editor.$el.blur();

      html = html.replace(/<span class="fr-tmp fr-sm">F<\/span>/, 'FROALA-SM');
      html = html.replace(/<span class="fr-tmp fr-em">F<\/span>/, 'FROALA-EM');

      // Beautify HTML.
      if (editor.codeBeautifier) {
        html = editor.codeBeautifier.run(html, editor.opts.codeBeautifierOptions);
      }

      var s_index;
      var e_index;

      // Code mirror is enabled.
      if (code_mirror) {
        s_index = html.indexOf('FROALA-SM');
        e_index = html.indexOf('FROALA-EM');

        if (s_index > e_index) {
          s_index = e_index;
        }
        else {
          e_index = e_index - 9;
        }

        html = html.replace(/FROALA-SM/g, '').replace(/FROALA-EM/g, '')
        var s_line = html.substring(0, s_index).length - html.substring(0, s_index).replace(/\n/g, '').length;
        var e_line = html.substring(0, e_index).length - html.substring(0, e_index).replace(/\n/g, '').length;

        s_index = html.substring(0, s_index).length - html.substring(0, html.substring(0, s_index).lastIndexOf('\n') + 1).length;
        e_index = html.substring(0, e_index).length - html.substring(0, html.substring(0, e_index).lastIndexOf('\n')  + 1).length;

        code_mirror.setSize(null, editor.opts.height ? editor.opts.height : 'auto');
        if (editor.opts.heightMin) editor.$box.find('.CodeMirror-scroll').css('min-height', editor.opts.heightMin);
        code_mirror.setValue(html);
        code_mirror.focus();
        code_mirror.setSelection({ line: s_line, ch: s_index }, { line: e_line, ch: e_index })
        code_mirror.refresh();
        code_mirror.clearHistory();
      }

      // No code mirror.
      else {
        s_index = html.indexOf('FROALA-SM');
        e_index = html.indexOf('FROALA-EM') - 9;

        if (editor.opts.heightMin) {
          $html_area.css('min-height', editor.opts.heightMin);
        }

        if (editor.opts.height) {
          $html_area.css('height', editor.opts.height);
        }

        if (editor.opts.heightMax) {
          $html_area.css('max-height', editor.opts.height || editor.opts.heightMax);
        }

        $html_area.val(html.replace(/FROALA-SM/g, '').replace(/FROALA-EM/g, '')).trigger('change');
        var scroll_top = $(editor.o_doc).scrollTop();
        $html_area.focus();
        $html_area.get(0).setSelectionRange(s_index, e_index);
        $(editor.o_doc).scrollTop(scroll_top);
      }

      // Disable buttons.
      editor.$tb.find(' > .fr-command').not($btn).addClass('fr-disabled');
      $btn.addClass('fr-active');

      if (!editor.helpers.isMobile() && editor.opts.toolbarInline) {
        editor.toolbar.hide();
      }
    }

    /**
     * Toggle the code view.
     */
    function toggle (val) {
      if (typeof val == 'undefined') val = !isActive();

      var $btn = editor.$tb.find('.fr-command[data-cmd="html"]');

      if (!val) {
        editor.$box.toggleClass('fr-code-view', false);
        _showText($btn);
      } else {
        editor.popups.hideAll();
        editor.$box.toggleClass('fr-code-view', true);
        _showHTML($btn);
      }
    }

    /**
     * Destroy.
     */
    function _destroy () {
      if (isActive()) {
        toggle(editor.$tb.find('button[data-cmd="html"]'));
      }

      if (code_mirror) code_mirror.toTextArea();
      $html_area.val('').removeData().remove();
      $html_area = null;

      if ($back_button) {
        $back_button.remove();
        $back_button = null;
      }
    }

    function _initArea () {
      // Add the coding textarea to the wrapper.
      $html_area = $('<textarea class="fr-code" tabindex="-1">');
      editor.$wp.append($html_area);

      $html_area.attr('dir', editor.opts.direction);

      // Exit code view button for inline toolbar.
      if (!editor.$box.hasClass('fr-basic')) {
        $back_button = $('<a data-cmd="html" title="Code View" class="fr-command fr-btn html-switch' + (editor.helpers.isMobile() ? '' : ' fr-desktop') + '" role="button" tabindex="-1"><i class="fa fa-code"></i></button>');
        editor.$box.append($back_button);

        editor.events.bindClick(editor.$box, 'a.html-switch', function () {
          toggle(false);
        });
      }

      var cancel = function () {
        return !isActive();
      }

      // Disable refresh of the buttons while enabled.
      editor.events.on('buttons.refresh', cancel);
      editor.events.on('copy', cancel, true);
      editor.events.on('cut', cancel, true);
      editor.events.on('paste', cancel, true);

      editor.events.on('destroy', _destroy, true);

      editor.events.on('html.set', function () {
        if (isActive()) toggle(true);
      });

      editor.events.on('form.submit', function () {
        if (isActive()) {
          // Code mirror enabled.
          editor.html.set(get());

          editor.events.trigger('contentChanged', [], true);
        }
      }, true);
    }

    /**
     * Initialize.
     */
    var $back_button;
    function _init () {
      if (!editor.$wp) return false;
    }

    return {
      _init: _init,
      toggle: toggle,
      isActive: isActive,
      get: get
    }
  };

  $.FE.RegisterCommand('html', {
    title: 'Code View',
    undo: false,
    focus: false,
    forcedRefresh: true,
    callback: function () {
      this.codeView.toggle();
    },
    plugin: 'codeView'
  })

  $.FE.DefineIcon('html', {
    NAME: 'code'
  });

}));

};


modules['helpcenter/primary/views/password'] = function(require, exports, module, modules) {
var passwords = require('helpcenter/primary/data/passwords');
module.exports = Backbone.View.extend({

	events: {
		'keyup #new-password'    : 'validate',
		'click .js-set-password' : 'isWeak'
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Validates the password according to password policy
	 */
	validate: function(e) {
		if ($('#user_type').val()) {
			var usertype = $('#user_type').val() + '-';
		} else {
			var usertype = '';
		}
		var password = $('#new-password').val();
		var length = $('#' + usertype + 'policy-minchars').data('value');
		var number = $('#' + usertype + 'policy-number').data('value');
		var uppercase = $('#' + usertype + 'policy-uppercase').data('value');
		var splChar = $('#' + usertype + 'policy-splchar').data('value');
		var maxConsecutive = $('#' + usertype + 'policy-maximumconsecutive').data('value');
		var validateLength = true;
		var validateNumber = true;
		var validateCase = true;
		var validateSplChar = true;
		var validateConsecutive = true;

		// Hide alert bar if exists
		if ($('.js-alert').is(':visible') && e.keyCode != 13) {
			$('.js-alert').fadeOut(150);
		}

		// Validate length
		if (length) {
			if (password.length >= length) {
				$('#' + usertype + 'policy-minchars').addClass('has-met');
			} else {
				$('#' + usertype + 'policy-minchars').removeClass('has-met');
				validateLength = false;
			}
		}

		// Validate number
		if (number) {
			if ((password.match(/\d/g) || []).length >= number) {
			    $('#' + usertype + 'policy-number').addClass('has-met');
			} else {
			    $('#' + usertype + 'policy-number').removeClass('has-met');
			    validateNumber = false;
			}
		}

		// Validate uppercase
		if (uppercase) {
			if ((password.match(/[A-Z]/g) || []).length >= uppercase) {
			    $('#' + usertype + 'policy-uppercase').addClass('has-met');
			} else {
			    $('#' + usertype + 'policy-uppercase').removeClass('has-met');
			    validateCase = false;
			}
		}

		// Validate special characters
		if (splChar) {
			if ((password.match(/[$-/:-?{-~!"^_#`@\[\]]/g) || []).length >= splChar) {
			    $('#' + usertype + 'policy-splchar').addClass('has-met');
			} else {
			    $('#' + usertype + 'policy-splchar').removeClass('has-met');
			    validateSplChar = false;
			}
		}

		// Validate consecutive characters
		if (maxConsecutive) {
			var pattern = new RegExp('(.)\\1{'+(maxConsecutive)+'}?(?!\\1)',"g");
			var input = password.match(pattern);
			if (input && input.length) {
				$('#' + usertype + 'policy-maximumconsecutive').removeClass('has-met');
				validateConsecutive = false;
			} else {
				$('#' + usertype + 'policy-maximumconsecutive').addClass('has-met');
			}
		}

		// Enable/disable the submit button
		if (validateLength && validateNumber && validateCase && validateSplChar && validateConsecutive) {
			$('input[type="submit"]').removeClass('is-disabled');
		} else {
			$('input[type="submit"]').addClass('is-disabled');
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Shows error notification if chosen passowrd is weak
	 * @param {Object} e
	 */
	isWeak: function(e) {
		var newPassword = $('#new-password').val();
		for (var i = 0; i <= passwords.length - 1; i++) {
			if (newPassword == passwords[i]) {
				e.preventDefault();
				$('.js-alert').show();
				break;
			}
		}
	}
});
};


modules['backbone'] = function(require, exports, module, modules) {
//     Backbone.js 1.2.1

//     (c) 2010-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
//     Backbone may be freely distributed under the MIT license.
//     For all details and documentation:
//     http://backbonejs.org

(function(factory) {

	// Establish the root object, `window` (`self`) in the browser, or `global` on the server.
	// We use `self` instead of `window` for `WebWorker` support.
	var root = (typeof self == 'object' && self.self == self && self) ||
		(typeof global == 'object' && global.global == global && global);

	// Set up Backbone appropriately for the environment. Start with AMD.
	if (typeof define === 'function' && define.amd) {
		define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
			// Export global even in AMD case in case this script is loaded with
			// others that may still expect a global Backbone.
			root.Backbone = factory(root, exports, _, $);
		});

		// Next for Node.js or CommonJS. jQuery may not be needed as a module.
	} else if (typeof exports !== 'undefined') {
		var _ = require('underscore'), $;
		try { $ = require('jquery'); } catch(e) {}
		factory(root, exports, _, $);

		// Finally, as a browser global.
	} else {
		root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$));
	}

}(function(root, Backbone, _, $) {

	// Initial Setup
	// -------------

	// Save the previous value of the `Backbone` variable, so that it can be
	// restored later on, if `noConflict` is used.
	var previousBackbone = root.Backbone;

	// Create a local reference to a common array method we'll want to use later.
	var slice = [].slice;

	// Current version of the library. Keep in sync with `package.json`.
	Backbone.VERSION = '1.2.1';

	// For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
	// the `$` variable.
	Backbone.$ = $;

	// Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
	// to its previous owner. Returns a reference to this Backbone object.
	Backbone.noConflict = function() {
		root.Backbone = previousBackbone;
		return this;
	};

	// Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option
	// will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and
	// set a `X-Http-Method-Override` header.
	Backbone.emulateHTTP = false;

	// Turn on `emulateJSON` to support legacy servers that can't deal with direct
	// `application/json` requests ... this will encode the body as
	// `application/x-www-form-urlencoded` instead and will send the model in a
	// form param named `model`.
	Backbone.emulateJSON = false;

	// Proxy Underscore methods to a Backbone class' prototype using a
	// particular attribute as the data argument
	var addMethod = function(length, method, attribute) {
		switch (length) {
			case 1: return function() {
				return _[method](this[attribute]);
			};
			case 2: return function(value) {
				return _[method](this[attribute], value);
			};
			case 3: return function(iteratee, context) {
				return _[method](this[attribute], iteratee, context);
			};
			case 4: return function(iteratee, defaultVal, context) {
				return _[method](this[attribute], iteratee, defaultVal, context);
			};
			default: return function() {
				var args = slice.call(arguments);
				args.unshift(this[attribute]);
				return _[method].apply(_, args);
			};
		}
	};
	var addUnderscoreMethods = function(Class, methods, attribute) {
		_.each(methods, function(length, method) {
			if (_[method]) Class.prototype[method] = addMethod(length, method, attribute);
		});
	};

	// Backbone.Events
	// ---------------

	// A module that can be mixed in to *any object* in order to provide it with
	// custom events. You may bind with `on` or remove with `off` callback
	// functions to an event; `trigger`-ing an event fires all callbacks in
	// succession.
	//
	//     var object = {};
	//     _.extend(object, Backbone.Events);
	//     object.on('expand', function(){ alert('expanded'); });
	//     object.trigger('expand');
	//
	var Events = Backbone.Events = {};

	// Regular expression used to split event strings.
	var eventSplitter = /\s+/;

	// Iterates over the standard `event, callback` (as well as the fancy multiple
	// space-separated events `"change blur", callback` and jQuery-style event
	// maps `{event: callback}`), reducing them by manipulating `memo`.
	// Passes a normalized single event name and callback, as well as any
	// optional `opts`.
	var eventsApi = function(iteratee, memo, name, callback, opts) {
		var i = 0, names;
		if (name && typeof name === 'object') {
			// Handle event maps.
			if (callback !== void 0 && 'context' in opts && opts.context === void 0) opts.context = callback;
			for (names = _.keys(name); i < names.length ; i++) {
				memo = iteratee(memo, names[i], name[names[i]], opts);
			}
		} else if (name && eventSplitter.test(name)) {
			// Handle space separated event names.
			for (names = name.split(eventSplitter); i < names.length; i++) {
				memo = iteratee(memo, names[i], callback, opts);
			}
		} else {
			memo = iteratee(memo, name, callback, opts);
		}
		return memo;
	};

	// Bind an event to a `callback` function. Passing `"all"` will bind
	// the callback to all events fired.
	Events.on = function(name, callback, context) {
		return internalOn(this, name, callback, context);
	};

	// An internal use `on` function, used to guard the `listening` argument from
	// the public API.
	var internalOn = function(obj, name, callback, context, listening) {
		obj._events = eventsApi(onApi, obj._events || {}, name, callback, {
			context: context,
			ctx: obj,
			listening: listening
		});

		if (listening) {
			var listeners = obj._listeners || (obj._listeners = {});
			listeners[listening.id] = listening;
		}

		return obj;
	};

	// Inversion-of-control versions of `on`. Tell *this* object to listen to
	// an event in another object... keeping track of what it's listening to.
	Events.listenTo =  function(obj, name, callback) {
		if (!obj) return this;
		var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
		var listeningTo = this._listeningTo || (this._listeningTo = {});
		var listening = listeningTo[id];

		// This object is not listening to any other events on `obj` yet.
		// Setup the necessary references to track the listening callbacks.
		if (!listening) {
			var thisId = this._listenId || (this._listenId = _.uniqueId('l'));
			listening = listeningTo[id] = {obj: obj, objId: id, id: thisId, listeningTo: listeningTo, count: 0};
		}

		// Bind callbacks on obj, and keep track of them on listening.
		internalOn(obj, name, callback, this, listening);
		return this;
	};

	// The reducing API that adds a callback to the `events` object.
	var onApi = function(events, name, callback, options) {
		if (callback) {
			var handlers = events[name] || (events[name] = []);
			var context = options.context, ctx = options.ctx, listening = options.listening;
			if (listening) listening.count++;

			handlers.push({ callback: callback, context: context, ctx: context || ctx, listening: listening });
		}
		return events;
	};

	// Remove one or many callbacks. If `context` is null, removes all
	// callbacks with that function. If `callback` is null, removes all
	// callbacks for the event. If `name` is null, removes all bound
	// callbacks for all events.
	Events.off =  function(name, callback, context) {
		if (!this._events) return this;
		this._events = eventsApi(offApi, this._events, name, callback, {
			context: context,
			listeners: this._listeners
		});
		return this;
	};

	// Tell this object to stop listening to either specific events ... or
	// to every object it's currently listening to.
	Events.stopListening =  function(obj, name, callback) {
		var listeningTo = this._listeningTo;
		if (!listeningTo) return this;

		var ids = obj ? [obj._listenId] : _.keys(listeningTo);

		for (var i = 0; i < ids.length; i++) {
			var listening = listeningTo[ids[i]];

			// If listening doesn't exist, this object is not currently
			// listening to obj. Break out early.
			if (!listening) break;

			listening.obj.off(name, callback, this);
		}
		if (_.isEmpty(listeningTo)) this._listeningTo = void 0;

		return this;
	};

	// The reducing API that removes a callback from the `events` object.
	var offApi = function(events, name, callback, options) {
		// No events to consider.
		if (!events) return;

		var i = 0, listening;
		var context = options.context, listeners = options.listeners;

		// Delete all events listeners and "drop" events.
		if (!name && !callback && !context) {
			var ids = _.keys(listeners);
			for (; i < ids.length; i++) {
				listening = listeners[ids[i]];
				delete listeners[listening.id];
				delete listening.listeningTo[listening.objId];
			}
			return;
		}

		var names = name ? [name] : _.keys(events);
		for (; i < names.length; i++) {
			name = names[i];
			var handlers = events[name];

			// Bail out if there are no events stored.
			if (!handlers) break;

			// Replace events if there are any remaining.  Otherwise, clean up.
			var remaining = [];
			for (var j = 0; j < handlers.length; j++) {
				var handler = handlers[j];
				if (
					callback && callback !== handler.callback &&
					callback !== handler.callback._callback ||
					context && context !== handler.context
				) {
					remaining.push(handler);
				} else {
					listening = handler.listening;
					if (listening && --listening.count === 0) {
						delete listeners[listening.id];
						delete listening.listeningTo[listening.objId];
					}
				}
			}

			// Update tail event if the list has any events.  Otherwise, clean up.
			if (remaining.length) {
				events[name] = remaining;
			} else {
				delete events[name];
			}
		}
		if (_.size(events)) return events;
	};

	// Bind an event to only be triggered a single time. After the first time
	// the callback is invoked, it will be removed. When multiple events are
	// passed in using the space-separated syntax, the event will fire once for every
	// event you passed in, not once for a combination of all events
	Events.once =  function(name, callback, context) {
		// Map the event into a `{event: once}` object.
		var events = eventsApi(onceMap, {}, name, callback, _.bind(this.off, this));
		return this.on(events, void 0, context);
	};

	// Inversion-of-control versions of `once`.
	Events.listenToOnce =  function(obj, name, callback) {
		// Map the event into a `{event: once}` object.
		var events = eventsApi(onceMap, {}, name, callback, _.bind(this.stopListening, this, obj));
		return this.listenTo(obj, events);
	};

	// Reduces the event callbacks into a map of `{event: onceWrapper}`.
	// `offer` unbinds the `onceWrapper` after it has been called.
	var onceMap = function(map, name, callback, offer) {
		if (callback) {
			var once = map[name] = _.once(function() {
				offer(name, once);
				callback.apply(this, arguments);
			});
			once._callback = callback;
		}
		return map;
	};

	// Trigger one or many events, firing all bound callbacks. Callbacks are
	// passed the same arguments as `trigger` is, apart from the event name
	// (unless you're listening on `"all"`, which will cause your callback to
	// receive the true name of the event as the first argument).
	Events.trigger =  function(name) {
		if (!this._events) return this;

		var length = Math.max(0, arguments.length - 1);
		var args = Array(length);
		for (var i = 0; i < length; i++) args[i] = arguments[i + 1];

		eventsApi(triggerApi, this._events, name, void 0, args);
		return this;
	};

	// Handles triggering the appropriate event callbacks.
	var triggerApi = function(objEvents, name, cb, args) {
		if (objEvents) {
			var events = objEvents[name];
			var allEvents = objEvents.all;
			if (events && allEvents) allEvents = allEvents.slice();
			if (events) triggerEvents(events, args);
			if (allEvents) triggerEvents(allEvents, [name].concat(args));
		}
		return objEvents;
	};

	// A difficult-to-believe, but optimized internal dispatch function for
	// triggering events. Tries to keep the usual cases speedy (most internal
	// Backbone events have 3 arguments).
	var triggerEvents = function(events, args) {
		var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
		switch (args.length) {
			case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return;
			case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return;
			case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
			case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
			default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return;
		}
	};

	// Aliases for backwards compatibility.
	Events.bind   = Events.on;
	Events.unbind = Events.off;

	// Allow the `Backbone` object to serve as a global event bus, for folks who
	// want global "pubsub" in a convenient place.
	_.extend(Backbone, Events);

	// Backbone.Model
	// --------------

	// Backbone **Models** are the basic data object in the framework --
	// frequently representing a row in a table in a database on your server.
	// A discrete chunk of data and a bunch of useful, related methods for
	// performing computations and transformations on that data.

	// Create a new model with the specified attributes. A client id (`cid`)
	// is automatically generated and assigned for you.
	var Model = Backbone.Model = function(attributes, options) {
		var attrs = attributes || {};
		options || (options = {});
		this.cid = _.uniqueId(this.cidPrefix);
		this.attributes = {};
		if (options.collection) this.collection = options.collection;
		if (options.parse) attrs = this.parse(attrs, options) || {};
		attrs = _.defaults({}, attrs, _.result(this, 'defaults'));
		this.set(attrs, options);
		this.changed = {};
		this.initialize.apply(this, arguments);
	};

	// Attach all inheritable methods to the Model prototype.
	_.extend(Model.prototype, Events, {

		// A hash of attributes whose current and previous value differ.
		changed: null,

		// The value returned during the last failed validation.
		validationError: null,

		// The default name for the JSON `id` attribute is `"id"`. MongoDB and
		// CouchDB users may want to set this to `"_id"`.
		idAttribute: 'id',

		// The prefix is used to create the client id which is used to identify models locally.
		// You may want to override this if you're experiencing name clashes with model ids.
		cidPrefix: 'c',

		// Initialize is an empty function by default. Override it with your own
		// initialization logic.
		initialize: function(){},

		// Return a copy of the model's `attributes` object.
		toJSON: function(options) {
			return _.clone(this.attributes);
		},

		// Proxy `Backbone.sync` by default -- but override this if you need
		// custom syncing semantics for *this* particular model.
		sync: function() {
			return Backbone.sync.apply(this, arguments);
		},

		// Get the value of an attribute.
		get: function(attr) {
			return this.attributes[attr];
		},

		// Get the HTML-escaped value of an attribute.
		escape: function(attr) {
			return _.escape(this.get(attr));
		},

		// Returns `true` if the attribute contains a value that is not null
		// or undefined.
		has: function(attr) {
			return this.get(attr) != null;
		},

		// Special-cased proxy to underscore's `_.matches` method.
		matches: function(attrs) {
			return !!_.iteratee(attrs, this)(this.attributes);
		},

		// Set a hash of model attributes on the object, firing `"change"`. This is
		// the core primitive operation of a model, updating the data and notifying
		// anyone who needs to know about the change in state. The heart of the beast.
		set: function(key, val, options) {
			if (key == null) return this;

			// Handle both `"key", value` and `{key: value}` -style arguments.
			var attrs;
			if (typeof key === 'object') {
				attrs = key;
				options = val;
			} else {
				(attrs = {})[key] = val;
			}

			options || (options = {});

			// Run validation.
			if (!this._validate(attrs, options)) return false;

			// Extract attributes and options.
			var unset      = options.unset;
			var silent     = options.silent;
			var changes    = [];
			var changing   = this._changing;
			this._changing = true;

			if (!changing) {
				this._previousAttributes = _.clone(this.attributes);
				this.changed = {};
			}

			var current = this.attributes;
			var changed = this.changed;
			var prev    = this._previousAttributes;

			// Check for changes of `id`.
			if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];

			// For each `set` attribute, update or delete the current value.
			for (var attr in attrs) {
				val = attrs[attr];
				if (!_.isEqual(current[attr], val)) changes.push(attr);
				if (!_.isEqual(prev[attr], val)) {
					changed[attr] = val;
				} else {
					delete changed[attr];
				}
				unset ? delete current[attr] : current[attr] = val;
			}

			// Trigger all relevant attribute changes.
			if (!silent) {
				if (changes.length) this._pending = options;
				for (var i = 0; i < changes.length; i++) {
					this.trigger('change:' + changes[i], this, current[changes[i]], options);
				}
			}

			// You might be wondering why there's a `while` loop here. Changes can
			// be recursively nested within `"change"` events.
			if (changing) return this;
			if (!silent) {
				while (this._pending) {
					options = this._pending;
					this._pending = false;
					this.trigger('change', this, options);
				}
			}
			this._pending = false;
			this._changing = false;
			return this;
		},

		// Remove an attribute from the model, firing `"change"`. `unset` is a noop
		// if the attribute doesn't exist.
		unset: function(attr, options) {
			return this.set(attr, void 0, _.extend({}, options, {unset: true}));
		},

		// Clear all attributes on the model, firing `"change"`.
		clear: function(options) {
			var attrs = {};
			for (var key in this.attributes) attrs[key] = void 0;
			return this.set(attrs, _.extend({}, options, {unset: true}));
		},

		// Determine if the model has changed since the last `"change"` event.
		// If you specify an attribute name, determine if that attribute has changed.
		hasChanged: function(attr) {
			if (attr == null) return !_.isEmpty(this.changed);
			return _.has(this.changed, attr);
		},

		// Return an object containing all the attributes that have changed, or
		// false if there are no changed attributes. Useful for determining what
		// parts of a view need to be updated and/or what attributes need to be
		// persisted to the server. Unset attributes will be set to undefined.
		// You can also pass an attributes object to diff against the model,
		// determining if there *would be* a change.
		changedAttributes: function(diff) {
			if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
			var old = this._changing ? this._previousAttributes : this.attributes;
			var changed = {};
			for (var attr in diff) {
				var val = diff[attr];
				if (_.isEqual(old[attr], val)) continue;
				changed[attr] = val;
			}
			return _.size(changed) ? changed : false;
		},

		// Get the previous value of an attribute, recorded at the time the last
		// `"change"` event was fired.
		previous: function(attr) {
			if (attr == null || !this._previousAttributes) return null;
			return this._previousAttributes[attr];
		},

		// Get all of the attributes of the model at the time of the previous
		// `"change"` event.
		previousAttributes: function() {
			return _.clone(this._previousAttributes);
		},

		// Fetch the model from the server, merging the response with the model's
		// local attributes. Any changed attributes will trigger a "change" event.
		fetch: function(options) {
			options = _.extend({parse: true}, options);
			var model = this;
			var success = options.success;
			options.success = function(resp) {
				var serverAttrs = options.parse ? model.parse(resp, options) : resp;
				if (!model.set(serverAttrs, options)) return false;
				if (success) success.call(options.context, model, resp, options);
				model.trigger('sync', model, resp, options);
			};
			wrapError(this, options);
			return this.sync('read', this, options);
		},

		// Set a hash of model attributes, and sync the model to the server.
		// If the server returns an attributes hash that differs, the model's
		// state will be `set` again.
		save: function(key, val, options) {
			// Handle both `"key", value` and `{key: value}` -style arguments.
			var attrs;
			if (key == null || typeof key === 'object') {
				attrs = key;
				options = val;
			} else {
				(attrs = {})[key] = val;
			}

			options = _.extend({validate: true, parse: true}, options);
			var wait = options.wait;

			// If we're not waiting and attributes exist, save acts as
			// `set(attr).save(null, opts)` with validation. Otherwise, check if
			// the model will be valid when the attributes, if any, are set.
			if (attrs && !wait) {
				if (!this.set(attrs, options)) return false;
			} else {
				if (!this._validate(attrs, options)) return false;
			}

			// After a successful server-side save, the client is (optionally)
			// updated with the server-side state.
			var model = this;
			var success = options.success;
			var attributes = this.attributes;
			options.success = function(resp) {
				// Ensure attributes are restored during synchronous saves.
				model.attributes = attributes;
				var serverAttrs = options.parse ? model.parse(resp, options) : resp;
				if (wait) serverAttrs = _.extend({}, attrs, serverAttrs);
				if (serverAttrs && !model.set(serverAttrs, options)) return false;
				if (success) success.call(options.context, model, resp, options);
				model.trigger('sync', model, resp, options);
			};
			wrapError(this, options);

			// Set temporary attributes if `{wait: true}` to properly find new ids.
			if (attrs && wait) this.attributes = _.extend({}, attributes, attrs);

			var method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
			if (method === 'patch' && !options.attrs) options.attrs = attrs;
			var xhr = this.sync(method, this, options);

			// Restore attributes.
			this.attributes = attributes;

			return xhr;
		},

		// Destroy this model on the server if it was already persisted.
		// Optimistically removes the model from its collection, if it has one.
		// If `wait: true` is passed, waits for the server to respond before removal.
		destroy: function(options) {
			options = options ? _.clone(options) : {};
			var model = this;
			var success = options.success;
			var wait = options.wait;

			var destroy = function() {
				model.stopListening();
				model.trigger('destroy', model, model.collection, options);
			};

			options.success = function(resp) {
				if (wait) destroy();
				if (success) success.call(options.context, model, resp, options);
				if (!model.isNew()) model.trigger('sync', model, resp, options);
			};

			var xhr = false;
			if (this.isNew()) {
				_.defer(options.success);
			} else {
				wrapError(this, options);
				xhr = this.sync('delete', this, options);
			}
			if (!wait) destroy();
			return xhr;
		},

		// Default URL for the model's representation on the server -- if you're
		// using Backbone's restful methods, override this to change the endpoint
		// that will be called.
		url: function() {
			var base =
					_.result(this, 'urlRoot') ||
					_.result(this.collection, 'url') ||
					urlError();
			if (this.isNew()) return base;
			var id = this.get(this.idAttribute);
			return base.replace(/[^\/]$/, '$&/') + encodeURIComponent(id);
		},

		// **parse** converts a response into the hash of attributes to be `set` on
		// the model. The default implementation is just to pass the response along.
		parse: function(resp, options) {
			return resp;
		},

		// Create a new model with identical attributes to this one.
		clone: function() {
			return new this.constructor(this.attributes);
		},

		// A model is new if it has never been saved to the server, and lacks an id.
		isNew: function() {
			return !this.has(this.idAttribute);
		},

		// Check if the model is currently in a valid state.
		isValid: function(options) {
			return this._validate({}, _.defaults({validate: true}, options));
		},

		// Run validation against the next complete set of model attributes,
		// returning `true` if all is well. Otherwise, fire an `"invalid"` event.
		_validate: function(attrs, options) {
			if (!options.validate || !this.validate) return true;
			attrs = _.extend({}, this.attributes, attrs);
			var error = this.validationError = this.validate(attrs, options) || null;
			if (!error) return true;
			this.trigger('invalid', this, error, _.extend(options, {validationError: error}));
			return false;
		}

	});

	// Underscore methods that we want to implement on the Model.
	var modelMethods = { keys: 1, values: 1, pairs: 1, invert: 1, pick: 0,
		omit: 0, chain: 1, isEmpty: 1 };

	// Mix in each Underscore method as a proxy to `Model#attributes`.
	addUnderscoreMethods(Model, modelMethods, 'attributes');

	// Backbone.Collection
	// -------------------

	// If models tend to represent a single row of data, a Backbone Collection is
	// more analogous to a table full of data ... or a small slice or page of that
	// table, or a collection of rows that belong together for a particular reason
	// -- all of the messages in this particular folder, all of the documents
	// belonging to this particular author, and so on. Collections maintain
	// indexes of their models, both in order, and for lookup by `id`.

	// Create a new **Collection**, perhaps to contain a specific type of `model`.
	// If a `comparator` is specified, the Collection will maintain
	// its models in sort order, as they're added and removed.
	var Collection = Backbone.Collection = function(models, options) {
		options || (options = {});
		if (options.model) this.model = options.model;
		if (options.comparator !== void 0) this.comparator = options.comparator;
		this._reset();
		this.initialize.apply(this, arguments);
		if (models) this.reset(models, _.extend({silent: true}, options));
	};

	// Default options for `Collection#set`.
	var setOptions = {add: true, remove: true, merge: true};
	var addOptions = {add: true, remove: false};

	// Define the Collection's inheritable methods.
	_.extend(Collection.prototype, Events, {

		// The default model for a collection is just a **Backbone.Model**.
		// This should be overridden in most cases.
		model: Model,

		// Initialize is an empty function by default. Override it with your own
		// initialization logic.
		initialize: function(){},

		// The JSON representation of a Collection is an array of the
		// models' attributes.
		toJSON: function(options) {
			return this.map(function(model) { return model.toJSON(options); });
		},

		// Proxy `Backbone.sync` by default.
		sync: function() {
			return Backbone.sync.apply(this, arguments);
		},

		// Add a model, or list of models to the set.
		add: function(models, options) {
			return this.set(models, _.extend({merge: false}, options, addOptions));
		},

		// Remove a model, or a list of models from the set.
		remove: function(models, options) {
			options = _.extend({}, options);
			var singular = !_.isArray(models);
			models = singular ? [models] : _.clone(models);
			var removed = this._removeModels(models, options);
			if (!options.silent && removed) this.trigger('update', this, options);
			return singular ? removed[0] : removed;
		},

		// Update a collection by `set`-ing a new list of models, adding new ones,
		// removing models that are no longer present, and merging models that
		// already exist in the collection, as necessary. Similar to **Model#set**,
		// the core operation for updating the data contained by the collection.
		set: function(models, options) {
			options = _.defaults({}, options, setOptions);
			if (options.parse && !this._isModel(models)) models = this.parse(models, options);
			var singular = !_.isArray(models);
			models = singular ? (models ? [models] : []) : models.slice();
			var id, model, attrs, existing, sort;
			var at = options.at;
			if (at != null) at = +at;
			if (at < 0) at += this.length + 1;
			var sortable = this.comparator && (at == null) && options.sort !== false;
			var sortAttr = _.isString(this.comparator) ? this.comparator : null;
			var toAdd = [], toRemove = [], modelMap = {};
			var add = options.add, merge = options.merge, remove = options.remove;
			var order = !sortable && add && remove ? [] : false;
			var orderChanged = false;

			// Turn bare objects into model references, and prevent invalid models
			// from being added.
			for (var i = 0; i < models.length; i++) {
				attrs = models[i];

				// If a duplicate is found, prevent it from being added and
				// optionally merge it into the existing model.
				if (existing = this.get(attrs)) {
					if (remove) modelMap[existing.cid] = true;
					if (merge && attrs !== existing) {
						attrs = this._isModel(attrs) ? attrs.attributes : attrs;
						if (options.parse) attrs = existing.parse(attrs, options);
						existing.set(attrs, options);
						if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true;
					}
					models[i] = existing;

					// If this is a new, valid model, push it to the `toAdd` list.
				} else if (add) {
					model = models[i] = this._prepareModel(attrs, options);
					if (!model) continue;
					toAdd.push(model);
					this._addReference(model, options);
				}

				// Do not add multiple models with the same `id`.
				model = existing || model;
				if (!model) continue;
				id = this.modelId(model.attributes);
				if (order && (model.isNew() || !modelMap[id])) {
					order.push(model);

					// Check to see if this is actually a new model at this index.
					orderChanged = orderChanged || !this.models[i] || model.cid !== this.models[i].cid;
				}

				modelMap[id] = true;
			}

			// Remove nonexistent models if appropriate.
			if (remove) {
				for (var i = 0; i < this.length; i++) {
					if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model);
				}
				if (toRemove.length) this._removeModels(toRemove, options);
			}

			// See if sorting is needed, update `length` and splice in new models.
			if (toAdd.length || orderChanged) {
				if (sortable) sort = true;
				this.length += toAdd.length;
				if (at != null) {
					for (var i = 0; i < toAdd.length; i++) {
						this.models.splice(at + i, 0, toAdd[i]);
					}
				} else {
					if (order) this.models.length = 0;
					var orderedModels = order || toAdd;
					for (var i = 0; i < orderedModels.length; i++) {
						this.models.push(orderedModels[i]);
					}
				}
			}

			// Silently sort the collection if appropriate.
			if (sort) this.sort({silent: true});

			// Unless silenced, it's time to fire all appropriate add/sort events.
			if (!options.silent) {
				var addOpts = at != null ? _.clone(options) : options;
				for (var i = 0; i < toAdd.length; i++) {
					if (at != null) addOpts.index = at + i;
					(model = toAdd[i]).trigger('add', model, this, addOpts);
				}
				if (sort || orderChanged) this.trigger('sort', this, options);
				if (toAdd.length || toRemove.length) this.trigger('update', this, options);
			}

			// Return the added (or merged) model (or models).
			return singular ? models[0] : models;
		},

		// When you have more items than you want to add or remove individually,
		// you can reset the entire set with a new list of models, without firing
		// any granular `add` or `remove` events. Fires `reset` when finished.
		// Useful for bulk operations and optimizations.
		reset: function(models, options) {
			options = options ? _.clone(options) : {};
			for (var i = 0; i < this.models.length; i++) {
				this._removeReference(this.models[i], options);
			}
			options.previousModels = this.models;
			this._reset();
			models = this.add(models, _.extend({silent: true}, options));
			if (!options.silent) this.trigger('reset', this, options);
			return models;
		},

		// Add a model to the end of the collection.
		push: function(model, options) {
			return this.add(model, _.extend({at: this.length}, options));
		},

		// Remove a model from the end of the collection.
		pop: function(options) {
			var model = this.at(this.length - 1);
			return this.remove(model, options);
		},

		// Add a model to the beginning of the collection.
		unshift: function(model, options) {
			return this.add(model, _.extend({at: 0}, options));
		},

		// Remove a model from the beginning of the collection.
		shift: function(options) {
			var model = this.at(0);
			return this.remove(model, options);
		},

		// Slice out a sub-array of models from the collection.
		slice: function() {
			return slice.apply(this.models, arguments);
		},

		// Get a model from the set by id.
		get: function(obj) {
			if (obj == null) return void 0;
			var id = this.modelId(this._isModel(obj) ? obj.attributes : obj);
			return this._byId[obj] || this._byId[id] || this._byId[obj.cid];
		},

		// Get the model at the given index.
		at: function(index) {
			if (index < 0) index += this.length;
			return this.models[index];
		},

		// Return models with matching attributes. Useful for simple cases of
		// `filter`.
		where: function(attrs, first) {
			var matches = _.matches(attrs);
			return this[first ? 'find' : 'filter'](function(model) {
				return matches(model.attributes);
			});
		},

		// Return the first model with matching attributes. Useful for simple cases
		// of `find`.
		findWhere: function(attrs) {
			return this.where(attrs, true);
		},

		// Force the collection to re-sort itself. You don't need to call this under
		// normal circumstances, as the set will maintain sort order as each item
		// is added.
		sort: function(options) {
			if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
			options || (options = {});

			// Run sort based on type of `comparator`.
			if (_.isString(this.comparator) || this.comparator.length === 1) {
				this.models = this.sortBy(this.comparator, this);
			} else {
				this.models.sort(_.bind(this.comparator, this));
			}

			if (!options.silent) this.trigger('sort', this, options);
			return this;
		},

		// Pluck an attribute from each model in the collection.
		pluck: function(attr) {
			return _.invoke(this.models, 'get', attr);
		},

		// Fetch the default set of models for this collection, resetting the
		// collection when they arrive. If `reset: true` is passed, the response
		// data will be passed through the `reset` method instead of `set`.
		fetch: function(options) {
			options = _.extend({parse: true}, options);
			var success = options.success;
			var collection = this;
			options.success = function(resp) {
				var method = options.reset ? 'reset' : 'set';
				collection[method](resp, options);
				if (success) success.call(options.context, collection, resp, options);
				collection.trigger('sync', collection, resp, options);
			};
			wrapError(this, options);
			return this.sync('read', this, options);
		},

		// Create a new instance of a model in this collection. Add the model to the
		// collection immediately, unless `wait: true` is passed, in which case we
		// wait for the server to agree.
		create: function(model, options) {
			options = options ? _.clone(options) : {};
			var wait = options.wait;
			model = this._prepareModel(model, options);
			if (!model) return false;
			if (!wait) this.add(model, options);
			var collection = this;
			var success = options.success;
			options.success = function(model, resp, callbackOpts) {
				if (wait) collection.add(model, callbackOpts);
				if (success) success.call(callbackOpts.context, model, resp, callbackOpts);
			};
			model.save(null, options);
			return model;
		},

		// **parse** converts a response into a list of models to be added to the
		// collection. The default implementation is just to pass it through.
		parse: function(resp, options) {
			return resp;
		},

		// Create a new collection with an identical list of models as this one.
		clone: function() {
			return new this.constructor(this.models, {
				model: this.model,
				comparator: this.comparator
			});
		},

		// Define how to uniquely identify models in the collection.
		modelId: function (attrs) {
			return attrs[this.model.prototype.idAttribute || 'id'];
		},

		// Private method to reset all internal state. Called when the collection
		// is first initialized or reset.
		_reset: function() {
			this.length = 0;
			this.models = [];
			this._byId  = {};
		},

		// Prepare a hash of attributes (or other model) to be added to this
		// collection.
		_prepareModel: function(attrs, options) {
			if (this._isModel(attrs)) {
				if (!attrs.collection) attrs.collection = this;
				return attrs;
			}
			options = options ? _.clone(options) : {};
			options.collection = this;
			var model = new this.model(attrs, options);
			if (!model.validationError) return model;
			this.trigger('invalid', this, model.validationError, options);
			return false;
		},

		// Internal method called by both remove and set.
		// Returns removed models, or false if nothing is removed.
		_removeModels: function(models, options) {
			var removed = [];
			for (var i = 0; i < models.length; i++) {
				var model = this.get(models[i]);
				if (!model) continue;

				var index = this.indexOf(model);
				this.models.splice(index, 1);
				this.length--;

				if (!options.silent) {
					options.index = index;
					model.trigger('remove', model, this, options);
				}

				removed.push(model);
				this._removeReference(model, options);
			}
			return removed.length ? removed : false;
		},

		// Method for checking whether an object should be considered a model for
		// the purposes of adding to the collection.
		_isModel: function (model) {
			return model instanceof Model;
		},

		// Internal method to create a model's ties to a collection.
		_addReference: function(model, options) {
			this._byId[model.cid] = model;
			var id = this.modelId(model.attributes);
			if (id != null) this._byId[id] = model;
			model.on('all', this._onModelEvent, this);
		},

		// Internal method to sever a model's ties to a collection.
		_removeReference: function(model, options) {
			delete this._byId[model.cid];
			var id = this.modelId(model.attributes);
			if (id != null) delete this._byId[id];
			if (this === model.collection) delete model.collection;
			model.off('all', this._onModelEvent, this);
		},

		// Internal method called every time a model in the set fires an event.
		// Sets need to update their indexes when models change ids. All other
		// events simply proxy through. "add" and "remove" events that originate
		// in other collections are ignored.
		_onModelEvent: function(event, model, collection, options) {
			if ((event === 'add' || event === 'remove') && collection !== this) return;
			if (event === 'destroy') this.remove(model, options);
			if (event === 'change') {
				var prevId = this.modelId(model.previousAttributes());
				var id = this.modelId(model.attributes);
				if (prevId !== id) {
					if (prevId != null) delete this._byId[prevId];
					if (id != null) this._byId[id] = model;
				}
			}
			this.trigger.apply(this, arguments);
		}

	});

	// Underscore methods that we want to implement on the Collection.
	// 90% of the core usefulness of Backbone Collections is actually implemented
	// right here:
	var collectionMethods = { forEach: 3, each: 3, map: 3, collect: 3, reduce: 4,
		foldl: 4, inject: 4, reduceRight: 4, foldr: 4, find: 3, detect: 3, filter: 3,
		select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 2,
		contains: 2, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3,
		head: 3, take: 3, initial: 3, rest: 3, tail: 3, drop: 3, last: 3,
		without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3,
		isEmpty: 1, chain: 1, sample: 3, partition: 3 };

	// Mix in each Underscore method as a proxy to `Collection#models`.
	addUnderscoreMethods(Collection, collectionMethods, 'models');

	// Underscore methods that take a property name as an argument.
	var attributeMethods = ['groupBy', 'countBy', 'sortBy', 'indexBy'];

	// Use attributes instead of properties.
	_.each(attributeMethods, function(method) {
		if (!_[method]) return;
		Collection.prototype[method] = function(value, context) {
			var iterator = _.isFunction(value) ? value : function(model) {
				return model.get(value);
			};
			return _[method](this.models, iterator, context);
		};
	});

	// Backbone.View
	// -------------

	// Backbone Views are almost more convention than they are actual code. A View
	// is simply a JavaScript object that represents a logical chunk of UI in the
	// DOM. This might be a single item, an entire list, a sidebar or panel, or
	// even the surrounding frame which wraps your whole app. Defining a chunk of
	// UI as a **View** allows you to define your DOM events declaratively, without
	// having to worry about render order ... and makes it easy for the view to
	// react to specific changes in the state of your models.

	// Creating a Backbone.View creates its initial element outside of the DOM,
	// if an existing element is not provided...
	var View = Backbone.View = function(options) {
		this.cid = _.uniqueId('view');
		_.extend(this, _.pick(options, viewOptions));
		this._ensureElement();
		this.initialize.apply(this, arguments);
	};

	// Cached regex to split keys for `delegate`.
	var delegateEventSplitter = /^(\S+)\s*(.*)$/;

	// List of view options to be merged as properties.
	var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];

	// Set up all inheritable **Backbone.View** properties and methods.
	_.extend(View.prototype, Events, {

		// The default `tagName` of a View's element is `"div"`.
		tagName: 'div',

		// jQuery delegate for element lookup, scoped to DOM elements within the
		// current view. This should be preferred to global lookups where possible.
		$: function(selector) {
			return this.$el.find(selector);
		},

		// Initialize is an empty function by default. Override it with your own
		// initialization logic.
		initialize: function(){},

		// **render** is the core function that your view should override, in order
		// to populate its element (`this.el`), with the appropriate HTML. The
		// convention is for **render** to always return `this`.
		render: function() {
			return this;
		},

		// Remove this view by taking the element out of the DOM, and removing any
		// applicable Backbone.Events listeners.
		remove: function() {
			this._removeElement();
			this.stopListening();
			return this;
		},

		// Remove this view's element from the document and all event listeners
		// attached to it. Exposed for subclasses using an alternative DOM
		// manipulation API.
		_removeElement: function() {
			this.$el.remove();
		},

		// Change the view's element (`this.el` property) and re-delegate the
		// view's events on the new element.
		setElement: function(element) {
			this.undelegateEvents();
			this._setElement(element);
			this.delegateEvents();
			return this;
		},

		// Creates the `this.el` and `this.$el` references for this view using the
		// given `el`. `el` can be a CSS selector or an HTML string, a jQuery
		// context or an element. Subclasses can override this to utilize an
		// alternative DOM manipulation API and are only required to set the
		// `this.el` property.
		_setElement: function(el) {
			this.$el = el instanceof Backbone.$ ? el : Backbone.$(el);
			this.el = this.$el[0];
		},

		// Set callbacks, where `this.events` is a hash of
		//
		// *{"event selector": "callback"}*
		//
		//     {
		//       'mousedown .title':  'edit',
		//       'click .button':     'save',
		//       'click .open':       function(e) { ... }
		//     }
		//
		// pairs. Callbacks will be bound to the view, with `this` set properly.
		// Uses event delegation for efficiency.
		// Omitting the selector binds the event to `this.el`.
		delegateEvents: function(events) {
			events || (events = _.result(this, 'events'));
			if (!events) return this;
			this.undelegateEvents();
			for (var key in events) {
				var method = events[key];
				if (!_.isFunction(method)) method = this[method];
				if (!method) continue;
				var match = key.match(delegateEventSplitter);
				this.delegate(match[1], match[2], _.bind(method, this));
			}
			return this;
		},

		// Add a single event listener to the view's element (or a child element
		// using `selector`). This only works for delegate-able events: not `focus`,
		// `blur`, and not `change`, `submit`, and `reset` in Internet Explorer.
		delegate: function(eventName, selector, listener) {
			this.$el.on(eventName + '.delegateEvents' + this.cid, selector, listener);
			return this;
		},

		// Clears all callbacks previously bound to the view by `delegateEvents`.
		// You usually don't need to use this, but may wish to if you have multiple
		// Backbone views attached to the same DOM element.
		undelegateEvents: function() {
			if (this.$el) this.$el.off('.delegateEvents' + this.cid);
			return this;
		},

		// A finer-grained `undelegateEvents` for removing a single delegated event.
		// `selector` and `listener` are both optional.
		undelegate: function(eventName, selector, listener) {
			this.$el.off(eventName + '.delegateEvents' + this.cid, selector, listener);
			return this;
		},

		// Produces a DOM element to be assigned to your view. Exposed for
		// subclasses using an alternative DOM manipulation API.
		_createElement: function(tagName) {
			return document.createElement(tagName);
		},

		// Ensure that the View has a DOM element to render into.
		// If `this.el` is a string, pass it through `$()`, take the first
		// matching element, and re-assign it to `el`. Otherwise, create
		// an element from the `id`, `className` and `tagName` properties.
		_ensureElement: function() {
			if (!this.el) {
				var attrs = _.extend({}, _.result(this, 'attributes'));
				if (this.id) attrs.id = _.result(this, 'id');
				if (this.className) attrs['class'] = _.result(this, 'className');
				this.setElement(this._createElement(_.result(this, 'tagName')));
				this._setAttributes(attrs);
			} else {
				this.setElement(_.result(this, 'el'));
			}
		},

		// Set attributes from a hash on this view's element.  Exposed for
		// subclasses using an alternative DOM manipulation API.
		_setAttributes: function(attributes) {
			this.$el.attr(attributes);
		}

	});

	// Backbone.sync
	// -------------

	// Override this function to change the manner in which Backbone persists
	// models to the server. You will be passed the type of request, and the
	// model in question. By default, makes a RESTful Ajax request
	// to the model's `url()`. Some possible customizations could be:
	//
	// * Use `setTimeout` to batch rapid-fire updates into a single request.
	// * Send up the models as XML instead of JSON.
	// * Persist models via WebSockets instead of Ajax.
	//
	// Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
	// as `POST`, with a `_method` parameter containing the true HTTP method,
	// as well as all requests with the body as `application/x-www-form-urlencoded`
	// instead of `application/json` with the model in a param named `model`.
	// Useful when interfacing with server-side languages like **PHP** that make
	// it difficult to read the body of `PUT` requests.
	Backbone.sync = function(method, model, options) {
		var type = methodMap[method];

		// Default options, unless specified.
		_.defaults(options || (options = {}), {
			emulateHTTP: Backbone.emulateHTTP,
			emulateJSON: Backbone.emulateJSON
		});

		// Default JSON-request options.
		var params = {type: type, dataType: 'json'};

		// Ensure that we have a URL.
		if (!options.url) {
			params.url = _.result(model, 'url') || urlError();
		}

		// Ensure that we have the appropriate request data.
		if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
			params.contentType = 'application/json';
			params.data = JSON.stringify(options.attrs || model.toJSON(options));
		}

		// For older servers, emulate JSON by encoding the request into an HTML-form.
		if (options.emulateJSON) {
			params.contentType = 'application/x-www-form-urlencoded';
			params.data = params.data ? {model: params.data} : {};
		}

		// For older servers, emulate HTTP by mimicking the HTTP method with `_method`
		// And an `X-HTTP-Method-Override` header.
		if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) {
			params.type = 'POST';
			if (options.emulateJSON) params.data._method = type;
			var beforeSend = options.beforeSend;
			options.beforeSend = function(xhr) {
				xhr.setRequestHeader('X-HTTP-Method-Override', type);
				if (beforeSend) return beforeSend.apply(this, arguments);
			};
		}

		// Don't process data on a non-GET request.
		if (params.type !== 'GET' && !options.emulateJSON) {
			params.processData = false;
		}

		// Pass along `textStatus` and `errorThrown` from jQuery.
		var error = options.error;
		options.error = function(xhr, textStatus, errorThrown) {
			options.textStatus = textStatus;
			options.errorThrown = errorThrown;
			if (error) error.call(options.context, xhr, textStatus, errorThrown);
		};

		// Make the request, allowing the user to override any Ajax options.
		var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
		model.trigger('request', model, xhr, options);
		return xhr;
	};

	// Map from CRUD to HTTP for our default `Backbone.sync` implementation.
	var methodMap = {
		'create': 'POST',
		'update': 'PUT',
		'patch':  'PATCH',
		'delete': 'DELETE',
		'read':   'GET'
	};

	// Set the default implementation of `Backbone.ajax` to proxy through to `$`.
	// Override this if you'd like to use a different library.
	Backbone.ajax = function() {
		return Backbone.$.ajax.apply(Backbone.$, arguments);
	};

	// Backbone.Router
	// ---------------

	// Routers map faux-URLs to actions, and fire events when routes are
	// matched. Creating a new one sets its `routes` hash, if not set statically.
	var Router = Backbone.Router = function(options) {
		options || (options = {});
		if (options.routes) this.routes = options.routes;
		this._bindRoutes();
		this.initialize.apply(this, arguments);
	};

	// Cached regular expressions for matching named param parts and splatted
	// parts of route strings.
	var optionalParam = /\((.*?)\)/g;
	var namedParam    = /(\(\?)?:\w+/g;
	var splatParam    = /\*\w+/g;
	var escapeRegExp  = /[\-{}\[\]+?.,\\\^$|#\s]/g;

	// Set up all inheritable **Backbone.Router** properties and methods.
	_.extend(Router.prototype, Events, {

		// Initialize is an empty function by default. Override it with your own
		// initialization logic.
		initialize: function(){},

		// Manually bind a single named route to a callback. For example:
		//
		//     this.route('search/:query/p:num', 'search', function(query, num) {
		//       ...
		//     });
		//
		route: function(route, name, callback) {
			if (!_.isRegExp(route)) route = this._routeToRegExp(route);
			if (_.isFunction(name)) {
				callback = name;
				name = '';
			}
			if (!callback) callback = this[name];
			var router = this;
			Backbone.history.route(route, function(fragment) {
				var args = router._extractParameters(route, fragment);
				if (router.execute(callback, args, name) !== false) {
					router.trigger.apply(router, ['route:' + name].concat(args));
					router.trigger('route', name, args);
					Backbone.history.trigger('route', router, name, args);
				}
			});
			return this;
		},

		// Execute a route handler with the provided parameters.  This is an
		// excellent place to do pre-route setup or post-route cleanup.
		execute: function(callback, args, name) {
			if (callback) callback.apply(this, args);
		},

		// Simple proxy to `Backbone.history` to save a fragment into the history.
		navigate: function(fragment, options) {
			Backbone.history.navigate(fragment, options);
			return this;
		},

		// Bind all defined routes to `Backbone.history`. We have to reverse the
		// order of the routes here to support behavior where the most general
		// routes can be defined at the bottom of the route map.
		_bindRoutes: function() {
			if (!this.routes) return;
			this.routes = _.result(this, 'routes');
			var route, routes = _.keys(this.routes);
			while ((route = routes.pop()) != null) {
				this.route(route, this.routes[route]);
			}
		},

		// Convert a route string into a regular expression, suitable for matching
		// against the current location hash.
		_routeToRegExp: function(route) {
			route = route.replace(escapeRegExp, '\\$&')
				.replace(optionalParam, '(?:$1)?')
				.replace(namedParam, function(match, optional) {
							 return optional ? match : '([^/?]+)';
						 })
				.replace(splatParam, '([^?]*?)');
			return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$');
		},

		// Given a route, and a URL fragment that it matches, return the array of
		// extracted decoded parameters. Empty or unmatched parameters will be
		// treated as `null` to normalize cross-browser behavior.
		_extractParameters: function(route, fragment) {
			var params = route.exec(fragment).slice(1);
			return _.map(params, function(param, i) {
				// Don't decode the search params.
				if (i === params.length - 1) return param || null;
				return param ? decodeURIComponent(param) : null;
			});
		}

	});

	// Backbone.History
	// ----------------

	// Handles cross-browser history management, based on either
	// [pushState](http://diveintohtml5.info/history.html) and real URLs, or
	// [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange)
	// and URL fragments. If the browser supports neither (old IE, natch),
	// falls back to polling.
	var History = Backbone.History = function() {
		this.handlers = [];
		_.bindAll(this, 'checkUrl');

		// Ensure that `History` can be used outside of the browser.
		if (typeof window !== 'undefined') {
			this.location = window.location;
			this.history = window.history;
		}
	};

	// Cached regex for stripping a leading hash/slash and trailing space.
	var routeStripper = /^[#\/]|\s+$/g;

	// Cached regex for stripping leading and trailing slashes.
	var rootStripper = /^\/+|\/+$/g;

	// Cached regex for stripping urls of hash.
	var pathStripper = /#.*$/;

	// Has the history handling already been started?
	History.started = false;

	// Set up all inheritable **Backbone.History** properties and methods.
	_.extend(History.prototype, Events, {

		// The default interval to poll for hash changes, if necessary, is
		// twenty times a second.
		interval: 50,

		// Are we at the app root?
		atRoot: function() {
			var path = this.location.pathname.replace(/[^\/]$/, '$&/');
			return path === this.root && !this.getSearch();
		},

		// Does the pathname match the root?
		matchRoot: function() {
			var path = this.decodeFragment(this.location.pathname);
			var root = path.slice(0, this.root.length - 1) + '/';
			return root === this.root;
		},

		// Unicode characters in `location.pathname` are percent encoded so they're
		// decoded for comparison. `%25` should not be decoded since it may be part
		// of an encoded parameter.
		decodeFragment: function(fragment) {
			return decodeURI(fragment.replace(/%25/g, '%2525'));
		},

		// In IE6, the hash fragment and search params are incorrect if the
		// fragment contains `?`.
		getSearch: function() {
			var match = this.location.href.replace(/#.*/, '').match(/\?.+/);
			return match ? match[0] : '';
		},

		// Gets the true hash value. Cannot use location.hash directly due to bug
		// in Firefox where location.hash will always be decoded.
		getHash: function(window) {
			var match = (window || this).location.href.match(/#(.*)$/);
			return match ? match[1] : '';
		},

		// Get the pathname and search params, without the root.
		getPath: function() {
			var path = this.decodeFragment(
				this.location.pathname + this.getSearch()
			).slice(this.root.length - 1);
			return path.charAt(0) === '/' ? path.slice(1) : path;
		},

		// Get the cross-browser normalized URL fragment from the path or hash.
		getFragment: function(fragment) {
			if (fragment == null) {
				if (this._usePushState || !this._wantsHashChange) {
					fragment = this.getPath();
				} else {
					fragment = this.getHash();
				}
			}
			return fragment.replace(routeStripper, '');
		},

		// Start the hash change handling, returning `true` if the current URL matches
		// an existing route, and `false` otherwise.
		start: function(options) {
			if (History.started) throw new Error('Backbone.history has already been started');
			History.started = true;

			// Figure out the initial configuration. Do we need an iframe?
			// Is pushState desired ... is it available?
			this.options          = _.extend({root: '/'}, this.options, options);
			this.root             = this.options.root;
			this._wantsHashChange = this.options.hashChange !== false;
			this._hasHashChange   = 'onhashchange' in window;
			this._useHashChange   = this._wantsHashChange && this._hasHashChange;
			this._wantsPushState  = !!this.options.pushState;
			this._hasPushState    = !!(this.history && this.history.pushState);
			this._usePushState    = this._wantsPushState && this._hasPushState;
			this.fragment         = this.getFragment();

			// Normalize root to always include a leading and trailing slash.
			this.root = ('/' + this.root + '/').replace(rootStripper, '/');

			// Transition from hashChange to pushState or vice versa if both are
			// requested.
			if (this._wantsHashChange && this._wantsPushState) {

				// If we've started off with a route from a `pushState`-enabled
				// browser, but we're currently in a browser that doesn't support it...
				if (!this._hasPushState && !this.atRoot()) {
					var root = this.root.slice(0, -1) || '/';
					this.location.replace(root + '#' + this.getPath());
					// Return immediately as browser will do redirect to new url
					return true;

					// Or if we've started out with a hash-based route, but we're currently
					// in a browser where it could be `pushState`-based instead...
				} else if (this._hasPushState && this.atRoot()) {
					this.navigate(this.getHash(), {replace: true});
				}

			}

			// Proxy an iframe to handle location events if the browser doesn't
			// support the `hashchange` event, HTML5 history, or the user wants
			// `hashChange` but not `pushState`.
			if (!this._hasHashChange && this._wantsHashChange && !this._usePushState) {
				this.iframe = document.createElement('iframe');
				this.iframe.src = 'javascript:0';
				this.iframe.style.display = 'none';
				this.iframe.tabIndex = -1;
				var body = document.body;
				// Using `appendChild` will throw on IE < 9 if the document is not ready.
				var iWindow = body.insertBefore(this.iframe, body.firstChild).contentWindow;
				iWindow.document.open();
				iWindow.document.close();
				iWindow.location.hash = '#' + this.fragment;
			}

			// Add a cross-platform `addEventListener` shim for older browsers.
			var addEventListener = window.addEventListener || function (eventName, listener) {
					return attachEvent('on' + eventName, listener);
				};

			// Depending on whether we're using pushState or hashes, and whether
			// 'onhashchange' is supported, determine how we check the URL state.
			if (this._usePushState) {
				addEventListener('popstate', this.checkUrl, false);
			} else if (this._useHashChange && !this.iframe) {
				addEventListener('hashchange', this.checkUrl, false);
			} else if (this._wantsHashChange) {
				this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
			}

			if (!this.options.silent) return this.loadUrl();
		},

		// Disable Backbone.history, perhaps temporarily. Not useful in a real app,
		// but possibly useful for unit testing Routers.
		stop: function() {
			// Add a cross-platform `removeEventListener` shim for older browsers.
			var removeEventListener = window.removeEventListener || function (eventName, listener) {
					return detachEvent('on' + eventName, listener);
				};

			// Remove window listeners.
			if (this._usePushState) {
				removeEventListener('popstate', this.checkUrl, false);
			} else if (this._useHashChange && !this.iframe) {
				removeEventListener('hashchange', this.checkUrl, false);
			}

			// Clean up the iframe if necessary.
			if (this.iframe) {
				document.body.removeChild(this.iframe);
				this.iframe = null;
			}

			// Some environments will throw when clearing an undefined interval.
			if (this._checkUrlInterval) clearInterval(this._checkUrlInterval);
			History.started = false;
		},

		// Add a route to be tested when the fragment changes. Routes added later
		// may override previous routes.
		route: function(route, callback) {
			this.handlers.unshift({route: route, callback: callback});
		},

		// Checks the current URL to see if it has changed, and if it has,
		// calls `loadUrl`, normalizing across the hidden iframe.
		checkUrl: function(e) {
			var current = this.getFragment();

			// If the user pressed the back button, the iframe's hash will have
			// changed and we should use that for comparison.
			if (current === this.fragment && this.iframe) {
				current = this.getHash(this.iframe.contentWindow);
			}

			if (current === this.fragment) return false;
			if (this.iframe) this.navigate(current);
			this.loadUrl();
		},

		// Attempt to load the current URL fragment. If a route succeeds with a
		// match, returns `true`. If no defined routes matches the fragment,
		// returns `false`.
		loadUrl: function(fragment) {
			// If the root doesn't match, no routes can match either.
			if (!this.matchRoot()) return false;
			fragment = this.fragment = this.getFragment(fragment);
			return _.any(this.handlers, function(handler) {
				if (handler.route.test(fragment)) {
					handler.callback(fragment);
					return true;
				}
			});
		},

		// Save a fragment into the hash history, or replace the URL state if the
		// 'replace' option is passed. You are responsible for properly URL-encoding
		// the fragment in advance.
		//
		// The options object can contain `trigger: true` if you wish to have the
		// route callback be fired (not usually desirable), or `replace: true`, if
		// you wish to modify the current URL without adding an entry to the history.
		navigate: function(fragment, options) {
			if (!History.started) return false;
			if (!options || options === true) options = {trigger: !!options};

			// Normalize the fragment.
			fragment = this.getFragment(fragment || '');

			// Don't include a trailing slash on the root.
			var root = this.root;
			if (fragment === '' || fragment.charAt(0) === '?') {
				root = root.slice(0, -1) || '/';
			}
			var url = root + fragment;

			// Strip the hash and decode for matching.
			fragment = this.decodeFragment(fragment.replace(pathStripper, ''));

			if (this.fragment === fragment) return;
			this.fragment = fragment;

			// If pushState is available, we use it to set the fragment as a real URL.
			if (this._usePushState) {
				this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);

				// If hash changes haven't been explicitly disabled, update the hash
				// fragment to store history.
			} else if (this._wantsHashChange) {
				this._updateHash(this.location, fragment, options.replace);
				if (this.iframe && (fragment !== this.getHash(this.iframe.contentWindow))) {
					var iWindow = this.iframe.contentWindow;

					// Opening and closing the iframe tricks IE7 and earlier to push a
					// history entry on hash-tag change.  When replace is true, we don't
					// want this.
					if (!options.replace) {
						iWindow.document.open();
						iWindow.document.close();
					}

					this._updateHash(iWindow.location, fragment, options.replace);
				}

				// If you've told us that you explicitly don't want fallback hashchange-
				// based history, then `navigate` becomes a page refresh.
			} else {
				return this.location.assign(url);
			}
			if (options.trigger) return this.loadUrl(fragment);
		},

		// Update the hash location, either replacing the current entry, or adding
		// a new one to the browser history.
		_updateHash: function(location, fragment, replace) {
			if (replace) {
				var href = location.href.replace(/(javascript:|#).*$/, '');
				location.replace(href + '#' + fragment);
			} else {
				// Some browsers require that `hash` contains a leading #.
				location.hash = '#' + fragment;
			}
		}

	});

	// Create the default Backbone.history.
	Backbone.history = new History;

	// Helpers
	// -------

	// Helper function to correctly set up the prototype chain for subclasses.
	// Similar to `goog.inherits`, but uses a hash of prototype properties and
	// class properties to be extended.
	var extend = function(protoProps, staticProps) {
		var parent = this;
		var child;

		// The constructor function for the new subclass is either defined by you
		// (the "constructor" property in your `extend` definition), or defaulted
		// by us to simply call the parent constructor.
		if (protoProps && _.has(protoProps, 'constructor')) {
			child = protoProps.constructor;
		} else {
			child = function(){ return parent.apply(this, arguments); };
		}

		// Add static properties to the constructor function, if supplied.
		_.extend(child, parent, staticProps);

		// Set the prototype chain to inherit from `parent`, without calling
		// `parent` constructor function.
		var Surrogate = function(){ this.constructor = child; };
		Surrogate.prototype = parent.prototype;
		child.prototype = new Surrogate;

		// Add prototype properties (instance properties) to the subclass,
		// if supplied.
		if (protoProps) _.extend(child.prototype, protoProps);

		// Set a convenience property in case the parent's prototype is needed
		// later.
		child.__super__ = parent.prototype;

		return child;
	};

	// Set up inheritance for the model, collection, router, view and history.
	Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend;

	// Throw an error when a URL is needed, and none is supplied.
	var urlError = function() {
		throw new Error('A "url" property or function must be specified');
	};

	// Wrap an optional error callback with a fallback error event.
	var wrapError = function(model, options) {
		var error = options.error;
		options.error = function(resp) {
			if (error) error.call(options.context, model, resp, options);
			model.trigger('error', model, resp, options);
		};
	};

	return Backbone;

}));
};


modules['helpcenter/primary/views/fileinput'] = function(require, exports, module, modules) {
module.exports = Backbone.View.extend({
	el: $('.js-file-view'),

	events: {
		'dragenter .file-input' : 'enter',
		'focus .file-input'     : 'focus',
		'dragleave .file-input' : 'defocus',
		'drop .file-input'      : 'defocus',
		'blur .file-input'      : 'defocus',
		'change .file-input'    : 'change',
		'click .file-input'     : 'click'
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Shows blue border around input field
	 * @param  {Object} e
	 */
	focus: function(e) {
		$(e.target).prevAll('.file-btn').addClass('is-active');
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Removes blue border
	 * @param  {Object} e
	 */
	defocus: function(e) {
		$(e.target).prevAll('.file-btn').removeClass('is-active');
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Focuses file input field and adds new field if required
	 * @param  {Object} e
	 */
	enter: function(e) {
		var elem = $(e.target);
		elem.prevAll('.file-btn').addClass('is-active');

		// If a file is already chosen, append new file input
		// so that previous one doesn't get cleared
		if(elem.val() !== "") {
			e.preventDefault();
			elem.after('<input class="file-input" type="file" name="files[]" multiple>');
			setTimeout(function() {
				elem.prevAll('.file-btn').addClass('is-active');
			}, 10);
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Sets file info
	 * @param  {Object} e
	 */
	change: function(e) {
		var count = 0;
		$(this.el).find('.file-input').each(function() {
		    count = count + $(this)[0].files.length;
		});

		var msgContainer = $(e.target).prevAll('.file-btn').find('.js-inputval');

		if (count == 1) {
			msgContainer.text($(".file-input").val().split('\\').pop());
		} else {
			var tpl_fileInfo = _.template($('.js-tpl-fileinfo').html());
			var tpl_html = tpl_fileInfo({ fileCounter: count });
			msgContainer.text(tpl_html);
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Adds new input field if required
	 * @param  {Object} e
	 */
	click: function(e) {
		var elem = $(e.target);
		if(elem.val() !== "") {
			e.preventDefault();
			elem.after('<input class="file-input" type="file" name="files[]" multiple>');
			elem.parents('.js-file-view').find('.file-input').last().click();
		}
	}
});
};


modules['helpcenter/primary/plugins/froala/table'] = function(require, exports, module, modules) {
/*!
 * froala_editor v2.3.4 (https://www.froala.com/wysiwyg-editor)
 * License https://froala.com/wysiwyg-editor/terms/
 * Copyright 2014-2016 Froala Labs
 */

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof module === 'object' && module.exports) {
        // Node/CommonJS
        module.exports = function( root, jQuery ) {
            if ( jQuery === undefined ) {
                // require('jQuery') returns a factory that requires window to
                // build a jQuery instance, we normalize how we use modules
                // that require this pattern but the window provided is a noop
                // if it's defined (how jquery works)
                if ( typeof window !== 'undefined' ) {
                    jQuery = require('jquery');
                }
                else {
                    jQuery = require('jquery')(root);
                }
            }
            factory(jQuery);
            return jQuery;
        };
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {

  'use strict';

  $.extend($.FE.POPUP_TEMPLATES, {
    'table.insert': '[_BUTTONS_][_ROWS_COLUMNS_]',
    'table.edit': '[_BUTTONS_]',
    'table.colors': '[_BUTTONS_][_COLORS_]'
  })

  // Extend defaults.
  $.extend($.FE.DEFAULTS, {
    tableInsertMaxSize: 10,
    tableEditButtons: ['tableHeader', 'tableRemove', '|', 'tableRows', 'tableColumns', 'tableStyle', '-', 'tableCells', 'tableCellBackground', 'tableCellVerticalAlign', 'tableCellHorizontalAlign', 'tableCellStyle'],
    tableInsertButtons: ['tableBack', '|'],
    tableResizer: true,
    tableResizerOffset: 5,
    tableResizingLimit: 30,
    tableColorsButtons: ['tableBack', '|'],
    tableColors: [
      '#61BD6D', '#1ABC9C', '#54ACD2', '#2C82C9', '#9365B8', '#475577', '#CCCCCC',
      '#41A85F', '#00A885', '#3D8EB9', '#2969B0', '#553982', '#28324E', '#000000',
      '#F7DA64', '#FBA026', '#EB6B56', '#E25041', '#A38F84', '#EFEFEF', '#FFFFFF',
      '#FAC51C', '#F37934', '#D14841', '#B8312F', '#7C706B', '#D1D5D8', 'REMOVE'
    ],
    tableColorsStep: 7,
    tableCellStyles: {
      'fr-highlighted': 'Highlighted',
      'fr-thick': 'Thick'
    },
    tableStyles: {
      'fr-dashed-borders': 'Dashed Borders',
      'fr-alternate-rows': 'Alternate Rows'
    },
    tableCellMultipleStyles: true,
    tableMultipleStyles: true,
    tableInsertHelper: true,
    tableInsertHelperOffset: 15
  });

  $.FE.PLUGINS.table = function (editor) {
    var $resizer;
    var $insert_helper;
    var mouseDownCellFlag;
    var mouseDownFlag;
    var mouseDownCell;
    var mouseMoveTimer;
    var resizingFlag;

    /*
     * Show the insert table popup.
     */
    function _showInsertPopup () {
      var $btn = editor.$tb.find('.fr-command[data-cmd="insertTable"]');

      var $popup = editor.popups.get('table.insert');
      if (!$popup) $popup = _initInsertPopup();

      if (!$popup.hasClass('fr-active')) {

        // Insert table popup
        editor.popups.refresh('table.insert');
        editor.popups.setContainer('table.insert', editor.$tb);

        // Insert table left and top position.
        var left = $btn.offset().left + $btn.outerWidth() / 2;
        var top = $btn.offset().top + (editor.opts.toolbarBottom ? 10 : $btn.outerHeight() - 10);
        editor.popups.show('table.insert', left, top, $btn.outerHeight());
      }
    }

    /*
     * Show the table edit popup.
     */
    function _showEditPopup () {
      // Set popup position.
      var map = _tableMap();
      if (map) {
        var $popup = editor.popups.get('table.edit');
        if (!$popup) $popup = _initEditPopup();

        editor.popups.setContainer('table.edit', $(editor.opts.scrollableContainer));
        var offset = _selectionOffset(map);
        var left = (offset.left + offset.right) / 2;
        var top = offset.bottom;

        editor.popups.show('table.edit', left, top, offset.bottom - offset.top);

        // Disable toolbar buttons only if there are more than one cells selected.
        if (editor.edit.isDisabled()) {
          // Disable toolbar.
          editor.toolbar.disable();

          // Allow text selection.
          editor.$el.removeClass('fr-no-selection');
          editor.edit.on();

          editor.selection.setAtEnd(editor.$el.find('.fr-selected-cell:last').get(0));
          editor.selection.restore();
          editor.button.bulkRefresh();
        }
      }
    }

    /*
     * Show the table colors popup.
     */
    function _showColorsPopup () {
      // Set popup position.
      var map = _tableMap();
      if (map) {
        var $popup = editor.popups.get('table.colors');
        if (!$popup) $popup = _initColorsPopup();

        editor.popups.setContainer('table.colors', $(editor.opts.scrollableContainer));
        var offset = _selectionOffset(map);
        var left = (offset.left + offset.right) / 2;
        var top = offset.bottom;

        // Refresh selected color.
        _refreshColor();

        editor.popups.show('table.colors', left, top, offset.bottom - offset.top);
      }
    }

    /*
     * Called on table edit popup hide.
     */
    function _hideEditPopup () {
      // Enable toolbar.
      if (selectedCells().length === 0) {
        editor.toolbar.enable();
      }
    }

    /**
     * Init the insert table popup.
     */
    function _initInsertPopup (delayed) {
      if (delayed) {
        editor.popups.onHide('table.insert', function () {
          // Clear previous cell selection.
          editor.popups.get('table.insert').find('.fr-table-size .fr-select-table-size > span[data-row="1"][data-col="1"]').trigger('mouseenter');
        });

        return true;
      }

      // Table buttons.
      var table_buttons = '';
      if (editor.opts.tableInsertButtons.length > 0) {
        table_buttons = '<div class="fr-buttons">' + editor.button.buildList(editor.opts.tableInsertButtons) + '</div>';
      }

      var template = {
        buttons: table_buttons,
        rows_columns: _insertTableHtml()
      };

      var $popup = editor.popups.create('table.insert', template);

      // Initialize insert table grid events.
      editor.events.$on($popup, 'mouseenter', '.fr-table-size .fr-select-table-size .fr-table-cell', function (e) {
        var $table_cell = $(e.currentTarget);
        var row = $table_cell.data('row');
        var col = $table_cell.data('col');
        var $select_size = $table_cell.parent();

        // Update size in title.
        $select_size.siblings('.fr-table-size-info').html(row + ' &times; ' + col);

        // Remove hover class from all cells.
        $select_size.find('> span').removeClass('hover');

        // Add hover class only to the correct cells.
        for (var i = 1; i <= editor.opts.tableInsertMaxSize; i++) {
          for (var j = 0; j <= editor.opts.tableInsertMaxSize; j++) {
            var $cell = $select_size.find('> span[data-row="' + i + '"][data-col="' + j + '"]');

            if (i <= row && j <= col) {
              $cell.addClass('hover');
            } else if ((i <= row + 1 || (i <= 2 && !editor.helpers.isMobile()))) {
              $cell.css('display', 'inline-block');
            } else if (i > 2 && !editor.helpers.isMobile()) {
              $cell.css('display', 'none');
            }
          }
        }
      }, true);

      return $popup;
    }

    /*
     * The HTML for insert table grid.
     */
    function _insertTableHtml () {
      // Grid html
      var rows_columns = '<div class="fr-table-size"><div class="fr-table-size-info">1 &times; 1</div><div class="fr-select-table-size">'

      for (var i = 1; i <= editor.opts.tableInsertMaxSize; i++) {
        for (var j = 1; j <= editor.opts.tableInsertMaxSize; j++) {
          var display = 'inline-block';

          // Display only first 2 rows.
          if (i > 2 && !editor.helpers.isMobile()) {
            display = 'none';
          }

          var cls = 'fr-table-cell ';
          if (i == 1 && j == 1) {
            cls += ' hover';
          }

          rows_columns += '<span class="fr-command ' + cls + '" data-cmd="tableInsert" data-row="' + i + '" data-col="' + j + '" data-param1="' + i + '" data-param2="' + j + '" style="display: ' + display + ';"><span></span></span>';
        }
        rows_columns += '<div class="new-line"></div>';
      }

      rows_columns += '</div></div>';

      return rows_columns;
    }

    /**
     * Init the table edit popup.
     */
    function _initEditPopup (delayed) {
      if (delayed) {
        editor.popups.onHide('table.edit', _hideEditPopup);

        return true;
      }

      // Table buttons.
      var table_buttons = '';
      if (editor.opts.tableEditButtons.length > 0) {
        table_buttons = '<div class="fr-buttons">' + editor.button.buildList(editor.opts.tableEditButtons) + '</div>';
      }

      var template = {
        buttons: table_buttons
      };

      var $popup = editor.popups.create('table.edit', template);

      editor.events.$on(editor.$wp, 'scroll.table-edit', function () {
        if (editor.popups.isVisible('table.edit')) {
          _showEditPopup();
        }
      });

      return $popup;
    }

    /*
     * Init the table cell background popup.
     */
    function _initColorsPopup () {
      // Table colors buttons.
      var table_buttons = '';
      if (editor.opts.tableColorsButtons.length > 0) {
        table_buttons = '<div class="fr-buttons fr-table-colors-buttons">' + editor.button.buildList(editor.opts.tableColorsButtons) + '</div>';
      }

      var template = {
        buttons: table_buttons,
        colors: _colorsHTML()
      };

      var $popup = editor.popups.create('table.colors', template);

      editor.events.$on(editor.$wp, 'scroll.table-colors', function () {
        if (editor.popups.isVisible('table.colors')) {
          _showColorsPopup();
        }
      });

      return $popup;
    }

    /*
     * HTML for the table colors.
     */
    function _colorsHTML () {
      // Create colors html.
      var colors_html = '<div class="fr-table-colors">';

      // Add colors.
      for (var i = 0; i < editor.opts.tableColors.length; i++) {
        if (i !== 0 && i % editor.opts.tableColorsStep === 0) {
          colors_html += '<br>';
        }

        if (editor.opts.tableColors[i] != 'REMOVE') {
          colors_html += '<span class="fr-command" style="background: ' + editor.opts.tableColors[i] + ';" data-cmd="tableCellBackgroundColor" data-param1="' + editor.opts.tableColors[i] + '"></span>';
        }

        else {
          colors_html += '<span class="fr-command" data-cmd="tableCellBackgroundColor" data-param1="REMOVE" title="' + editor.language.translate('Clear Formatting') + '"><i class="fa fa-eraser"></i></span>';
        }
      }

      colors_html += '</div>';

      return colors_html;
    }

    /*
     * Show the current selected color.
     */
    function _refreshColor () {
      var $popup = editor.popups.get('table.colors');
      var $cell = editor.$el.find('.fr-selected-cell:first');

      // Remove current color selection.
      $popup.find('.fr-selected-color').removeClass('fr-selected-color');

      // Find the selected color.
      $popup.find('span[data-param1="' + editor.helpers.RGBToHex($cell.css('background-color')) + '"]').addClass('fr-selected-color');
    }

    /*
     * Insert table method.
     */
    function insert (rows, cols) {
      // Create table HTML.
      var table = '<table style="width: 100%;"><tbody>';
      var cell_width = 100 / cols;
      var i;
      var j;

      for (i = 0; i < rows; i++) {
        table += '<tr>';

        for (j = 0; j < cols; j++) {
          table += '<td style="width: ' + cell_width.toFixed(4) + '%;">';
          if (i === 0 && j === 0)table += $.FE.MARKERS;
          table += '<br></td>';
        }
        table += '</tr>';
      }
      table += '</tbody></table>';

      editor.html.insert(table);

      // Update cursor position.
      editor.selection.restore()
    }

    /*
     * Delete table method.
     */
    function remove () {
      if (selectedCells().length > 0) {
        var $current_table = selectedTable();

        // Update cursor position.
        editor.selection.setBefore($current_table.get(0)) || editor.selection.setAfter($current_table.get(0));
        editor.selection.restore();

        // Hide table edit popup.
        editor.popups.hide('table.edit');

        // Delete table.
        $current_table.remove();

        // Enable toolbar.
        editor.toolbar.enable();
      }
    }

    /*
     * Add table header.
     */
    function addHeader () {
      var $table = selectedTable();

      // If there is a selection in the table and the table doesn't have a header already.
      if ($table.length > 0 && $table.find('th').length === 0) {
        // Create header HTML.
        var thead = '<thead><tr>';

        var i;
        var col = 0;

        // Get first row and count table columns.
        $table.find('tr:first > td').each (function () {
          var $td = $(this);

          col += parseInt($td.attr('colspan'), 10) || 1;
        });

        // Add cells.
        for (i = 0; i < col; i++) {
          thead += '<th><br></th>';
        }

        thead += '</tr></thead>'

        $table.prepend(thead);

        // Reposition table edit popup.
        _showEditPopup();
      }
    }

    /*
     * Remove table header.
     */
    function removeHeader () {
      var $current_table = selectedTable();
      var $table_header = $current_table.find('thead');

      // Table has a header.
      if ($table_header.length > 0) {
        // If table does not have any other rows then delete table.
        if ($current_table.find('tbody tr').length === 0) {
          // Remove table.
          remove();
        }

        else {
          $table_header.remove();

          // Reposition table edit popup if there any more selected celss.
          if (selectedCells().length > 0) {
            _showEditPopup();
          }
          else {
            // Hide popup.
            editor.popups.hide('table.edit');

            // Update cursor position.
            var td = $current_table.find('tbody tr:first td:first').get(0);
            if (td) {
              editor.selection.setAtEnd(td);
              editor.selection.restore();
            }
          }
        }
      }
    }

    /*
     * Insert row method.
     */
    function insertRow (position) {
      var $table = selectedTable();

      // We have selection in a table.
      if ($table.length > 0) {
        // Cannot insert row above the table header.
        if (editor.$el.find('th.fr-selected-cell').length > 0 && position == 'above') {
          return;
        }

        var i;
        var ref_row;

        // Create a table map.
        var map = _tableMap();

        // Get selected cells from the table.
        var selection = _currentSelection(map);

        // Reference row.
        if (position == 'above') {
          ref_row = selection.min_i;
        } else {
          ref_row = selection.max_i;
        }

        // Create row HTML.
        var tr = '<tr>';

        // Add cells.
        for (i = 0; i < map[ref_row].length; i++) {
          // If cell has rowspan we should increase it.
          if ((position == 'below' && ref_row < map.length - 1 && map[ref_row][i] == map[ref_row + 1][i]) ||
              (position == 'above' && ref_row > 0 && map[ref_row][i] == map[ref_row - 1][i])) {

            // Don't increase twice for colspan.
            if (i === 0 || (i > 0 && map[ref_row][i] != map[ref_row][i - 1])) {
              var $cell = $(map[ref_row][i]);
              $cell.attr('rowspan', parseInt($cell.attr('rowspan'), 10) + 1);
            }

          } else {
            tr += '<td><br></td>';
          }
        }

        // Close row tag.
        tr += '</tr>';

        var $ref_row = $($table.find('tr').not($table.find('table tr')).get(ref_row));

        // Insert new row.
        if (position == 'below') {
          // Table edit popup should not change position.
          $ref_row.after(tr);
        }
        else if (position == 'above') {
          $ref_row.before(tr);

          // Reposition table edit popup.
          if (editor.popups.isVisible('table.edit')) {
            _showEditPopup();
          }
        }
      }
    }

    /*
     * Delete row method.
     */
    function deleteRow () {
      var $table = selectedTable();

      // We have selection in a table.
      if ($table.length > 0) {
        var i;
        var j;
        var $row;

        // Create a table map.
        var map = _tableMap();

        // Get selected cells from the table.
        var selection = _currentSelection(map);

        // If all the rows are selected then delete the entire table.
        if (selection.min_i === 0 && selection.max_i == map.length - 1) {
          remove();

        } else {
          // We should delete selected rows.
          for (i = selection.max_i; i >= selection.min_i; i--) {
            $row = $($table.find('tr').not($table.find('table tr')).get(i));

            // Go through the table map to check for rowspan on the row to delete.
            for (j = 0; j < map[i].length; j++) {
              // Don't do this twice if we have a colspan.
              if (j === 0 || map[i][j] != map[i][j - 1]) {
                var $cell = $(map[i][j]);

                // We should decrease rowspan.
                if (parseInt($cell.attr('rowspan'), 10) > 1) {
                  var rowspan = parseInt($cell.attr('rowspan'), 10) - 1;

                  if (rowspan == 1) {
                    $cell.removeAttr('rowspan');
                  } else {
                    $cell.attr('rowspan', rowspan);
                  }
                }

                // We might need to move tds on the row below if we have a rowspan that starts here.
                if (i < map.length - 1 && map[i][j] == map[i + 1][j] && (i === 0 || map[i][j] != map[i - 1][j])) {
                  // Move td to the row below.
                  var td = map[i][j];
                  var col = j;

                  // Go back until we get the last element (we might have colspan).
                  while (col > 0 && map[i][col] == map[i][col - 1]) {
                    col--;
                  }

                  // Add td at the beginning of the row below.
                  if (col === 0) {
                    $($table.find('tr').not($table.find('table tr')).get(i + 1)).prepend(td);

                  } else {
                    $(map[i + 1][col - 1]).after(td);
                  }
                }
              }
            }

            // Remove tbody or thead if there are no more rows.
            var $row_parent = $row.parent();
            $row.remove();
            if ($row_parent.find('tr').length === 0) {
              $row_parent.remove();
            }

            // Table has changed.
            map = _tableMap($table);
          }

          _updateCellSpan(0, map.length - 1, 0, map[0].length - 1, $table);

          // Update cursor position
          if (selection.min_i > 0) {
            // Place cursor in the row above selection.
            editor.selection.setAtEnd(map[selection.min_i - 1][0]);
          }
          else {
            // Place cursor in the row below selection.
            editor.selection.setAtEnd(map[0][0]);
          }
          editor.selection.restore();

          // Hide table edit popup.
          editor.popups.hide('table.edit');
        }
      }
    }

    /*
     * Insert column method.
     */
    function insertColumn (position) {
      var $table = selectedTable();

      // We have selection in a table.
      if ($table.length > 0) {
        // Create a table map.
        var map = _tableMap();

        // Get selected cells from the table.
        var selection = _currentSelection(map);

        // Reference row.
        var ref_col;

        if (position == 'before') {
          ref_col = selection.min_j;
        } else {
          ref_col = selection.max_j;
        }

        // Old and new column widths.
        var old_width = 100 / map[0].length;
        var new_width = 100 / (map[0].length + 1);

        // Go through all cells and get their initial (old) widths.
        var $cell;

        $table.find('th, td').each (function () {
          $cell = $(this);
          $cell.data('old-width', $cell.outerWidth() / $table.outerWidth() * 100);
        });

        // Parse each row to insert a new td.
        $table.find('tr').not($table.find('table tr')).each (function (index) {
          // Get the exact td index before / after which we have to add the new td.
          // ref_col means the table column number, but this is not the same with the td number in a row.
          // We might have colspan or rowspan greater than 1.
          var $row = $(this);
          var col_no = 0;
          var td_no = 0;
          var ref_td;

          // Start with the first td (td_no = 0) in the current row.
          // Sum colspans (col_no) to see when we reach ref_col.
          // Summing colspans we get the same number with the table column number.
          while (col_no - 1 < ref_col) {
            // Get current td.
            ref_td = $row.find('> th, > td').get(td_no);

            // There are no more tds in this row.
            if (!ref_td) {
              ref_td = null;
              break;
            }

            // The current td is the same with the td from the table map.
            if (ref_td == map[index][col_no]) {
              // The current td might have colspan.
              col_no += parseInt($(ref_td).attr('colspan'), 10) || 1;

              // Go to the next td on the current row.
              td_no++;
            }

            // If current td is not the same with the td from the table map.
            // This means that this table cell (map[index][td_no]) has rowspan.
            // There is at least one td less on this row due to rowspan (based on map[index][td_no] colspan value).
            // We want to count this as a column as well.
            else {
              col_no += parseInt($(map[index][col_no]).attr('colspan'), 10) || 1;

              // ref_td is one td ahead. Get previous td if we want to insert column after.
              if (position == 'after') {
                // There is a rowspan and so ref_td is the first td, but it is not in the first column.
                if (td_no === 0) {
                  ref_td = -1;

                } else {
                  ref_td = $row.find('> th, > td').get(td_no - 1);
                }
              }
            }
          }

          var $ref_td = $(ref_td);

          // If the computed column number is higher than the reference number
          // then on this row we have a colspan longer than the reference column.
          // When adding a column after we should increase colspan on this row.
          //
          // If we want to add a column before, but the td on the reference column is
          // the same with the previous one then we have a td with colspan that
          // starts before the column reference number.
          // When adding a column before we should increase colspan on this row.
          if ((position == 'after' && col_no - 1 > ref_col) ||
              (position == 'before' && ref_col > 0 && map[index][ref_col] == map[index][ref_col - 1])) {

            // Don't increase twice for rowspan.
            if (index === 0 || (index > 0 && map[index][ref_col] != map[index - 1][ref_col])) {
              var colspan = parseInt($ref_td.attr('colspan'), 10) + 1;

              $ref_td.attr('colspan', colspan)

              // Update this cell's width.
              $ref_td.css('width', ($ref_td.data('old-width') * new_width / old_width + new_width).toFixed(4) + '%');
              $ref_td.removeData('old-width');
            }

          }

          else {
            // Create HTML for a new cell.
            var td;

            // Might be a td or a th.
            if ($row.find('th').length > 0) {
              td = '<th style="width: ' + new_width.toFixed(4) + '%;"><br></th>';
            }
            else {
              td = '<td style="width: ' + new_width.toFixed(4) + '%;"><br></td>';
            }

            // Insert exactly at the beginning.
            if (ref_td == -1) {
              $row.prepend(td);

            // Insert exactly at the end.
            } else if (ref_td == null) {
              $row.append(td);

            // Insert td on the current row.
            } else {
              if (position == 'before') {
                $ref_td.before(td);
              }

              else if (position == 'after') {
                $ref_td.after(td);
              }
            }
          }
        });

        // Parse each row to update cells' width.
        $table.find('th, td').each (function () {
          $cell = $(this);

          // Update width and remove data.
          if ($cell.data('old-width')) {
            $cell.css('width', ($cell.data('old-width') * new_width / old_width).toFixed(4) + '%');
            $cell.removeData('old-width')
          }
        });

        // Reposition table edit popup.
        if (editor.popups.isVisible('table.edit')) {
          _showEditPopup();
        }
      }
    }

    /*
     * Delete column method.
     */
    function deleteColumn () {
      var $table = selectedTable();

      // We have selection in a table.
      if ($table.length > 0) {
        var i;
        var j;
        var $cell;

        // Create a table map.
        var map = _tableMap();

        // Get selected cells from the table.
        var selection = _currentSelection(map);

        // If all the rows are selected then delete the entire table.
        if (selection.min_j === 0 && selection.max_j == map[0].length - 1) {
          remove();

        } else {
          // Old and new column widths.
          var old_width = 100 / map[0].length;
          var new_width = 100 / (map[0].length - selection.max_j + selection.min_j - 1);

          // Go through all cells and get their initial (old) widths.
          $table.find('th, td').each (function () {
            $cell = $(this);

            if (!$cell.hasClass('fr-selected-cell')) {
              $cell.data('old-width', $cell.outerWidth() / $table.outerWidth() * 100);
            }
          });

          // We should delete selected columns.
          for (j = selection.max_j; j >= selection.min_j; j--) {
            // Go through the table map to check for colspan.
            for (i = 0; i < map.length; i++) {
              // Don't do this twice if we have a rowspan.
              if (i === 0 || map[i][j] != map[i - 1][j]) {
                // We should decrease colspan.
                $cell = $(map[i][j]);

                if ((parseInt($cell.attr('colspan'), 10) || 1) > 1) {
                  var colspan = parseInt($cell.attr('colspan'), 10) - 1;

                  if (colspan == 1) {
                    $cell.removeAttr('colspan');
                  } else {
                    $cell.attr('colspan', colspan);
                  }

                  // Update cell width.
                  $cell.css('width', (($cell.data('old-width') - _columnWidth(j, map)) * new_width / old_width).toFixed(4) + '%');
                  $cell.removeData('old-width');

                // If there is no colspan delete the cell.
                } else {
                  // We might need to delete the row too if it is empty.
                  var $row = $($cell.parent().get(0));

                  $cell.remove();

                  // Check if there are any more tds in the current row.
                  if ($row.find('> th, > td').length === 0) {
                    // Check if it is okay to delete the tr.
                    if ($row.prev().length === 0 || $row.next().length === 0 ||
                        $row.prev().find('> th[rowspan], > td[rowspan]').length < $row.prev().find('> th, > td').length) {
                      $row.remove();
                    }
                  }
                }
              }
            }
          }

          _updateCellSpan(0, map.length - 1, 0, map[0].length - 1, $table);

          // Update cursor position
          if (selection.min_j > 0) {
            // Place cursor in the column before selection.
            editor.selection.setAtEnd(map[selection.min_i][selection.min_j - 1]);
          }
          else {
            // Place cursor in the column after selection.
            editor.selection.setAtEnd(map[selection.min_i][0]);
          }
          editor.selection.restore();

          // Hide table edit popup.
          editor.popups.hide('table.edit');

          // Scale current cells' width after column has been deleted.
          $table.find('th, td').each (function () {
            $cell = $(this);

            // Update width and remove data.
            if ($cell.data('old-width')) {
              $cell.css('width', ($cell.data('old-width') * new_width / old_width).toFixed(4) + '%');
              $cell.removeData('old-width')
            }
          });
        }
      }
    }

    /*
     * Update or remove colspan attribute.
     */
    function _updateColspan (min_j, max_j, $table) {
      var i;
      var j;
      var k;
      var first_span;
      var span;
      var decrease = 0;

      // Create a table map.
      var map = _tableMap($table);

      // A column might have been deleted.
      max_j = Math.min(max_j, map[0].length - 1);

      // Find out how much we should decrease colspan.
      // Parsing only the first row is enough.
      for (j = min_j; j <= max_j; j++) {
        // This cell has colspan and has already been checked.
        if (j > min_j && map[0][j] == map[0][j - 1]) {
          continue;
        }

        // Current cell colspan
        first_span = parseInt(map[0][j].getAttribute('colspan'), 10) || 1;

        // Cell has colspan between min_j and max_j.
        /* j + 1 will never exceed the number of columns in a table.
         * A colspan is detected before the last column and all next cells on that row are skipped.
         */
        if (first_span > 1 && map[0][j] == map[0][j + 1]) {
          // The value we should decrease rowspan with.
          decrease = first_span - 1;

          // Check all columns on the current row.
          // We found a colspan on the first row (i = 0), continue with second row (i = 1).
          for (i = 1; i < map.length; i++) {
            // This cell has rowspan and has already been checked.
            if (map[i][j] == map[i - 1][j]) {
              continue;
            }

            // Look for a colspan on the same columns.
            for (k = j; k < j + first_span; k++) {
              span = parseInt(map[i][k].getAttribute('colspan'), 10) || 1;

              // There are other cells with colspan on this column.
              /* k + 1 will never exceed the number of columns in a table.
               * A colspan is detected before the last column and all next cells on that row are skipped.
               */
              if (span > 1 && map[i][k] == map[i][k + 1]) {
                decrease = Math.min(decrease, span - 1);

                // Skip colspan.
                k += decrease;
              }
              else {
                decrease = Math.max (0, decrease - 1);

                // Stop if decrease reaches 0.
                if (!decrease) {
                  break;
                }
              }
            }

            // Stop looking on the next columns if decrease reaches 0.
            if (!decrease) {
              break;
            }
          }
        }
      }

      // Update colspan attribute.
      if (decrease) {
        _decreaseCellSpan(map, decrease, 'colspan', 0, map.length - 1, min_j, max_j);
      }
    }

    /*
     * Update or remove rowspan attribute.
     */
    function _updateRowspan (min_i, max_i, $table) {
      var i;
      var j;
      var k;
      var first_span;
      var span;
      var decrease = 0;

      // Create a table map.
      var map = _tableMap($table);

      // A row might have been deleted.
      max_i = Math.min(max_i, map.length - 1);

      // Find out how much we should decrease rowspan.
      // Parsing only the first column is enough.
      for (i = min_i; i <= max_i; i++) {
        // This cell has rowspan and has already been checked.
        if (i > min_i && map[i][0] == map[i - 1][0]) {
          continue;
        }

        // Current cell rowspan
        first_span = parseInt(map[i][0].getAttribute('rowspan'), 10) || 1;

        // Cell has rowspan between min_i and max_i.
        /* i + 1 will never exceed the number of rows in a table.
         * A rowspan is detected before the last row and all next cells on that column are skipped.
         */
        if (first_span > 1 && map[i][0] == map[i + 1][0]) {
          // The value we should decrease rowspan with.
          decrease = first_span - 1;

          // Check all columns on the current row.
          // We found a rowspan on the first column (j = 0), continue with second column (j = 1).
          for (j = 1; j < map[0].length; j++) {
            // This cell has colspan and has already been checked.
            if (map[i][j] == map[i][j - 1]) {
              continue;
            }

            // Look for a rowspan on the same rows.
            for (k = i; k < i + first_span; k++) {
              span = parseInt(map[k][j].getAttribute('rowspan'), 10) || 1;

              // There are other cells with rowspan on this row.
              /* k + 1 will never exceed the number of rows in a table.
               * A rowspan is detected before the last row and all next cells on that column are skipped.
               */
              if (span > 1 && map[k][j] == map[k + 1][j]) {
                decrease = Math.min(decrease, span - 1);

                // Skip rowspan.
                k += decrease;
              }
              else {
                decrease = Math.max (0, decrease - 1);

                // Stop if decrease reaches 0.
                if (!decrease) {
                  break;
                }
              }
            }

            // Stop looking on the next columns if decrease reaches 0.
            if (!decrease) {
              break;
            }
          }
        }
      }

      // Update rowspan attribute.
      if (decrease) {
        _decreaseCellSpan(map, decrease, 'rowspan', min_i, max_i, 0, map[0].length - 1);
      }
    }

    /*
     * Decrease the colspan or rowspan with the amount specified.
     */
    function _decreaseCellSpan (map, decrease, span_type, min_i, max_i, min_j, max_j) {
      // Update span attribute.
      var i;
      var j;
      var span;

      // Go only through lines and columns that need to be updated.
      for (i = min_i; i <= max_i; i++) {
        for (j = min_j; j <= max_j; j++) {
          // This cell has rowspan or colspan and has already been checked.
          if ((i > min_i && map[i][j] == map[i - 1][j]) || (j > min_j && map[i][j] == map[i][j - 1])) {
            continue;
          }

          span = parseInt(map[i][j].getAttribute(span_type), 10) || 1;

          // Update cell span.
          if (span > 1) {
            if (span - decrease > 1) map[i][j].setAttribute(span_type, span - decrease);
            else map[i][j].removeAttribute(span_type);
          }
        }
      }
    }

    /*
     * Update or remove colspan and rowspan attributes.
     */
    function _updateCellSpan (min_i, max_i, min_j, max_j, $table) {
      _updateRowspan(min_i, max_i, $table);
      _updateColspan(min_j, max_j, $table);
    }

    /*
     * Merge selected cells method.
     */
    function mergeCells () {
      // We have more than one cell selected in a table. Cannot merge td and th.
      if (selectedCells().length > 1 && (editor.$el.find('th.fr-selected-cell').length === 0 || editor.$el.find('td.fr-selected-cell').length === 0)) {
        // Create a table map.
        var map = _tableMap();

        // Get selected cells.
        var selection = _currentSelection(map);

        var i;
        var $cell;
        var cells = editor.$el.find('.fr-selected-cell');
        var $first_cell = $(cells[0]);
        var $first_row = $first_cell.parent();
        var first_row_cells = $first_row.find('.fr-selected-cell');
        var $current_table = $first_cell.closest('table');
        var content = $first_cell.html();
        var width = 0;

        // Update cell width.
        for (i = 0; i < first_row_cells.length; i++) {
          width += $(first_row_cells[i]).outerWidth();
        }

        $first_cell.css('width', (width / $current_table.outerWidth() * 100).toFixed(4) + '%');

        // Set the colspan for the merged cells.
        if (selection.min_j < selection.max_j) {
          $first_cell.attr('colspan', selection.max_j - selection.min_j + 1);
        }

        // Set the rowspan for the merged cells.
        if (selection.min_i < selection.max_i) {
          $first_cell.attr('rowspan', selection.max_i - selection.min_i + 1);
        }

        // Go through all selected cells to merge their content.
        for (i = 1; i < cells.length; i++) {
          $cell = $(cells[i])

          // If cell is empty, don't add only <br> tags.
          if ($cell.html() != '<br>' && $cell.html() !== '') {
            content += '<br>' + $cell.html();
          }

          // Remove cell.
          $cell.remove();
        }

        // Set the HTML content.
        $first_cell.html(content);
        editor.selection.setAtEnd($first_cell.get(0));
        editor.selection.restore();

        // Enable toolbar.
        editor.toolbar.enable();

        // Update rowspan before removing empty rows (otherwise table map is not correct).
        _updateRowspan(selection.min_i, selection.max_i, $current_table);

        // Merge is done, check if we have empty trs to clean.
        var empty_trs = $current_table.find('tr:empty');

        for (i = empty_trs.length - 1; i >= 0; i--) {
          $(empty_trs[i]).remove();
        }

        // Update colspan after removing empty rows and updating rowspan.
        _updateColspan(selection.min_j, selection.max_j, $current_table);

        // Reposition table edit popup.
        _showEditPopup();
      }
    }

    /*
     * Split cell horizontally method.
     */
    function splitCellHorizontally () {
      // We have only one cell selected in a table.
      if (selectedCells().length == 1) {
        var $selected_cell = editor.$el.find('.fr-selected-cell');
        var $current_row = $selected_cell.parent();
        var $current_table = $selected_cell.closest('table');
        var current_rowspan = parseInt($selected_cell.attr('rowspan'), 10);

        // Create a table map.
        var map = _tableMap();
        var cell_origin = _cellOrigin($selected_cell.get(0), map);

        // Create new td.
        var $new_td = $selected_cell.clone().html('<br>');

        // Cell has rowspan.
        if (current_rowspan > 1) {
          // Split current cell's rowspan.
          var new_rowspan = Math.ceil(current_rowspan / 2);

          if (new_rowspan > 1) {
            $selected_cell.attr('rowspan', new_rowspan);
          } else {
            $selected_cell.removeAttr('rowspan');
          }

          // Update new td's rowspan.
          if (current_rowspan - new_rowspan > 1) {
            $new_td.attr('rowspan', current_rowspan - new_rowspan);
          } else {
            $new_td.removeAttr('rowspan');
          }

          // Find where we should insert the new td.
          var row = cell_origin.row + new_rowspan;
          var col = cell_origin.col === 0 ? cell_origin.col : cell_origin.col - 1;

          // Go back until we find a td on this row. We might have colspans and rowspans.
          while (col >= 0 && (map[row][col] == map[row][col - 1] || (row > 0 && map[row][col] == map[row - 1][col]))) {
            col--;
          }

          if (col == -1) {
            // We couldn't find a previous td on this row. Prepend the new td.
            $($current_table.find('tr').not($current_table.find('table tr')).get(row)).prepend($new_td);

          } else {
            $(map[row][col]).after($new_td);
          }

        } else {
          // Add new row bellow with only one cell.
          var $row = $('<tr>').append($new_td);
          var i;

          // Increase rowspan for all cells on the current row.
          for (i = 0; i < map[0].length; i++) {
            // Don't do this twice if we have a colspan.
            if (i === 0 || map[cell_origin.row][i] != map[cell_origin.row][i - 1]) {
              var $cell = $(map[cell_origin.row][i]);

              if (!$cell.is($selected_cell)) {
                $cell.attr('rowspan', (parseInt($cell.attr('rowspan'), 10) || 1) + 1);
              }
            }
          }

          $current_row.after($row);
        }

        // Remove selection
        _removeSelection();

        // Hide table edit popup.
        editor.popups.hide('table.edit');
      }
    }

    /*
     * Split cell vertically method.
     */
    function splitCellVertically () {
      // We have only one cell selected in a table.
      if (selectedCells().length == 1) {
        var $selected_cell = editor.$el.find('.fr-selected-cell');
        var current_colspan = parseInt($selected_cell.attr('colspan'), 10) || 1;
        var parent_width = $selected_cell.parent().outerWidth();
        var width = $selected_cell.outerWidth();

        // Create new td.
        var $new_td = $selected_cell.clone().html('<br>');

        // Create a table map.
        var map = _tableMap();
        var cell_origin = _cellOrigin($selected_cell.get(0), map);

        if (current_colspan > 1) {
          // Split current colspan.
          var new_colspan = Math.ceil(current_colspan / 2);

          width = _columnsWidth(cell_origin.col, cell_origin.col + new_colspan - 1, map) / parent_width * 100;
          var new_width = _columnsWidth(cell_origin.col + new_colspan, cell_origin.col + current_colspan - 1, map) / parent_width * 100;

          // Update colspan for current cell.
          if (new_colspan > 1) {
            $selected_cell.attr('colspan', new_colspan);
          } else {
            $selected_cell.removeAttr('colspan');
          }

          // Update new td's colspan.
          if (current_colspan - new_colspan > 1) {
            $new_td.attr('colspan', current_colspan - new_colspan);
          } else {
            $new_td.removeAttr('colspan');
          }

          // Update cell width.
          $selected_cell.css('width', width.toFixed(4) + '%');
          $new_td.css('width', new_width.toFixed(4) + '%');

        // Increase colspan for all cells on the current column.
        } else {
          var i;

          for (i = 0; i < map.length; i++) {
            // Don't do this twice if we have a rowspan.
            if (i === 0 || map[i][cell_origin.col] != map[i - 1][cell_origin.col]) {
              var $cell = $(map[i][cell_origin.col]);

              if (!$cell.is($selected_cell)) {
                var colspan = (parseInt($cell.attr('colspan'), 10) || 1) + 1;
                $cell.attr('colspan', colspan);
              }
            }
          }

          // Update cell width.
          width = width / parent_width * 100 / 2;

          $selected_cell.css('width', width.toFixed(4) + '%');
          $new_td.css('width', width.toFixed(4) + '%');
        }

        // Add a new td after the current one.
        $selected_cell.after($new_td);

        // Remove selection
        _removeSelection();

        // Hide table edit popup.
        editor.popups.hide('table.edit');
      }
    }

    /*
     * Set background color to selected cells.
     */
    function setBackground (color) {
      // Set background  color.
      if (color != 'REMOVE') {
        editor.$el.find('.fr-selected-cell').css('background-color', editor.helpers.HEXtoRGB(color));
      }

      // Remove background color.
      else {
        editor.$el.find('.fr-selected-cell').css('background-color', '');
      }
    }

    /*
     * Set vertical align to selected cells.
     */
    function verticalAlign (val) {
      editor.$el.find('.fr-selected-cell').css('vertical-align', val);
    }

    /*
     * Apply horizontal alignment to selected cells.
     */
    function horizontalAlign (val) {
      editor.$el.find('.fr-selected-cell').css('text-align', val);
    }

    /**
     * Apply specific style to selected table or selected cells.
     * val              class to apply.
     * obj              table or selected cells.
     * multiple_styles  editor.opts.tableStyles or editor.opts.tableCellStyles.
     * style            editor.opts.tableStyles or editor.opts.tableCellStyles
     */
    function applyStyle (val, obj, multiple_styles, styles) {
      if (obj.length > 0) {
        // Remove multiple styles.
        if (!multiple_styles) {
          var classes = Object.keys(styles);
          classes.splice(classes.indexOf(val), 1);
          obj.removeClass(classes.join(' '));
        }

        obj.toggleClass(val);
      }
    }

    /*
     * Create a table map.
     */
    function _tableMap ($table) {
      $table = $table || null;

      var map = [];

      if ($table == null && selectedCells().length > 0) {
        $table = selectedTable();
      }

      if ($table) {
        $table.find('tr').not($table.find('table tr')).each (function (row, tr) {
          var $tr = $(tr);

          var c_index = 0;
          $tr.find('> th, > td').each (function (col, td) {
            var $td = $(td);
            var cspan = parseInt($td.attr('colspan'), 10) || 1;
            var rspan = parseInt($td.attr('rowspan'), 10) || 1;

            for (var i = row; i < row + rspan; i++) {
              for (var j = c_index; j < c_index + cspan; j++) {
                if (!map[i]) map[i] = [];
                if (!map[i][j]) {
                  map[i][j] = td;
                } else {
                  c_index++;
                }
              }
            }

            c_index += cspan;
          })
        });

        return map;
      }
    }

    /*
     * Get the i, j coordinates of a cell in the table map.
     * These are the coordinates where the cell starts.
     */
    function _cellOrigin (td, map) {
      for (var i = 0; i < map.length; i++) {
        for (var j = 0; j < map[i].length; j++) {
          if (map[i][j] == td) {
            return {
              row: i,
              col: j
            };
          }
        }
      }
    }

    /*
     * Get the i, j coordinates where a cell ends in the table map.
     */
    function _cellEnds (origin_i, origin_j, map) {
      var max_i = origin_i + 1;
      var max_j = origin_j + 1;

      // Compute max_i
      while (max_i < map.length) {
        if (map[max_i][origin_j] != map[origin_i][origin_j]) {
          max_i--;
          break;
        }

        max_i++;
      }

      if (max_i == map.length) {
        max_i--;
      }

      // Compute max_j
      while (max_j < map[origin_i].length) {
        if (map[origin_i][max_j] != map[origin_i][origin_j]) {
          max_j--;
          break;
        }

        max_j++;
      }

      if (max_j == map[origin_i].length) {
        max_j--;
      }

      return {
        row: max_i,
        col: max_j
      };
    }

    /*
     * Remove selection from cells.
     */
    function _removeSelection () {
      var cells = editor.$el.find('.fr-selected-cell');

      if (cells.length > 0) {
        // Remove selection.
        cells.each (function () {
          var $cell = $(this);

          $cell.removeClass('fr-selected-cell');

          if ($cell.attr('class') === '') {
            $cell.removeAttr('class');
          }
        });
      }
    }

    /*
     * Clear selection to prevent Firefox cell selection.
     */
    function _clearSelection () {
      setTimeout(function () {
        editor.selection.clear();

        // Prevent text selection while selecting multiple cells.
        // Happens in Chrome.
        editor.$el.addClass('fr-no-selection');

        // Cursor will not appear if we don't make blur.
        editor.$el.blur();
      }, 0);
    }

    /*
     * Get current selected cells coordintates.
     */
    function _currentSelection (map) {
      var min_i = map.length;
      var max_i = 0;
      var min_j = map[0].length;
      var max_j = 0;
      var i;
      var cells = editor.$el.find('.fr-selected-cell');

      for (i = 0; i < cells.length; i++) {
        var cellOrigin = _cellOrigin(cells[i], map);
        var cellEnd = _cellEnds(cellOrigin.row, cellOrigin.col, map);

        min_i = Math.min(cellOrigin.row, min_i);
        max_i = Math.max(cellEnd.row, max_i);
        min_j = Math.min(cellOrigin.col, min_j);
        max_j = Math.max(cellEnd.col, max_j);
      }

      return {
        min_i: min_i,
        max_i: max_i,
        min_j: min_j,
        max_j: max_j
      };
    }

    /*
     * Minimum and maximum coordinates for the selection in the table map.
     */
    function _selectionLimits (min_i, max_i, min_j, max_j, map) {
      var first_i = min_i;
      var last_i = max_i;
      var first_j = min_j;
      var last_j = max_j;
      var i;
      var j;
      var cellOrigin;
      var cellEnd;

      // Go through first and last columns.
      for (i = first_i; i <= last_i; i++) {
        // First column.
        if ((parseInt($(map[i][first_j]).attr('rowspan'), 10) || 1) > 1 ||
            (parseInt($(map[i][first_j]).attr('colspan'), 10) || 1) > 1) {
          cellOrigin = _cellOrigin(map[i][first_j], map);
          cellEnd = _cellEnds(cellOrigin.row, cellOrigin.col, map);

          first_i = Math.min(cellOrigin.row, first_i);
          last_i = Math.max(cellEnd.row, last_i);
          first_j = Math.min(cellOrigin.col, first_j);
          last_j = Math.max(cellEnd.col, last_j);
        }

        // Last column.
        if ((parseInt($(map[i][last_j]).attr('rowspan'), 10) || 1) > 1 ||
            (parseInt($(map[i][last_j]).attr('colspan'), 10) || 1) > 1) {
          cellOrigin = _cellOrigin(map[i][last_j], map);
          cellEnd = _cellEnds(cellOrigin.row, cellOrigin.col, map);

          first_i = Math.min(cellOrigin.row, first_i);
          last_i = Math.max(cellEnd.row, last_i);
          first_j = Math.min(cellOrigin.col, first_j);
          last_j = Math.max(cellEnd.col, last_j);
        }
      }

      // Go through first and last rows.
      for (j = first_j; j <= last_j; j++) {
        // First row.
        if ((parseInt($(map[first_i][j]).attr('rowspan'), 10) || 1) > 1 ||
            (parseInt($(map[first_i][j]).attr('colspan'), 10) || 1) > 1) {
          cellOrigin = _cellOrigin(map[first_i][j], map);
          cellEnd = _cellEnds(cellOrigin.row, cellOrigin.col, map);

          first_i = Math.min(cellOrigin.row, first_i);
          last_i = Math.max(cellEnd.row, last_i);
          first_j = Math.min(cellOrigin.col, first_j);
          last_j = Math.max(cellEnd.col, last_j);
        }

        // Last column.
        if ((parseInt($(map[last_i][j]).attr('rowspan'), 10) || 1) > 1 ||
            (parseInt($(map[last_i][j]).attr('colspan'), 10) || 1) > 1) {
          cellOrigin = _cellOrigin(map[last_i][j], map);
          cellEnd = _cellEnds(cellOrigin.row, cellOrigin.col, map);

          first_i = Math.min(cellOrigin.row, first_i);
          last_i = Math.max(cellEnd.row, last_i);
          first_j = Math.min(cellOrigin.col, first_j);
          last_j = Math.max(cellEnd.col, last_j);
        }
      }

      if (first_i == min_i && last_i == max_i && first_j == min_j && last_j == max_j) {
        return {
          min_i: min_i,
          max_i: max_i,
          min_j: min_j,
          max_j: max_j
        };

      } else {
        return _selectionLimits(first_i, last_i, first_j, last_j, map);
      }
    }

    /*
     * Get the left and right offset position for the current selection.
     */
    function _selectionOffset (map) {
      var selection = _currentSelection(map);

      // Top left cell.
      var $tl = $(map[selection.min_i][selection.min_j]);

      // Top right cell.
      var $tr = $(map[selection.min_i][selection.max_j]);

      // Bottom left cell.
      var $bl = $(map[selection.max_i][selection.min_j]);

      var left = $tl.offset().left
      var right = $tr.offset().left + $tr.outerWidth();
      var top = $tl.offset().top;
      var bottom = $bl.offset().top + $bl.outerHeight();

      return {
        left: left,
        right: right,
        top: top,
        bottom: bottom
      };
    }

    /*
     * Select table cells
     */
    function _selectCells (firstCell, lastCell) {
      // If the first and last cells are the same then just select it.
      if ($(firstCell).is(lastCell)) {
        // Remove previous selection.
        _removeSelection();

        // Enable editor toolbar.
        editor.edit.on();

        $(firstCell).addClass('fr-selected-cell');

      // Select multiple celss.
      } else {
        // Prevent Firefox cell selection.
        _clearSelection();

        // Turn editor toolbar off.
        editor.edit.off();

        // Create a table map.
        var map = _tableMap();

        // Get first and last cell's i and j map coordinates.
        var firstCellOrigin = _cellOrigin(firstCell, map);
        var lastCellOrigin = _cellOrigin(lastCell, map);

        // Some cells between these coordinates might have colspan or rowspan.
        // The selected area exceeds first and last cells' coordinates.
        var limits = _selectionLimits(Math.min(firstCellOrigin.row, lastCellOrigin.row),
                                                  Math.max(firstCellOrigin.row, lastCellOrigin.row),
                                                  Math.min(firstCellOrigin.col, lastCellOrigin.col),
                                                  Math.max(firstCellOrigin.col, lastCellOrigin.col),
                                                  map);
        // Remove previous selection.
        _removeSelection();

        // Select all cells between the first and last cell.
        for (var i = limits.min_i; i <= limits.max_i; i++) {
          for (var j = limits.min_j; j <= limits.max_j; j++) {
            $(map[i][j]).addClass('fr-selected-cell');
          }
        }
      }
    }

    /*
     * Get the cell under the mouse cursor.
     */
    function _getCellUnder (e) {
      var cell = null;
      var $target = $(e.target);

      if (e.target.tagName == 'TD' || e.target.tagName == 'TH') {
        cell = e.target;
      } else if ($target.closest('td').length > 0) {
        cell = $target.closest('td').get(0);
      } else if ($target.closest('th').length > 0) {
        cell = $target.closest('th').get(0);
      }

      // Cell should reside inside editor.
      if (editor.$el.find(cell).length === 0) return null;

      return cell;
    }

    /*
     * Stop table cell editing and allow text editing.
     */
    function _stopEdit () {
      // Clear previous selection.
      _removeSelection();

      // Hide table edit popup.
      editor.popups.hide('table.edit');
    }

    /*
     * Mark that mouse is down.
     */
    function _mouseDown (e) {
      var cell = _getCellUnder(e);

      // Stop table editing if user clicks outside the table.
      if (selectedCells().length > 0 && !cell) {
        _stopEdit();
      }

      // Only do mouseDown if the editor is not disabled by user.
      if (!editor.edit.isDisabled() || editor.popups.isVisible('table.edit')) {
        // On left click.
        if (e.which == 1 && !(e.which == 1 && editor.helpers.isMac() && e.ctrlKey)) {
          mouseDownFlag = true;

          // User clicked on a table cell.
          if (cell) {
            // We always have to clear previous selection except when using shift key to select multiple cells.
            if (selectedCells().length > 0 && !e.shiftKey) {
              _stopEdit();
            }

            e.stopPropagation();

            editor.events.trigger('image.hideResizer');
            editor.events.trigger('video.hideResizer');

            // Keep record of left mouse click being down
            mouseDownCellFlag = true;

            var tag_name = cell.tagName.toLowerCase();

            // Select multiple cells using Shift key
            if (e.shiftKey && editor.$el.find(tag_name + '.fr-selected-cell').length > 0) {

              // Cells must be in the same table.
              if ($(editor.$el.find(tag_name + '.fr-selected-cell').closest('table')).is($(cell).closest('table'))) {
                // Select cells between.
                _selectCells(mouseDownCell, cell);

              // Do nothing if cells are not in the same table.
              } else {
                // Prevent Firefox selection.
                _clearSelection();
              }
            }

            else {
              // Prevent Firefox selection for ctrl / cmd key.
              // https://github.com/froala/wysiwyg-editor/issues/1323:
              //  - we have more than one cell selected or
              //  - selection is starting in another cell than the one we clicked on.
              if ((editor.keys.ctrlKey(e) || e.shiftKey) && (selectedCells().length > 1 || ($(cell).find(editor.selection.element()).length === 0 && !$(cell).is(editor.selection.element())))) {
                _clearSelection();
              }

              // Save cell where mouse has been clicked
              mouseDownCell = cell;

              // Select cell.
              _selectCells(mouseDownCell, mouseDownCell);
            }
          }
        }

        // On right click stop table editing.
        else if ((e.which == 3 || (e.which == 1 && editor.helpers.isMac() && e.ctrlKey)) && cell) {
          _stopEdit();
        }
      }
    }

    /*
     * Notify that mouse is no longer pressed.
     */
    function _mouseUp (e) {
      // User clicked somewhere else in the editor (except the toolbar).
      // We need this because mouse down is not triggered outside the editor.
      if (!mouseDownCellFlag && !editor.$tb.is(e.target) && !editor.$tb.is($(e.target).closest(editor.$tb.get(0)))) {
        if (selectedCells().length > 0) {
          editor.toolbar.enable();
        }

        _removeSelection();
      }

      // On left click.
      if (e.which == 1 && !(e.which == 1 && editor.helpers.isMac() && e.ctrlKey)) {
        mouseDownFlag = false;

        // Mouse down was in a table cell.
        if (mouseDownCellFlag) {
          // Left click is no longer pressed.
          mouseDownCellFlag = false;

          var cell = _getCellUnder(e);

          // If we have one selected cell and mouse is lifted somewhere else.
          if (!cell && selectedCells().length == 1) {
            // We have a text selection and not cell selection.
            _removeSelection();
          }

          // If there are selected cells then show table edit popup.
          else if (selectedCells().length > 0) {
            if (editor.selection.isCollapsed()) {
              _showEditPopup();
            }

            // No text selection.
            else {
              _removeSelection();
            }
          }
        }

        // Resizing stops.
        if (resizingFlag) {
          resizingFlag = false;

          $resizer.removeClass('fr-moving');

          // Allow text selection.
          editor.$el.removeClass('fr-no-selection');
          editor.edit.on();

          // Set release Y coordinate.
          var left = parseFloat($resizer.css('left')) + editor.opts.tableResizerOffset;
          if (editor.opts.iframe) {
            left -= editor.$iframe.offset().left;
          }
          $resizer.data('release-position', left);

          // Clear resizing limits.
          $resizer.removeData('max-left');
          $resizer.removeData('max-right');

          // Resize.
          _resize(e);

          // Hide resizer.
          _hideResizer();
        }
      }
    }

    /*
     * User drags mouse over multiple cells to select them.
     */
    function _mouseEnter (e) {
      if (mouseDownCellFlag === true) {
        var $cell = $(e.currentTarget);

        // Cells should be in the same table.
        if ($cell.closest('table').is(selectedTable())) {
          // Don't select both ths and tds.
          if (e.currentTarget.tagName == 'TD' && editor.$el.find('th.fr-selected-cell').length === 0) {
            // Select cells between.
            _selectCells(mouseDownCell, e.currentTarget);
            return;
          }

          else if (e.currentTarget.tagName == 'TH' && editor.$el.find('td.fr-selected-cell').length === 0) {
            // Select cells between.
            _selectCells(mouseDownCell, e.currentTarget);
            return;
          }
        }

        // Prevent firefox selection.
        _clearSelection();
      }
    }

    /*
     * Using the arrow keys to move the cursor through the table will not select cells.
     */
    function _usingArrows (e) {
      if (e.which == 37 || e.which == 38 || e.which == 39 || e.which == 40) {
        if (selectedCells().length > 0) {
          _stopEdit();
        }
      }
    }

    /*
     * Initilize table resizer.
     */
    function _initResizer () {
      // Append resizer HTML to editor wrapper.
      if (!editor.shared.$table_resizer) editor.shared.$table_resizer = $('<div class="fr-table-resizer"><div></div></div>');
      $resizer = editor.shared.$table_resizer;

      // Resize table. Mousedown.
      editor.events.$on($resizer, 'mousedown', function (e) {
        if (!editor.core.sameInstance($resizer)) return true;

        // Stop table editing.
        if (selectedCells().length > 0) {
          _stopEdit();
        }

        // Resize table only using left click.
        if (e.which == 1) {
          resizingFlag = true;

          $resizer.addClass('fr-moving');

          // Prevent text selection while dragging the table resizer.
          _clearSelection();

          // Turn editor toolbar off while resizing.
          editor.edit.off();

          // Show resizer.
          $resizer.find('div').css('opacity', 1);

          // Prevent selecting text when doing resize.
          return false;
        }
      });

      // Mousemove on table resizer.
      editor.events.$on($resizer, 'mousemove', function (e) {
        if (!editor.core.sameInstance($resizer)) return true;

        if (resizingFlag) {
          if (editor.opts.iframe) {
            e.pageX -= editor.$iframe.offset().left;
          }

          _mouseMove(e);
        }
      })

      // Editor destroy.
      editor.events.on('shared.destroy', function () {
        $resizer.html('').removeData().remove();
        $resizer = null;
      }, true);

      editor.events.on('destroy', function () {
        editor.$el.find('.fr-selected-cell').removeClass('fr-selected-cell');
        $resizer.hide().appendTo($('body'));
      }, true);
    }

    /*
     * Also clears top and left values, so it doesn't interfer with the insert helper.
     */
    function _hideResizer () {
      if ($resizer) {
        $resizer.find('div').css('opacity', 0);
        $resizer.css('top', 0);
        $resizer.css('left', 0);
        $resizer.css('height', 0);
        $resizer.find('div').css('height', 0);
        $resizer.hide();
      }
    }

    /**
     * Hide the insert helper.
     */
    function _hideInsertHelper () {
      if ($insert_helper) $insert_helper.removeClass('fr-visible').css('left', '-9999px');
    }

    /*
     * Place the table resizer between the columns where the mouse is.
     */
    function _placeResizer (e, tag_under) {
      var $tag_under = $(tag_under);
      var $table = $tag_under.closest('table');
      var $table_parent = $table.parent();

      // We might have another tag inside the table cell.
      if (tag_under && (tag_under.tagName != 'TD' && tag_under.tagName != 'TH')) {
        if ($tag_under.closest('td').length > 0) {
          tag_under = $tag_under.closest('td');
        } else if ($tag_under.closest('th').length > 0) {
          tag_under = $tag_under.closest('th');
        }
      }

      // The tag should be a table cell (TD or TH).
      if (tag_under && (tag_under.tagName == 'TD' || tag_under.tagName == 'TH')) {
        $tag_under = $(tag_under);

        // https://github.com/froala/wysiwyg-editor/issues/786.
        if (editor.$el.find($tag_under).length === 0) return false;

        // Tag's left and right coordinate.
        var tag_left = $tag_under.offset().left - 1;
        var tag_right = tag_left + $tag_under.outerWidth();

        // Only if the mouse is close enough to the left or right edges.
        if (Math.abs(e.pageX - tag_left) <= editor.opts.tableResizerOffset ||
            Math.abs(tag_right - e.pageX) <= editor.opts.tableResizerOffset) {

          // Create a table map.
          var map = _tableMap($table);
          var tag_origin = _cellOrigin(tag_under, map);

          var tag_end = _cellEnds(tag_origin.row, tag_origin.col, map);

          // The column numbers from the map that have to be resized.
          var first;
          var second;

          // Table resizer position and height.
          var resizer_top = $table.offset().top;
          var resizer_height = $table.outerHeight() - 1;
          var resizer_left;

          // The left and right limits between which the resizer can be moved.
          var max_left;
          var max_right;

          if (editor.opts.direction != 'rtl') {
            // Mouse is near the cells's left margin (skip left table border).
            if (tag_origin.col > 0 && e.pageX - tag_left <= editor.opts.tableResizerOffset) {
              // Table resizer's left position.
              resizer_left = tag_left;

              // Left limit.
              max_left = tag_left - _columnWidth(tag_origin.col - 1, map) + editor.opts.tableResizingLimit;

              // Right limit.
              max_right = tag_left + _columnWidth(tag_origin.col, map) - editor.opts.tableResizingLimit;

              // Columns to resize.
              first = tag_origin.col - 1;
              second = tag_origin.col;
            }

            // Mouse is near the cell's right margin.
            else if (tag_right - e.pageX <= editor.opts.tableResizerOffset) {
              // Table resizer's left possition.
              resizer_left = tag_right;

              // Check for next td.
              if (tag_end.col < map[tag_end.row].length && map[tag_end.row][tag_end.col + 1]) {
                // Left limit.
                max_left = tag_right - _columnWidth(tag_end.col, map) + editor.opts.tableResizingLimit;

                // Right limit.
                max_right = tag_right + _columnWidth(tag_end.col + 1, map) - editor.opts.tableResizingLimit;

                // Columns to resize.
                first = tag_end.col;
                second = tag_end.col + 1;
              }

              // Resize table.
              else {
                // Columns to resize.
                first = tag_end.col;
                second = null;

                // Resizer limits.
                max_left = $table.offset().left - 1 + map[0].length * editor.opts.tableResizingLimit;
                max_right = $table_parent.offset().left - 1 + $table_parent.width() + parseFloat($table_parent.css('padding-left'));
              }
            }
          }

          // RTL
          else {
            // Mouse is near the cell's right margin (skip right table border).
            if (tag_origin.col > 0 && tag_right - e.pageX <= editor.opts.tableResizerOffset) {
              // Table resizer's left position.
              resizer_left = tag_right;

              // Left limit.
              max_left = tag_right - _columnWidth(tag_origin.col, map) + editor.opts.tableResizingLimit;

              // Right limit.
              max_right = tag_right + _columnWidth(tag_origin.col - 1, map) - editor.opts.tableResizingLimit;

              // Columns to resize.
              first = tag_origin.col;
              second = tag_origin.col - 1;
            }

            // Mouse is near the cell's left margin.
            else if (e.pageX - tag_left <= editor.opts.tableResizerOffset) {
              // Table resizer's left position.
              resizer_left = tag_left;

              // Check for next td.
              if (tag_end.col < map[tag_end.row].length && map[tag_end.row][tag_end.col + 1]) {
                // Left limit.
                max_left = tag_left - _columnWidth(tag_end.col + 1, map) + editor.opts.tableResizingLimit;

                // Right limit.
                max_right = tag_left + _columnWidth(tag_end.col, map) - editor.opts.tableResizingLimit;

                // Columns to resize.
                first = tag_end.col + 1;
                second = tag_end.col;
              }

              // Resize table.
              else {
                // Columns to resize.
                first = null;
                second = tag_end.col;

                // Resizer limits.
                max_left = $table_parent.offset().left + parseFloat($table_parent.css('padding-left'));
                max_right = $table_parent.offset().left - 1 + $table_parent.width() + parseFloat($table_parent.css('padding-left')) - map[0].length * editor.opts.tableResizingLimit;
              }
            }
          }

          if (!$resizer) _initResizer();

          // Save table.
          $resizer.data('table', $table);

          // Save columns to resize.
          $resizer.data('first', first);
          $resizer.data('second', second);

          $resizer.data('instance', editor);
          editor.$wp.append($resizer);

          var left = resizer_left - editor.win.pageXOffset - editor.opts.tableResizerOffset;
          var top = resizer_top - editor.win.pageYOffset;

          if (editor.opts.iframe) {
            left += editor.$iframe.offset().left - $(editor.o_win).scrollLeft();
            top += editor.$iframe.offset().top - $(editor.o_win).scrollTop();

            max_left += editor.$iframe.offset().left;
            max_right += editor.$iframe.offset().left;
          }

          // Set resizing limits.
          $resizer.data('max-left', max_left);
          $resizer.data('max-right', max_right);

          // Initial position of the resizer
          $resizer.data('origin', resizer_left - editor.win.pageXOffset);

          // Set table resizer's top, left and height.
          $resizer.css('top', top);
          $resizer.css('left', left);
          $resizer.css('height', resizer_height);
          $resizer.find('div').css('height', resizer_height);

          // Set padding according to tableResizerOffset.
          $resizer.css('padding-left', editor.opts.tableResizerOffset);
          $resizer.css('padding-right', editor.opts.tableResizerOffset);

          // Show table resizer.
          $resizer.show();
        }

        // Hide resizer when the mouse moves away from the cell's border.
        else {
          if (editor.core.sameInstance($resizer)) _hideResizer();
        }
      }

      // Hide resizer if mouse is no longer over it.
      else if ($resizer && $tag_under.get(0) != $resizer.get(0) && $tag_under.parent().get(0) != $resizer.get(0)) {
        if (editor.core.sameInstance($resizer))  _hideResizer();
      }
    }

    /*
     * Show the insert column helper button.
     */
    function _showInsertColHelper (e, table) {
      if (editor.$box.find('.fr-line-breaker').is(':visible')) return false;

      // Insert Helper.
      if (!$insert_helper) _initInsertHelper();

      editor.$box.append($insert_helper);
      $insert_helper.data('instance', editor);

      var $table = $(table);
      var $row = $table.find('tr:first');

      var mouseX = e.pageX;

      var left = 0;
      var top = 0;

      if (editor.opts.iframe) {
        left += editor.$iframe.offset().left - $(editor.o_win).scrollLeft();
        top += editor.$iframe.offset().top - $(editor.o_win).scrollTop();
      }

      // Check where the column should be inserted.
      var btn_width;
      $row.find('th, td').each (function () {
        var $td = $(this);

        // Insert before this td.
        if ($td.offset().left <= mouseX && mouseX < $td.offset().left + $td.outerWidth() / 2) {
          btn_width = parseInt($insert_helper.find('a').css('width'), 10);

          $insert_helper.css('top', top + $td.offset().top - editor.win.pageYOffset - btn_width - 5);
          $insert_helper.css('left', left + $td.offset().left - editor.win.pageXOffset - btn_width / 2);
          $insert_helper.data('selected-cell', $td);
          $insert_helper.data('position', 'before');
          $insert_helper.addClass('fr-visible');

          return false;

        // Insert after this td.
        } else if ($td.offset().left + $td.outerWidth() / 2 <= mouseX && mouseX < $td.offset().left + $td.outerWidth()) {
          btn_width = parseInt($insert_helper.find('a').css('width'), 10);

          $insert_helper.css('top', top + $td.offset().top - editor.win.pageYOffset - btn_width - 5);
          $insert_helper.css('left', left + $td.offset().left + $td.outerWidth() - editor.win.pageXOffset - btn_width / 2);
          $insert_helper.data('selected-cell', $td);
          $insert_helper.data('position', 'after');
          $insert_helper.addClass('fr-visible');

          return false;
        }
      });
    }

    /*
     * Show the insert row helper button.
     */
    function _showInsertRowHelper (e, table) {
      if (editor.$box.find('.fr-line-breaker').is(':visible')) return false;

      if (!$insert_helper) _initInsertHelper();

      editor.$box.append($insert_helper);
      $insert_helper.data('instance', editor);

      var $table = $(table);
      var mouseY = e.pageY;

      var left = 0;
      var top = 0;
      if (editor.opts.iframe) {
        left += editor.$iframe.offset().left - $(editor.o_win).scrollLeft();
        top += editor.$iframe.offset().top - $(editor.o_win).scrollTop();
      }

      // Check where the row should be inserted.
      var btn_width;
      $table.find('tr').each (function () {
        var $tr = $(this);

        // Insert above this tr.
        if ($tr.offset().top <= mouseY && mouseY < $tr.offset().top + $tr.outerHeight() / 2) {
          btn_width = parseInt($insert_helper.find('a').css('width'), 10);

          $insert_helper.css('top', top + $tr.offset().top - editor.win.pageYOffset - btn_width / 2);
          $insert_helper.css('left', left + $tr.offset().left - editor.win.pageXOffset - btn_width - 5);
          $insert_helper.data('selected-cell', $tr.find('td:first'));
          $insert_helper.data('position', 'above');
          $insert_helper.addClass('fr-visible');

          return false;

        // Insert below this tr.
        } else if ($tr.offset().top + $tr.outerHeight() / 2 <= mouseY && mouseY < $tr.offset().top + $tr.outerHeight()) {
          btn_width = parseInt($insert_helper.find('a').css('width'), 10);

          $insert_helper.css('top', top + $tr.offset().top + $tr.outerHeight() - editor.win.pageYOffset - btn_width / 2);
          $insert_helper.css('left', left + $tr.offset().left - editor.win.pageXOffset - btn_width - 5);
          $insert_helper.data('selected-cell', $tr.find('td:first'));
          $insert_helper.data('position', 'below');
          $insert_helper.addClass('fr-visible');

          return false;
        }
      });
    }

    /*
     * Check if should show the insert column / row helper button.
     */
    function _insertHelper (e, tag_under) {
      // Don't show the insert helper if there are table cells selected.
      if (selectedCells().length === 0) {
        var i;
        var tag_below;
        var tag_right;

        // Tag is the editor element or body (inline toolbar). Look for closest tag bellow and at the right.
        if (tag_under && (tag_under.tagName == 'HTML' || tag_under.tagName == 'BODY' || editor.node.isElement(tag_under))) {
          // Look 1px down until a table tag is found or the insert helper offset is reached.
          for (i = 1; i <= editor.opts.tableInsertHelperOffset; i++) {
            // Look for tag below.
            tag_below = editor.doc.elementFromPoint(e.pageX - editor.win.pageXOffset, e.pageY - editor.win.pageYOffset + i);

            // We're on tooltip.
            if ($(tag_below).hasClass('fr-tooltip')) return true;

            // We found a tag bellow.
            if (tag_below && ((tag_below.tagName == 'TH' || tag_below.tagName == 'TD' || tag_below.tagName == 'TABLE') && ($(tag_below).parents('.fr-wrapper').length || editor.opts.iframe))) {
              // Show the insert column helper button.
              _showInsertColHelper (e, tag_below.closest('table'));
              return true;
            }

            // Look for tag at the right.
            tag_right = editor.doc.elementFromPoint(e.pageX - editor.win.pageXOffset + i, e.pageY - editor.win.pageYOffset);

            // We're on tooltip.
            if ($(tag_right).hasClass('fr-tooltip')) return true;

            // We found a tag at the right.
            if (tag_right && ((tag_right.tagName == 'TH' || tag_right.tagName == 'TD' || tag_right.tagName == 'TABLE') && ($(tag_right).parents('.fr-wrapper').length || editor.opts.iframe))) {
              // Show the insert row helper button.
              _showInsertRowHelper (e, tag_right.closest('table'));
              return true;
            }
          }
        }

        // Hide insert helper.
        if (editor.core.sameInstance($insert_helper)) {
          _hideInsertHelper();
        }
      }
    }

    /*
     * Check tag under the mouse on mouse move.
     */
    function _tagUnder (e) {
      mouseMoveTimer = null;

      // The tag under the mouse cursor.
      var tag_under = editor.doc.elementFromPoint(e.pageX - editor.win.pageXOffset, e.pageY - editor.win.pageYOffset);

      // Place table resizer if necessary.
      if (editor.opts.tableResizer && (!editor.popups.areVisible() || (editor.popups.areVisible() && editor.popups.isVisible('table.edit')))) {
        _placeResizer(e, tag_under);
      }

      // Show the insert column / row helper button.
      if (editor.opts.tableInsertHelper && !editor.popups.areVisible() && !(editor.$tb.hasClass('fr-inline') && editor.$tb.is(':visible'))) {
        _insertHelper(e, tag_under);
      }
    }

    /*
     * Repositon the resizer if the user scrolls while resizing.
     */
    function _repositionResizer () {
      if (resizingFlag) {
        var $table = $resizer.data('table');
        var top = $table.offset().top - editor.win.pageYOffset;

        if (editor.opts.iframe) {
          top += editor.$iframe.offset().top - $(editor.o_win).scrollTop();
        }

        $resizer.css('top', top);
      }
    }

    /*
     * Resize table method.
     */
    function _resize () {
      // Resizer initial position.
      var initial_positon = $resizer.data('origin');

      // Resizer release position.
      var release_position = $resizer.data('release-position');

      // Do resize only if the resizer's position has changed.
      if (initial_positon !== release_position) {
        // Columns that have to be resized.
        var first = $resizer.data('first');
        var second = $resizer.data('second');

        var $table = $resizer.data('table');
        var table_width = $table.outerWidth();

        // Resize columns and not the table.
        if (first !== null && second !== null) {
          // Create a table map.
          var map = _tableMap($table);

          // Got through all cells on these columns and get their initial width.
          var first_widths = [];
          var first_percentages = [];
          var second_widths = [];
          var second_percentages = [];
          var i;
          var $first_cell;
          var $second_cell;

          // We must do this before updating widths.
          for (i = 0; i < map.length; i++) {
            $first_cell = $(map[i][first]);
            $second_cell = $(map[i][second]);

            // Widths in px.
            first_widths[i] = $first_cell.outerWidth();
            second_widths[i] = $second_cell.outerWidth();

            // Widths in percentages.
            first_percentages[i] = first_widths[i] / table_width * 100;
            second_percentages[i] = second_widths[i] / table_width * 100;
          }

          // Got through all cells on these columns and update their widths.
          for (i = 0; i < map.length; i++) {
            $first_cell = $(map[i][first]);
            $second_cell = $(map[i][second]);

            // New percentage for the first cell.
            var first_cell_percentage = (first_percentages[i] * (first_widths[i] + release_position - initial_positon) / first_widths[i]).toFixed(4);

            $first_cell.css('width', first_cell_percentage + '%');
            $second_cell.css('width', (first_percentages[i] + second_percentages[i] - first_cell_percentage).toFixed(4) + '%');
          }
        }

        // Resize the table.
        else {
          var $table_parent = $table.parent();
          var table_percentage = table_width / $table_parent.width() * 100;
          var width;

          if (first == null) {
            width = (table_width - release_position + initial_positon) / table_width * table_percentage;
          } else {
            width = (table_width + release_position - initial_positon) / table_width * table_percentage;
          }

          $table.css('width', Math.round(width).toFixed(4) + '%');
        }
      }

      // Clear resizer data.
      $resizer.removeData('origin');
      $resizer.removeData('release-position');
      $resizer.removeData('first');
      $resizer.removeData('second');
      $resizer.removeData('table');

      editor.undo.saveStep();
    }

    /*
     * Get the width of the column. (columns may have colspan)
     */
    function _columnWidth (col, map) {
      var i;
      var width = $(map[0][col]).outerWidth();

      for (i = 1; i < map.length; i++) {
        width = Math.min(width, $(map[i][col]).outerWidth());
      }

      return width;
    }

    /*
     * Get the width of the columns between specified indexes.
     */
    function _columnsWidth(col1, col2, map) {
      var i;
      var width = 0;

      // Sum all columns widths.
      for (i = col1; i <= col2; i++) {
        width += _columnWidth(i, map);
      }

      return width;
    }

    /*
     * Set mouse timer to improve performance.
     */
    function _mouseMove (e) {
      // Prevent selecting text when we have cells selected.
      if (selectedCells().length > 1 && mouseDownFlag) {
        _clearSelection();
      }

      // Reset or set timer.
      if (mouseDownFlag === false && mouseDownCellFlag === false && resizingFlag === false) {
        if (mouseMoveTimer) {
          clearTimeout(mouseMoveTimer);
        }

        // Only resize table if the editor is not disabled by user.
        if (!editor.edit.isDisabled() || editor.popups.isVisible('table.edit')) {
          // Check tag under in order to place the table resizer or insert helper button.
          mouseMoveTimer = setTimeout(_tagUnder, 30, e);
        }

      // Move table resizer.
      } else if (resizingFlag) {
        // Cursor position.
        var pos = e.pageX - editor.win.pageXOffset;

        if (editor.opts.iframe) {
          pos += editor.$iframe.offset().left;
        }

        // Left and right limits.
        var left_limit = $resizer.data('max-left');
        var right_limit = $resizer.data('max-right');

        // Cursor is between the left and right limits.
        if (pos >= left_limit && pos <= right_limit) {
          $resizer.css('left', pos - editor.opts.tableResizerOffset);

        // Cursor has exceeded the left limit. Don't update if it already has the correct value.
        } else if (pos < left_limit && parseFloat($resizer.css('left'), 10) > left_limit - editor.opts.tableResizerOffset) {
          $resizer.css('left', left_limit - editor.opts.tableResizerOffset);

        // Cursor has exceeded the right limit. Don't update if it already has the correct value.
        } else if (pos > right_limit && parseFloat($resizer.css('left'), 10) < right_limit - editor.opts.tableResizerOffset) {
          $resizer.css('left', right_limit - editor.opts.tableResizerOffset);
        }
      } else if (mouseDownFlag) {
        _hideInsertHelper();
      }
    }

    /*
     * Place selection markers in a table cell.
     */
    function _addMarkersInCell ($cell) {
      if (editor.node.isEmpty($cell.get(0))) {
        $cell.prepend($.FE.MARKERS);
      }
      else {
        $cell.prepend($.FE.START_MARKER).append($.FE.END_MARKER);
      }
    }

    /*
     * Use TAB key to navigate through cells.
     */
    function _useTab (e) {
      var key_code = e.which;

      if (key_code == $.FE.KEYCODE.TAB && editor.opts.tabSpaces === 0) {
        // Get starting cell.
        var $cell;

        if (selectedCells().length > 0) {
          $cell = editor.$el.find('.fr-selected-cell:last')
        }
        else {
          var cell = editor.selection.element();

          if (cell.tagName == 'TD' || cell.tagName == 'TH') {
            $cell = $(cell);
          }
          else if ($(cell).closest('td').length > 0) {
            $cell = $(cell).closest('td');
          }
          else if ($(cell).closest('th').length > 0) {
            $cell = $(cell).closest('th');
          }
        }

        if ($cell) {
          e.preventDefault();

          _stopEdit();

          // Go backwards.
          if (e.shiftKey) {
            // Go to previous cell.
            if ($cell.prev().length > 0) {
              _addMarkersInCell($cell.prev());
            }

            // Go to prev row, last cell.
            else if ($cell.closest('tr').length > 0 && $cell.closest('tr').prev().length > 0) {
              _addMarkersInCell($cell.closest('tr').prev().find('td:last'));
            }

            // Go in THEAD, last cell.
            else if ($cell.closest('tbody').length > 0 && $cell.closest('table').find('thead tr').length > 0) {
              _addMarkersInCell($cell.closest('table').find('thead tr th:last'));
            }
          }

          // Go forward.
          else {
            // Go to next cell.
            if ($cell.next().length > 0) {
              _addMarkersInCell($cell.next());
            }

            // Go to next row, first cell.
            else if ($cell.closest('tr').length > 0 && $cell.closest('tr').next().length > 0) {
              _addMarkersInCell($cell.closest('tr').next().find('td:first'));
            }

            // Cursor is in THEAD. Go to next row in TBODY
            else if ($cell.closest('thead').length > 0 && $cell.closest('table').find('tbody tr').length > 0) {
              _addMarkersInCell($cell.closest('table').find('tbody tr td:first'));
            }

            // Add new row.
            else {
              $cell.addClass('fr-selected-cell');
              insertRow('below');
              _removeSelection();

              _addMarkersInCell($cell.closest('tr').next().find('td:first'));
            }
          }

          // Update cursor position.
          editor.selection.restore();
        }
      }
    }

    /*
     * Initilize insert helper.
     */
    function _initInsertHelper () {
      // Append insert helper HTML to editor wrapper.
      if (!editor.shared.$ti_helper) {
        editor.shared.$ti_helper = $('<div class="fr-insert-helper"><a class="fr-floating-btn" role="button" tabindex="-1" title="' + editor.language.translate('Insert') + '"><svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><path d="M22,16.75 L16.75,16.75 L16.75,22 L15.25,22.000 L15.25,16.75 L10,16.75 L10,15.25 L15.25,15.25 L15.25,10 L16.75,10 L16.75,15.25 L22,15.25 L22,16.75 Z"/></svg></a></div>');

        // Click on insert helper.
        editor.events.bindClick(editor.shared.$ti_helper, 'a', function () {
          var $td = $insert_helper.data('selected-cell');
          var position = $insert_helper.data('position');

          var inst = $insert_helper.data('instance') || editor;

          if (position == 'before') {
            $td.addClass('fr-selected-cell');
            inst.table.insertColumn(position);
            $td.removeClass('fr-selected-cell');

          } else if (position == 'after') {
            $td.addClass('fr-selected-cell');
            inst.table.insertColumn(position);
            $td.removeClass('fr-selected-cell');

          } else if (position == 'above') {
            $td.addClass('fr-selected-cell');
            inst.table.insertRow(position);
            $td.removeClass('fr-selected-cell');

          } else if (position == 'below') {
            $td.addClass('fr-selected-cell');
            inst.table.insertRow(position);
            $td.removeClass('fr-selected-cell');
          }

          // Hide the insert helper so it will reposition.
          _hideInsertHelper();
        });

        // Editor destroy.
        editor.events.on('shared.destroy', function () {
          editor.shared.$ti_helper.html('').removeData().remove();
          editor.shared.$ti_helper = null;
        }, true);

        // Prevent the insert helper hide when mouse is over it.
        editor.events.$on(editor.shared.$ti_helper, 'mousemove', function (e) {
          e.stopPropagation();
        }, true);

        // Hide the insert helper if the page is scrolled.
        editor.events.$on($(editor.o_win), 'scroll', function () {
          _hideInsertHelper();
        }, true);

        editor.events.$on(editor.$wp, 'scroll', function () {
          _hideInsertHelper();
        }, true);
      }

      $insert_helper = editor.shared.$ti_helper;

      editor.events.on('destroy', function () {
        $insert_helper = null;
      });

      // Table insert helper tooltip.
      editor.tooltip.bind(editor.$box, '.fr-insert-helper > a.fr-floating-btn');
    }

    /**
     * Destroy
     */
    function _destroy () {
      mouseDownCell = null;
      clearTimeout(mouseMoveTimer);
    }

    /*
     * Go back to the table edit popup.
     */
    function back () {
      if (selectedCells().length > 0) {
        _showEditPopup();
      }
      else {
        editor.popups.hide('table.insert');
        editor.toolbar.showInline();
      }
    }

    /**
     * Return selected cells.
     */
    function selectedCells () {
      return editor.$el.get(0).querySelectorAll('.fr-selected-cell');
    }

    /**
     * Return selected table.
     */
    function selectedTable () {
      var cells = selectedCells();
      if (cells.length) {
        var cell = cells[0];
        while (cell && cell.tagName != 'TABLE' && cell.parentNode != editor.$el.get(0)) {
          cell = cell.parentNode;
        }

        if (cell && cell.tagName == 'TABLE') return $(cell);
        return $([]);
      }

      return $([]);
    }

    /*
     * Init table.
     */
    function _init () {
      if (!editor.$wp) return false;

      // Do cell selection only on desktops (no touch devices)
      if (!editor.helpers.isMobile()) {
        // Remember if mouse is clicked.
        mouseDownFlag = false;
        mouseDownCellFlag = false;
        resizingFlag = false;

        // Mouse is down in a table cell.
        editor.events.$on(editor.$el, 'mousedown', _mouseDown);

        // Deselect table cells when user clicks on an image.
        editor.popups.onShow('image.edit', function () {
          _removeSelection();
          mouseDownFlag = false;
          mouseDownCellFlag = false;
        });

        // Deselect table cells when user clicks on a link.
        editor.popups.onShow('link.edit', function () {
          _removeSelection();
          mouseDownFlag = false;
          mouseDownCellFlag = false;
        });

        // Deselect table cells when a command is run.
        editor.events.on('commands.mousedown', function ($btn) {
          if ($btn.parents('.fr-toolbar').length > 0) {
            _removeSelection();
          }
        });

        // Mouse enter's a table cell.
        editor.events.$on(editor.$el, 'mouseenter', 'th, td', _mouseEnter);

        // Mouse is no longer pressed.
        editor.events.$on(editor.$win, 'mouseup', _mouseUp);

        // Iframe mouseup.
        if (editor.opts.iframe) {
          editor.events.$on($(editor.o_win), 'mouseup', _mouseUp);
        }

        // Moving cursor with arrow keys.
        editor.events.$on(editor.$el, 'keydown', _usingArrows);

        // Check tags under the mouse to see if the resizer needs to be shown.
        editor.events.$on(editor.$win, 'mousemove', _mouseMove);

        // Update resizer's position on scroll.
        editor.events.$on($(editor.o_win), 'scroll', _repositionResizer);

        // Reposition table edit popup when table cell content changes.
        editor.events.on('contentChanged', function () {
          if (selectedCells().length > 0) {
            _showEditPopup();

            // Make sure we reposition on image load.
            editor.$el.find('img').on('load.selected-cells', function () {
              $(this).off('load.selected-cells');
              if (selectedCells().length > 0) {
                _showEditPopup();
              }
            });
          }
        });

        // Reposition table edit popup on window resize.
        editor.events.$on($(editor.o_win), 'resize', function () {
          _removeSelection();
        });

        // Prevent backspace from doing browser back.
        editor.events.on('keydown', function (e) {
          var selected_cells = selectedCells();

          if (selected_cells.length > 0) {
            // ESC clear table cell selection.
            if (e.which == $.FE.KEYCODE.ESC) {
              if (editor.popups.isVisible('table.edit')) {
                _removeSelection();
                editor.popups.hide('table.edit');
                e.preventDefault();
                e.stopPropagation();
                e.stopImmediatePropagation();
                selected_cells = [];
                return false;
              }
            }

            // Backspace clears selected cells content.
            if (selected_cells.length > 1 && e.which == $.FE.KEYCODE.BACKSPACE) {
              editor.undo.saveStep();

              for (var i = 0; i < selected_cells.length; i++) {
                $(selected_cells[i]).html('<br>');

                if (i == selected_cells.length - 1) {
                  $(selected_cells[i]).prepend($.FE.MARKERS);
                }
              }

              editor.selection.restore();
              editor.undo.saveStep();
              selected_cells = [];
              return false;
            }

            // Prevent typing if cells are selected. (Allow browser refresh using keyboard)
            if (selected_cells.length > 1 && !editor.keys.ctrlKey(e)) {
              e.preventDefault();
              selected_cells = [];
              return false;
            }
          }

          selected_cells = [];
        }, true);

        // Clean selected cells.
        var c_selected_cells = [];
        editor.events.on('html.beforeGet', function () {
          c_selected_cells = selectedCells();
          for (var i = 0; i < c_selected_cells.length; i++) {
            c_selected_cells[i].className = (c_selected_cells[i].className || '').replace(/fr-selected-cell/g, '');
          }
        });

        editor.events.on('html.get', function (html) {
          html = html.replace(/<(td|th)((?:[\w\W]*?)) class=""((?:[\w\W]*?))>((?:[\w\W]*?))<\/(td|th)>/g, '<$1$2$3>$4</$5>');

          return html;
        });

        editor.events.on('html.afterGet', function () {
          for (var i = 0; i < c_selected_cells.length; i++) {
            c_selected_cells[i].className = (c_selected_cells[i].className ? c_selected_cells[i].className + ' ' : '') + 'fr-selected-cell';
          }
          c_selected_cells = [];
        });

        _initInsertPopup(true);
        _initEditPopup(true);
      }

      // Tab in cell
      editor.events.on('keydown', _useTab, true);

      editor.events.on('destroy', _destroy);
    }

    return {
      _init: _init,
      insert: insert,
      remove: remove,
      insertRow: insertRow,
      deleteRow: deleteRow,
      insertColumn: insertColumn,
      deleteColumn: deleteColumn,
      mergeCells: mergeCells,
      splitCellVertically: splitCellVertically,
      splitCellHorizontally: splitCellHorizontally,
      addHeader: addHeader,
      removeHeader: removeHeader,
      setBackground: setBackground,
      showInsertPopup: _showInsertPopup,
      showEditPopup: _showEditPopup,
      showColorsPopup: _showColorsPopup,
      back: back,
      verticalAlign: verticalAlign,
      horizontalAlign: horizontalAlign,
      applyStyle: applyStyle,
      selectedTable: selectedTable,
      selectedCells: selectedCells
    }
  };

  // Insert table button.
  $.FE.DefineIcon('insertTable', { NAME: 'table' });
  $.FE.RegisterCommand('insertTable', {
    title: 'Insert Table',
    undo: false,
    focus: true,
    refreshOnCallback: false,
    popup: true,
    callback: function () {
      if (!this.popups.isVisible('table.insert')) {
        this.table.showInsertPopup();
      }
      else {
        if (this.$el.find('.fr-marker')) {
          this.events.disableBlur();
          this.selection.restore();
        }
        this.popups.hide('table.insert');
      }
    },
    plugin: 'table'
  });

  $.FE.RegisterCommand('tableInsert', {
    callback: function (cmd, rows, cols) {
      this.table.insert(rows, cols);
      this.popups.hide('table.insert');
    }
  })

  // Table header button.
  $.FE.DefineIcon('tableHeader', { NAME: 'header' })
  $.FE.RegisterCommand('tableHeader', {
    title: 'Table Header',
    focus: false,
    callback: function () {
      var $btn = this.popups.get('table.edit').find('.fr-command[data-cmd="tableHeader"]');

      // If button is active the table has a header,
      if ($btn.hasClass('fr-active')) {
        this.table.removeHeader();
      }

      // Add table header.
      else {
        this.table.addHeader();
      }
    },
    refresh: function ($btn) {
      var $table = this.table.selectedTable();

      if ($table.length > 0) {
        // If table doesn't have a header.
        if ($table.find('th').length === 0) {
          $btn.removeClass('fr-active');
        }

        // Header button is active if table has header.
        else {
          $btn.addClass('fr-active');
        }
      }
    }
  });

  // Table rows action dropdown.
  $.FE.DefineIcon('tableRows', { NAME: 'bars' })
  $.FE.RegisterCommand('tableRows', {
    type: 'dropdown',
    focus: false,
    title: 'Row',
    options: {
      above: 'Insert row above',
      below: 'Insert row below',
      'delete': 'Delete row'
    },
    html: function () {
      var c = '<ul class="fr-dropdown-list">';
      var options =  $.FE.COMMANDS.tableRows.options;
      for (var val in options) {
        if (options.hasOwnProperty(val)) {
          c += '<li><a class="fr-command" data-cmd="tableRows" data-param1="' + val + '" title="' + this.language.translate(options[val]) + '">' + this.language.translate(options[val]) + '</a></li>';
        }
      }
      c += '</ul>';

      return c;
    },
    callback: function (cmd, val) {
      if (val == 'above' || val == 'below') {
        this.table.insertRow(val);
      } else {
        this.table.deleteRow();
      }
    }
  });

  // Table columns action dropdown.
  $.FE.DefineIcon('tableColumns', { NAME: 'bars fa-rotate-90' })
  $.FE.RegisterCommand('tableColumns', {
    type: 'dropdown',
    focus: false,
    title: 'Column',
    options: {
      before: 'Insert column before',
      after: 'Insert column after',
      'delete': 'Delete column'
    },
    html: function () {
      var c = '<ul class="fr-dropdown-list">';
      var options =  $.FE.COMMANDS.tableColumns.options;
      for (var val in options) {
        if (options.hasOwnProperty(val)) {
          c += '<li><a class="fr-command" data-cmd="tableColumns" data-param1="' + val + '" title="' + this.language.translate(options[val]) + '">' + this.language.translate(options[val]) + '</a></li>';
        }
      }
      c += '</ul>';

      return c;
    },
    callback: function (cmd, val) {
      if (val == 'before' || val == 'after') {
        this.table.insertColumn(val);
      } else {
        this.table.deleteColumn();
      }
    }
  });

  // Table cells action dropdown.
  $.FE.DefineIcon('tableCells', { NAME: 'square-o' })
  $.FE.RegisterCommand('tableCells', {
    type: 'dropdown',
    focus: false,
    title: 'Cell',
    options: {
      merge: 'Merge cells',
      'vertical-split': 'Vertical split',
      'horizontal-split': 'Horizontal split'
    },
    html: function () {
      var c = '<ul class="fr-dropdown-list">';
      var options =  $.FE.COMMANDS.tableCells.options;
      for (var val in options) {
        if (options.hasOwnProperty(val)) {
          c += '<li><a class="fr-command" data-cmd="tableCells" data-param1="' + val + '" title="' + this.language.translate(options[val]) + '">' + this.language.translate(options[val]) + '</a></li>';
        }
      }
      c += '</ul>';

      return c;
    },
    callback: function (cmd, val) {
      if (val == 'merge') {
        this.table.mergeCells();
      }
      else if (val == 'vertical-split') {
        this.table.splitCellVertically();
      }
      // 'horizontal-split'
      else {
        this.table.splitCellHorizontally();
      }
    },
    refreshOnShow: function ($btn, $dropdown) {
      // More than one cell selected.
      if (this.$el.find('.fr-selected-cell').length > 1) {
        $dropdown.find('a[data-param1="vertical-split"]').addClass('fr-disabled');
        $dropdown.find('a[data-param1="horizontal-split"]').addClass('fr-disabled');
        $dropdown.find('a[data-param1="merge"]').removeClass('fr-disabled');
      }

      // Only one selected cell.
      else {
        $dropdown.find('a[data-param1="merge"]').addClass('fr-disabled');
        $dropdown.find('a[data-param1="vertical-split"]').removeClass('fr-disabled');
        $dropdown.find('a[data-param1="horizontal-split"]').removeClass('fr-disabled');
      }
    }
  });

  // Remove table button.
  $.FE.DefineIcon('tableRemove', { NAME: 'trash' })
  $.FE.RegisterCommand('tableRemove', {
    title: 'Remove Table',
    focus: false,
    callback: function () {
      this.table.remove();
    }
  });

  // Table styles.
  $.FE.DefineIcon('tableStyle', { NAME: 'paint-brush' })
  $.FE.RegisterCommand('tableStyle', {
    title: 'Table Style',
    type: 'dropdown',
    focus: false,
    html: function () {
      var c = '<ul class="fr-dropdown-list">';
      var options =  this.opts.tableStyles;
      for (var val in options) {
        if (options.hasOwnProperty(val)) {
          c += '<li><a class="fr-command" data-cmd="tableStyle" data-param1="' + val + '" title="' + this.language.translate(options[val]) + '">' + this.language.translate(options[val]) + '</a></li>';
        }
      }
      c += '</ul>';

      return c;
    },
    callback: function (cmd, val) {
      this.table.applyStyle(val, this.$el.find('.fr-selected-cell').closest('table'), this.opts.tableMultipleStyles, this.opts.tableStyles);
    },
    refreshOnShow: function ($btn, $dropdown) {
      var $table = this.$el.find('.fr-selected-cell').closest('table');

      if ($table) {
        $dropdown.find('.fr-command').each (function () {
          var cls = $(this).data('param1');
          $(this).toggleClass('fr-active', $table.hasClass(cls));
        })
      }
    }
  });

  // Table cell background color button.
  $.FE.DefineIcon('tableCellBackground', { NAME: 'tint' })
  $.FE.RegisterCommand('tableCellBackground', {
    title: 'Cell Background',
    focus: false,
    callback: function () {
      this.table.showColorsPopup();
    }
  });

  // Select table cell background color command.
  $.FE.RegisterCommand('tableCellBackgroundColor', {
    undo: true,
    focus: false,
    callback: function (cmd, val) {
      this.table.setBackground(val);
    }
  });

  // Table back.
  $.FE.DefineIcon('tableBack', { NAME: 'arrow-left' });
  $.FE.RegisterCommand('tableBack', {
    title: 'Back',
    undo: false,
    focus: false,
    back: true,
    callback: function () {
      this.table.back();
    },
    refresh: function ($btn) {
      if (this.table.selectedCells().length === 0 && !this.opts.toolbarInline) {
        $btn.addClass('fr-hidden');
        $btn.next('.fr-separator').addClass('fr-hidden');
      }
      else {
        $btn.removeClass('fr-hidden');
        $btn.next('.fr-separator').removeClass('fr-hidden');
      }
    }
  });

  // Table vertical align dropdown.
  $.FE.DefineIcon('tableCellVerticalAlign', { NAME: 'arrows-v' })
  $.FE.RegisterCommand('tableCellVerticalAlign', {
    type: 'dropdown',
    focus: false,
    title: 'Vertical Align',
    options: {
      Top: 'Align Top',
      Middle: 'Align Middle',
      Bottom: 'Align Bottom'
    },
    html: function () {
      var c = '<ul class="fr-dropdown-list">';
      var options =  $.FE.COMMANDS.tableCellVerticalAlign.options;
      for (var val in options) {
        if (options.hasOwnProperty(val)) {
          c += '<li><a class="fr-command" data-cmd="tableCellVerticalAlign" data-param1="' + val.toLowerCase() + '" title="' + this.language.translate(options[val]) + '">' + this.language.translate(val) + '</a></li>';
        }
      }
      c += '</ul>';

      return c;
    },
    callback: function (cmd, val) {
      this.table.verticalAlign(val);
    },
    refreshOnShow: function ($btn, $dropdown) {
      $dropdown.find('.fr-command[data-param1="' + this.$el.find('.fr-selected-cell').css('vertical-align') + '"]').addClass('fr-active');
    }
  });

  // Table horizontal align dropdown.
  $.FE.DefineIcon('tableCellHorizontalAlign', { NAME: 'align-left' });
  $.FE.DefineIcon('align-left', { NAME: 'align-left' });
  $.FE.DefineIcon('align-right', { NAME: 'align-right' });
  $.FE.DefineIcon('align-center', { NAME: 'align-center' });
  $.FE.DefineIcon('align-justify', { NAME: 'align-justify' });
  $.FE.RegisterCommand('tableCellHorizontalAlign', {
    type: 'dropdown',
    focus: false,
    title: 'Horizontal Align',
    options: {
      left: 'Align Left',
      center: 'Align Center',
      right: 'Align Right',
      justify: 'Align Justify'
    },
    html: function () {
      var c = '<ul class="fr-dropdown-list">';
      var options =  $.FE.COMMANDS.tableCellHorizontalAlign.options;
      for (var val in options) {
        if (options.hasOwnProperty(val)) {
          c += '<li><a class="fr-command fr-title" data-cmd="tableCellHorizontalAlign" data-param1="' + val + '" title="' + this.language.translate(options[val]) + '">' + this.icon.create('align-' + val) + '</a></li>';
        }
      }
      c += '</ul>';

      return c;
    },
    callback: function (cmd, val) {
      this.table.horizontalAlign(val);
    },
    refresh: function ($btn) {
      var selected_cells = this.table.selectedCells();

      if (selected_cells.length) {
        $btn.find('> *:first').replaceWith(this.icon.create('align-' + this.helpers.getAlignment($(selected_cells[0]))));
      }
    },
    refreshOnShow: function ($btn, $dropdown) {
      $dropdown.find('.fr-command[data-param1="' + this.helpers.getAlignment(this.$el.find('.fr-selected-cell:first')) + '"]').addClass('fr-active');
    }
  });

  // Table cell styles.
  $.FE.DefineIcon('tableCellStyle', { NAME: 'magic' })
  $.FE.RegisterCommand('tableCellStyle', {
    title: 'Cell Style',
    type: 'dropdown',
    focus: false,
    html: function () {
      var c = '<ul class="fr-dropdown-list">';
      var options =  this.opts.tableCellStyles;
      for (var val in options) {
        if (options.hasOwnProperty(val)) {
          c += '<li><a class="fr-command" data-cmd="tableCellStyle" data-param1="' + val + '" title="' + this.language.translate(options[val]) + '">' + this.language.translate(options[val]) + '</a></li>';
        }
      }
      c += '</ul>';

      return c;
    },
    callback: function (cmd, val) {
      this.table.applyStyle(val, this.$el.find('.fr-selected-cell'), this.opts.tableCellMultipleStyles, this.opts.tableCellStyles);
    },
    refreshOnShow: function ($btn, $dropdown) {
      var $cell = this.$el.find('.fr-selected-cell:first');

      if ($cell) {
        $dropdown.find('.fr-command').each (function () {
          var cls = $(this).data('param1');
          $(this).toggleClass('fr-active', $cell.hasClass(cls));
        })
      }
    }
  });

}));

};


modules['helpcenter/primary/models/manage/author'] = function(require, exports, module, modules) {
module.exports = Backbone.Model.extend({
    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @param  {Object}
     */
    initialize: function(options) {
        this.name = options.name;
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @return {Obejct}
     */
    url: function() {
        return _Payload.portalpath + 'api/v1/autocomplete/users.json?name=' + this.name + '&in=OWNERS,ADMINS,AGENTS,COLLABORATORS&include=*';
    }
});
};


modules['helpcenter/primary/views/select-form'] = function(require, exports, module, modules) {
module.exports = Backbone.View.extend({
    el: $('#select-form'),

    /**
     * @author Tushar Arora <tushar.arora@kayako.com>
     * @description Gets options from router while instantiating view
     * @param  {Object}
     * @return {Object}
     */
    initialize: function (options) {
		$('.form-description').each(function() {
			$(this).css('width', $(this).text().length + 'ch');
		});
    }
});

};


modules['helpcenter/primary/views/autotimezone'] = function(require, exports, module, modules) {
var timezones = require('helpcenter/primary/data/timezones');

module.exports = Backbone.View.extend({
	el: $('#profile-container'),

	events: {
		'focus #timezone-input'   : 'listAll',
		'click #listTimeZones'    : 'listAll',
		'click #timezone-input'   : 'keep',
		'keyup #timezone-input'   : 'navigate',
		'keydown #timezone-input' : 'enter',
		'click .js-tz'            : 'set'
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Sets the selected timezone
	 */
	initialize: function() {
		var input = $('#timezone-input');
		var selected = input.data('selected');
		var timezoneName = '';

		for (var i = timezones.length - 1; i >= 0; i--) {
			if (timezones[i].value == selected) {
				timezoneName = timezones[i].name;
			}
		}
		input.val(timezoneName)
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Shows dropdown list of all timezones
	 * @param  {Object} e
	 */
	listAll: function(e) {
		e.stopPropagation();
		var view = this;
		$('#timezone-list').empty();

		var li = '';
		for (var i = timezones.length - 1; i >= 0; i--) {
			li += '<li class="autocomplete--field__item js-tz" data-value="' + timezones[i].value + '">' + timezones[i].name + '</li>' 
		}
		if (!$('#timezone-list').length) {
			$('#timezone').append($("<ul>", {
				id: "timezone-list", 
				class: "autocomplete--field fadeInD"
			}));
		}
		$('#timezone-list').append(li);

		// Select the first item
		view.next();

		$(document).click(function() {
			view.remove();
		});
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Keeps the dropdown from hiding
	 * @param  {Object} e
	 */
	keep: function(e) {
		e.stopPropagation();
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Shows filtered list of timezones
	 */
	filter: function() {
		$('#timezone-list').empty();
		var input = $('#timezone-input').val().replace(/\((\S+)/, '').replace(/\(/, '');
		var inputRegEx = new RegExp(input, 'i');
		var li = '';

		for (var i = timezones.length - 1; i >= 0; i--) {
			var str = timezones[i].name.replace(/\(GMT\+\d\d:\d\d\)\s/, '');
			if (inputRegEx.test(str)) {
				li += '<li class="autocomplete--field__item js-tz" data-value="' + timezones[i].value + '">' + timezones[i].name.replace(new RegExp('(' + input + ')', 'i') ,'<b>$1</b>') + '</li>' 
			}
		}

		if (!$('#timezone-list').length) {
			$('#timezone').append($("<ul>", {id: "timezone-list", class: "autocomplete--field fadeInD"}));
		}
		$('#timezone-list').append(li);

		// Select the first item
		this.next();
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Tiggers events on keyboard input 
	 * @param  {Object} e
	 */
	navigate: function(e) {
		switch (e.keyCode) {
			// up
			case 38:
			this.previous();
			break;
			// down
			case 40:
			this.next();
			break;
			// del
			case 8:
			this.backspace(e);
			break;
			default:
			this.filter();
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Sets the value if enter is pressed
	 */
	enter: function(e) {
		if (e.keyCode == 13) {
			this.set(e);
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Selects next list item
	 */
	next: function() {
		var next = $('.js-tz.is-selected').removeClass('is-selected').next('.js-tz');
		if (next.length == 0) {
			next = $('#timezone-list').children('.js-tz').first();
		}
		next.addClass('is-selected');
		this.scroll();
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Selects previous list item
	 */
	previous: function() {
		var prev = $('.js-tz.is-selected').removeClass('is-selected').prev('.js-tz');
		if (prev.length == 0) { 
			prev = $('#timezone-list').children().last();
		}
		prev.addClass('is-selected');
		this.scroll();
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Sets selected list item to input field
	 */
	set: function(e) {
		if (e.type == 'click') {
			// using mouse
			e.stopPropagation();
			$('#timezone-input').val($(e.target).text());
			$('#timezone-value').val($(e.target).data('value'));;
		} else {
			// using keyboard
			e.preventDefault();
			var selected = $('.js-tz.is-selected');
			if (selected.length) {
				$('#timezone-input').val(selected.text());
				$('#timezone-value').val(selected.data('value'));;
			}
		}
		this.remove();
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Removes dropdown list
	 */
	remove: function() {
		$('#timezone-list').fadeOut(120, function() {
			$(this).remove();
		});
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Removes dropdown list if input is empty
	 */
	backspace: function(e) {
		if (!$('#timezone-input').val().trim()) {
			this.listAll(e);
		} else {
			this.filter(e);
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Auto-scrolls a long list if user navigates using keyboard
	 */
	scroll: function() {
		var selected = $('.js-tz.is-selected');
		var list = $('#timezone-list');
		if (selected.length == 1) {
		    if (selected.position().top + selected.height() > list.height()) {
		        list.scrollTop(list.scrollTop() + selected.position().top + selected.height() - list.height());
		    } else if (selected.position().top < 0) {
		        list.scrollTop(list.scrollTop() + selected.position().top);
		    }
		}
	}
});
};


modules['helpcenter/primary/plugins/drilldown'] = function(require, exports, module, modules) {
/**
 * A simple jQuery plugin for creating animated drilldown menus.
 *
 * @name jQuery Drilldown
 * @version 1.0.0
 * @requires jQuery v1.7+
 * @author Aleksandras Nelkinas
 * @license [MIT]{@link http://opensource.org/licenses/mit-license.php}
 *
 * Copyright (c) 2015 Aleksandras Nelkinas
 */

;(function (factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD
    define(['jquery'], factory);
  } else if (typeof exports === 'object') {
    // Node/CommonJS
    module.exports = factory(require('jquery'));
  } else {
    // Browser globals
    factory(jQuery);
  }
}(function ($, undefined) {

  'use strict';

  var pluginName = 'drilldown';
  var defaults = {
    event: 'click',
    speed: 0,
    cssClass: {
      container: 'dd-container',
      root: 'dd-root',
      sub: 'dd-sub',
      back: 'dd__back'
    }
  };

  var Plugin = (function () {

    function Plugin(element, options) {
      var inst = this;

      this._name = pluginName;
      this._defaults = defaults;

      this.element = element;
      this.$element = $(element);

      this.options = $.extend({}, defaults, options);

      this._history = [];
      this._css = {
        float: 'left',
        width: null
      };

      this.$container = this.$element.find('.' + this.options.cssClass.container);

      this.$element.on(this.options.event + '.' + pluginName, '.js-dd__link', function (e) {
        handleAction.call(inst, e, $(this));
      });
    }

    Plugin.prototype = {

      /**
       * Destroys plugin instance.
       */
      destroy: function () {
        this.reset();

        this.$element.off(this.options.event + '.' + pluginName, '.js-dd__link');
      },

      /**
       * Resets drilldown to its initial state.
       */
      reset: function () {
        var $root;

        if (this._history.length) {
          $root = this._history[0];

          this.$container.empty().append($root);
          restoreState.call(this, $root);
        }

        this._history = [];
        this._css = {
          float: 'left',
          width: null
        };
      }

    };

    /**
     * Handles user action and decides whether or not and where to drill.
     *
     * @param {jQuery.Event} e
     * @param {jQuery}       $trigger
     * @private
     */
    function handleAction(e, $trigger) {
      var $next = $trigger.nextAll('.' + this.options.cssClass.sub);
      var preventDefault = true;

      if ($next.length) {
        down.call(this, $next);
        e.stopPropagation();
      } else if ($trigger.closest('.' + this.options.cssClass.back).length) {
        up.call(this);
        e.stopPropagation();
      } else {
        preventDefault = false;
      }

      if (preventDefault && $trigger.prop('tagName') === 'A') {
        e.preventDefault();
      }
    }

    /**
     * Drills down (deeper).
     *
     * @param {jQuery} $next
     * @private
     */
    function down($next) {
      if (!$next.length) {
        return;
      }

      this._css.width = this.$element.outerWidth();
      this.$container.width(this._css.width * 2);

      $next = $next.clone(true)
          .removeClass(this.options.cssClass.sub)
          .addClass(this.options.cssClass.root);

      this.$container.append($next);

      animateDrilling.call(this, -1 * this._css.width, function () {
        var $current = $next.prev();

        this._history.push($current.detach());

        restoreState.call(this, $next);
      }.bind(this));
    }

    /**
     * Drills up (back).
     *
     * @private
     */
    function up() {
      var $next = this._history.pop();

      this._css.width = this.$element.outerWidth();
      this.$container.width(this._css.width * 2);

      this.$container.prepend($next);

      animateDrilling.call(this, 0, function () {
        var $current = $next.next();

        $current.remove();

        restoreState.call(this, $next);
      }.bind(this));
    }

    /**
     * Animates drilling process.
     *
     * @param {Number}   marginLeft Target margin-left.
     * @param {Function} callback
     * @private
     */
    function animateDrilling(marginLeft, callback) {
      var $menus = this.$container.children('.' + this.options.cssClass.root);

      $menus.css(this._css);

      $menus.first().animate({
        marginLeft: marginLeft
      }, this.options.speed, callback);
    }

    /**
     * Restores initial menu's state.
     *
     * @param {jQuery} $menu
     * @private
     */
    function restoreState($menu) {
      $menu.css({
        float: '',
        width: '',
        marginLeft: ''
      });

      this.$container.css('width', '');
    }

    return Plugin;

  })();

  $.fn[pluginName] = function (options) {
    return this.each(function () {
      var inst = $.data(this, pluginName);
      var method = options;

      if (!inst) {
        $.data(this, pluginName, new Plugin(this, options));
      } else if (typeof method === 'string') {
        if (method === 'destroy') {
          $.removeData(this,  pluginName);
        }
        if (typeof inst[method] === 'function') {
          inst[method]();
        }
      }
    });
  };

}));

};


modules['helpcenter/primary/views/manage/category'] = function(require, exports, module, modules) {
var autosize = require('helpcenter/primary/plugins/autosize');
var redirect = require('helpcenter/primary/views/redirect');
var Sortable = require('helpcenter/primary/plugins/sortable');

function getParam(name) {
	var results = new RegExp('[\?&]' + name + '=([^&#]*)')
		.exec(window.location.href);

	return results ? results[1] : 0;
}

module.exports = Backbone.View.extend({
	model: require('helpcenter/primary/models/manage/content'),

	el: $('#category'),
	titles: [],
	descriptions: [],

	events: {
		'click #add-category': 'add',
		'click #edit-content': 'edit',
		'click #cancel-edit': 'cancelEdit',
		'click #prompt': 'prompt',
		'click #cancel-delete': 'cancelDelete',
		'click #save': 'update',
		'click #delete': 'delete',
		'click .js-locale-selector': 'toggleLocaleMenu',
		'click .locale__item': 'switchLocale'
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Gets options from router while instantiating view
	 * @param  {Object}
	 */
	initialize: function(options) {
		var view = this;
		this.options = options || {};
		this.itemId = this.options.itemId.split('-')[0];
		this.lastUpdated = null;
		this.sectionsOrder = [];

		// Initiate textarea auto resizer
		autosize($('.textfield--heading'));

		// Hide locale menu
		$(document).click(function() {
			$('.js-locale-list').removeClass('is-open');
		});

		// Get content for other locales
		if (!$('#add-category').length) {
			new this.model({
				'itemId': this.itemId,
				'item': 'categories',
				'query': 'update'
			}).fetch({
				success: function(response) {
					$.each(response.attributes.data.titles, function(index, val) {
						view.titles.push({
							"locale": val.locale,
							"translation": val.translation
						});
					});
					$.each(response.attributes.data.descriptions, function(index, val) {
						// the API might return null instead of
						// empty string which will fail during PUT
						// @see https://kayako.atlassian.net/browse/API-717
						if (val.translation === null) {
							val.translation = '';
						}

						view.descriptions.push({
							"locale": val.locale,
							"translation": val.translation
						});
					});

					// Fill data into the editor
					var activeTitle = activeDescription = '';
					$.each(view.titles, function(index, val) {
						if (val.locale == _Payload.activelocale) {
							activeTitle = val.translation;
						}
					});
					$.each(view.descriptions, function(index, val) {
						if (val.locale == _Payload.activelocale) {
							activeDescription = val.translation;
						}
					});
					$('#input--heading').val(activeTitle);
					$('#input--description').val(activeDescription);
					$('#edit-content').removeClass('is-disabled');
				},
				error: function(response) {
					// Show error message
					$('#save-bar').addClass('admin-bar--error');
				}
			});
		}

		if (getParam('edit') === '1') {
			this.enableSort();
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Loads the editing view
	 */
	edit: function() {
		var view = this;

		// Store last updated timestamp
		this.lastUpdated = $('#last-updated').find('span').data('time');

		// Remove grey bar
		$('#edit-bar').remove();

		// Show blue bar
		var saveBarTpl = _.template($('#tpl-save-bar').html());
		$('#category').prepend(saveBarTpl({referrer: 'category'}));

		// Show input fields in place of static content
		$('#input-content').removeClass('u-hidden');
		$('#display--heading, #display--description').addClass('u-hidden');

		// Disable uneditable content
		$('#add-block').addClass('u-hiddenvisually');

		// Fill data into the editor
		var activeTitle = activeDescription = '';
		$.each(view.titles, function(index, val) {
			if (val.locale === _Payload.activelocale) {
				activeTitle = val.translation;
			}
		});
		$.each(view.descriptions, function(index, val) {
			if (val.locale === _Payload.activelocale) {
				activeDescription = val.translation;
			}
		});
		$('#input--heading').val(activeTitle);
		$('#input--description').val(activeDescription);
		$('#edit-content').removeClass('is-disabled');

		this.enableSort();
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Reverts the editing view
	 */
	cancelEdit: function() {
		if (!this.lastUpdated) {
			this.lastUpdated = $('#timestamp').val();
		}

		if (this.sortable) {
			this.sortable.sort(this.originalOrder);
			this.sortable.destroy();
		}

		// Remove blue bar
		$('#save-bar').remove();

		// Show grey bar
		var editBarTpl = _.template($('#tpl-edit-bar').html());
		$('#category').prepend(editBarTpl({referrer: 'category', lastUpdated: this.lastUpdated}));
		$(".js-timeago").timeago();

		// Show static data inplace of input fields
		$('#input-content').addClass('u-hidden');
		$('#display--heading, #display--description').removeClass('u-hidden');

		$('#sectionlist-edit-view').addClass('u-hiddenvisually');

		// Remove disabled class from uneditable content
		$('#add-block, #listing, #sectionlist-view').removeClass('u-hiddenvisually');
	},

	enableSort: function() {
		var view = this;
		$('#listing').addClass('u-hiddenvisually');
		var itemId = this.itemId;

		// NOTE: can't use jquery here as Sortable.create requires object returned by getElementById
		var sections = document.getElementById('sectionlist-edit-view');
		if (sections && sections.getAttribute('data-sortable')) {
			$('#sectionlist-view').addClass('u-hiddenvisually');
			$('#sectionlist-edit-view').removeClass('u-hiddenvisually');

			this.originalOrder = $('#sectionlist-edit-view')
				.children('li')
				.map(function(index, x) { return x.id; } )
				.toArray();
			this.sectionsOrder = this.originalOrder;

			this.sortable = Sortable.create(sections, {
				group: "sections-list--sort-order",
				ghostClass: "sortable-listing-section-ghost",
				chosenClass: "sortable-listing-section-selected",
				animation: 150,
				store: {
					get: function(sortable) {
						return view.sectionsOrder;
					},
					set: function(sortable) {
						var sectionIds = sortable.toArray();
						view.sectionsOrder = sectionIds;
					}
				}
			});
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Checks if title and content of main locale are filled
	 * @return {Boolean}
	 */
	validate: function() {
		var view = this;
		var hasTitle = false;

		// Check if primary locale title is filled
		if (view.titles.length) {
			$.each(view.titles, function(index, el) {
				if (el.locale == _Payload.defaultlocale && el.translation.length) {
					hasTitle = true;
				}
			});
		}

		if (hasTitle) {
			$('#add-category').prop('disabled', 'true').addClass('button--loading');
			return true;
		} else {
			// Show error message
			$('#save-bar').addClass('admin-bar--required');
			return false;
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Pushes current locale data to titles[] and desctiptions[]
	 */
	pushData: function() {
		var view = this;
		var titleInArray, titleInArrayIndex, descInArray, descInArrayIndex, hasDefaultDesc;
		var activeLocale = $('.js-locale__item.is-active').clone().children().remove().end().text();
		$('#save-bar').removeClass('admin-bar--required');

		// Check if title is already there in array
		$.each(view.titles, function(index, value) {
			if (value.locale == activeLocale) {
				titleInArray = true;
				titleInArrayIndex = index;
			}
		});

		// Remove title from array if already present
		if (titleInArray) {
			view.titles.splice(titleInArrayIndex, 1);
		}

		// Push new title
		view.titles.push({
			"locale": activeLocale,
			"translation": $('#input--heading').val()
		});

		// Check if description is already there in array
		$.each(view.descriptions, function(index, value) {
			if (value.locale == activeLocale) {
				descInArray = true;
				descInArrayIndex = index;
			}
		});

		// Remove description from array if already present
		if (descInArray) {
			view.descriptions.splice(descInArrayIndex, 1);
		}

		// Push new description
		view.descriptions.push({
			"locale": activeLocale,
			"translation": $('#input--description').val()
		});

		// Push empty description for default language
		// because API does not accept it's empty value
		$.each(view.descriptions, function(index, value) {
			if (value.locale == _Payload.defaultlocale) {
				hasDefaultDesc = true;
			}
		});
		if (!hasDefaultDesc) {
			view.descriptions.push({
				"locale": _Payload.defaultlocale,
				"translation": ''
			});
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Creates new category
	 */
	add: function() {
		var view = this;

		// Push current locale data
		view.pushData();

		// Send to server if valid
		if (view.validate()) {
			// Disable buttons and show loading animation
			$('#cancel-edit').addClass('is-disabled');

			// Send POST request
			new this.model({
				'item': 'categories',
				'query': 'fetch'
			}).save({
				'brand_id': _Payload.brand_id,
				'titles': view.titles,
				'descriptions': view.descriptions
			}, {
				success: function(response) {
					// Redirect to category page
					$.each(response.attributes.data.slugs, function(index, el) {
						if (el.locale == _Payload.activelocale) {
							window.location.replace(_Payload.url + 'category/' + el.translation);
						} else if (el.locale == _Payload.defaultlocale) {
							window.location.replace(_Payload.url + 'category/' + el.translation);
						}
					});
				},
				error: function(model, xhr, response) {
					view.notSaved(xhr.status, xhr.responseJSON.errors[0]);
				}
			});
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Updates category in database
	 */
	update: function() {
		var view = this;

		// Push current locale data
		view.pushData();

		// Send to server if valid
		if (view.validate()) {
			// Disable buttons and show loading animation
			$('#cancel-edit').addClass('is-disabled');
			$('#save')
				.addClass('is-disabled button--loading')
				.prop('disabled', 'true')
				.after(_.template($('#tpl-loader').html()));

			var sortable = this.sortable;
			var sectionIds = view.sectionsOrder;
			var data = {
				'section_ids': sectionIds.join(','),
				'category_id': this.itemId
			};

			// Send update request
			new this.model({
				'itemId': this.itemId,
				'item': 'categories',
				'query': 'update'
			}).save({
				'brand_id': _Payload.brand_id,
				'titles': view.titles,
				'descriptions': view.descriptions
			}, {
				success: function() {
					// Reload the page
					if (view.sortable && !_.isEqual(view.articleOrder, view.originalOrder)) {
						$.ajax({
							type: 'PUT',
							url: '/api/v1/sections/reorder',
							headers: {
								'X-CSRF-Token': localStorage.getItem('ko:core:csrf') ? localStorage.getItem('ko:core:csrf').replace(/\"/g, '') : '',
								'Content-Type': 'application/json'
							},
							data: JSON.stringify(data),
							success: function(res) {
								// store order to localStorage
								view.sectionsOrder = [];
								view.originalOrder = [];
								view.sortable.destroy();
								window.location.href = window.location.href.replace('?edit=1', '');
							},
							error: function(response) {
								view.sortable.sort(view.originalOrder);

								var res = response.responseJSON;
								view.notSaved(res.status, res.errors[0]);
							}
						});
					} else {
						window.location.href = window.location.href.replace('?edit=1', '');
					}
				},
				error: function(model, xhr, response) {
					view.notSaved(xhr.status, xhr.responseJSON.errors[0]);
				}
			});
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Shows error if category fails to save
	 */
	notSaved: function(errorCode, errorMsg) {
		// Redirect if required
		if (errorCode == 404 || errorCode == 401) {
			redirect('category', view.options.itemId, errorCode);
		} else {
			// Show error message
			$('#save-bar').addClass('admin-bar--api-error');
			$('.admin-api-error').remove();

			var errorParameter = '';
			if (errorMsg.parameter) {
				errorParameter = '<span class="u-textbold u-capitalize">' + errorMsg.parameter + '</span>: ';
			}

			$('#save-bar__text').append('<span class="admin-api-error u-textwarning">' + errorParameter + '' + errorMsg.message + '.</span>');

			// Re-enable buttons and show loading animation
			$('#cancel-edit').removeClass('is-disabled');
			$('#save, #add-category').removeClass('is-disabled button--loading').removeAttr('disabled');
		}
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Shows the modal
	 */
	prompt: function() {
		var view = this;
		var modalTpl = _.template($('#tpl-delete-modal').html());
		$('#category').append(modalTpl);
		$('#delete-modal').fadeIn(300);
		$(document).keyup(function(e) {
			if (e.keyCode == 27) {
				view.cancelDelete();
			}
		});
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Hides the modal
	 */
	cancelDelete: function() {
		$('#delete-modal').fadeOut(200, function() {
			$(this).remove();
		});
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Deletes the category from database
	 */
	delete: function() {
		// Disable buttons and show loading animation
		$('#cancel-delete').addClass('is-disabled');
		$('#delete')
			.addClass('is-disabled button--loading')
			.prop('disabled', 'true')
			.after(_.template($('#tpl-loader').html()));

		new this.model({
			'itemId': this.itemId,
			'item': 'categories',
			'query': 'update'
		}).destroy({
			success: function() {
				// Redirect to homepage
				window.location.href = _Payload.url;
			},
			error: function() {
				// Show error message
				$('.modal__footer').addClass('modal__footer--error');

				// Re-enable buttons and hide loading animation
				$('#cancel-delete').removeClass('is-disabled');
				$('#delete')
					.removeClass('is-disabled button--loading')
					.removeAttr('disabled');
			}
		});
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Show/hide locale dropdown menu
	 */
	toggleLocaleMenu: function(e) {
		e.stopPropagation();
		$('.js-locale-list').toggleClass('is-open');
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Switches among active locales (Changes data of input fields)
	 * @param  {Object} e
	 */
	switchLocale: function(e) {
		var view = this;
		var titleInArray, titleInArrayIndex, descInArray, descInArrayIndex;
		var selectedTitle = selectedDesc = '';
		var selected = $(e.target).closest('.js-locale__item');

		if (!selected.hasClass('is-active')) {
			var activeLocale = $('.js-locale__item.is-active').clone().children().remove().end().text();
			var activeTitle = $('#input--heading').val();
			var activeDesc = $('#input--description').val();
			var selectedLocale = selected.clone().children().remove().end().text();

			// Check if title is already there in array
			$.each(view.titles, function(index, value) {
				if (value.locale == activeLocale) {
					titleInArray = true;
					titleInArrayIndex = index;
				}
				if (value.locale == selectedLocale) {
					selectedTitle = value.translation;
				}
			});

			// Remove title from array if already present
			if (titleInArray) {
				view.titles.splice(titleInArrayIndex, 1);
			}

			// Check if description is already there in array
			$.each(view.descriptions, function(index, value) {
				if (value.locale == activeLocale) {
					descInArray = true;
					descInArrayIndex = index;
				}
				if (value.locale == selectedLocale) {
					selectedDesc = value.translation;
				}
			});

			// Remove description from array if already present
			if (descInArray) {
				view.descriptions.splice(descInArrayIndex, 1);
			}

			// Fill input fields with selected locale
			$('#input--heading').val(selectedTitle);
			$('#input--description').val(selectedDesc);

			// Push currrent locale data into array
			view.titles.push({
				"locale": activeLocale,
				"translation": activeTitle
			});
			view.descriptions.push({
				"locale": activeLocale,
				"translation": activeDesc
			});

			// Tick the selected locale
			$(e.target).closest('.js-locale__item').addClass('is-active').siblings('.js-locale__item').removeClass('is-active');
			if (selectedLocale != _Payload.defaultlocale) {
				$('#locale-icon').removeClass('icon-locale').addClass('icon-locale-alert');
				$('#active-locale').text(selectedLocale);
			} else {
				$('#locale-icon').removeClass('icon-locale-alert').addClass('icon-locale');
				$('#active-locale').empty();
			}

		}
	}
});

};


modules['helpcenter/primary/models/manage/attachment'] = function(require, exports, module, modules) {
module.exports = Backbone.Model.extend({
    idAttribute: 'itemId',
    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @param  {Object}
     */
    initialize: function(options) {
        this.articleId = options.articleId;
        this.itemId = options.itemId;
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @return {Obejct}
     */
    url: function() {
        return _Payload.portalpath + '/api/v1/articles/' + this.articleId + '/attachments/' + this.itemId + '.json?include=*';
    }
});

};


modules['helpcenter/primary/plugins/datepicker'] = function(require, exports, module, modules) {
$ = require('jquery');

/*!
 * Date picker for pickadate.js v3.5.6
 * http://amsul.github.io/pickadate.js/date.htm
 */

(function ( factory ) {

    // AMD.
    if ( typeof define == 'function' && define.amd )
        define( ['picker', 'jquery'], factory )

    // Node.js/browserify.
    else if ( typeof exports == 'object' )
        module.exports = factory( require('helpcenter/primary/plugins/picker'), require('jquery') )

    // Browser globals.
    else factory( Picker, jQuery )

}(function( Picker, $ ) {


/**
 * Globals and constants
 */
var DAYS_IN_WEEK = 7,
    WEEKS_IN_CALENDAR = 6,
    _ = Picker._



/**
 * The date picker constructor
 */
function DatePicker( picker, settings ) {

    var calendar = this,
        element = picker.$node[ 0 ],
        elementValue = element.value,
        elementDataValue = picker.$node.data( 'value' ),
        valueString = elementDataValue || elementValue,
        formatString = elementDataValue ? settings.formatSubmit : settings.format,
        isRTL = function() {

            return element.currentStyle ?

                // For IE.
                element.currentStyle.direction == 'rtl' :

                // For normal browsers.
                getComputedStyle( picker.$root[0] ).direction == 'rtl'
        }

    calendar.settings = settings
    calendar.$node = picker.$node

    // The queue of methods that will be used to build item objects.
    calendar.queue = {
        min: 'measure create',
        max: 'measure create',
        now: 'now create',
        select: 'parse create validate',
        highlight: 'parse navigate create validate',
        view: 'parse create validate viewset',
        disable: 'deactivate',
        enable: 'activate'
    }

    // The component's item object.
    calendar.item = {}

    calendar.item.clear = null
    calendar.item.disable = ( settings.disable || [] ).slice( 0 )
    calendar.item.enable = -(function( collectionDisabled ) {
        return collectionDisabled[ 0 ] === true ? collectionDisabled.shift() : -1
    })( calendar.item.disable )

    calendar.
        set( 'min', settings.min ).
        set( 'max', settings.max ).
        set( 'now' )

    // When there’s a value, set the `select`, which in turn
    // also sets the `highlight` and `view`.
    if ( valueString ) {
        calendar.set( 'select', valueString, {
            format: formatString,
            defaultValue: true
        })
    }

    // If there’s no value, default to highlighting “today”.
    else {
        calendar.
            set( 'select', null ).
            set( 'highlight', calendar.item.now )
    }


    // The keycode to movement mapping.
    calendar.key = {
        40: 7, // Down
        38: -7, // Up
        39: function() { return isRTL() ? -1 : 1 }, // Right
        37: function() { return isRTL() ? 1 : -1 }, // Left
        go: function( timeChange ) {
            var highlightedObject = calendar.item.highlight,
                targetDate = new Date( highlightedObject.year, highlightedObject.month, highlightedObject.date + timeChange )
            calendar.set(
                'highlight',
                targetDate,
                { interval: timeChange }
            )
            this.render()
        }
    }


    // Bind some picker events.
    picker.
        on( 'render', function() {
            picker.$root.find( '.' + settings.klass.selectMonth ).on( 'change', function() {
                var value = this.value
                if ( value ) {
                    picker.set( 'highlight', [ picker.get( 'view' ).year, value, picker.get( 'highlight' ).date ] )
                    picker.$root.find( '.' + settings.klass.selectMonth ).trigger( 'focus' )
                }
            })
            picker.$root.find( '.' + settings.klass.selectYear ).on( 'change', function() {
                var value = this.value
                if ( value ) {
                    picker.set( 'highlight', [ value, picker.get( 'view' ).month, picker.get( 'highlight' ).date ] )
                    picker.$root.find( '.' + settings.klass.selectYear ).trigger( 'focus' )
                }
            })
        }, 1 ).
        on( 'open', function() {
            var includeToday = ''
            if ( calendar.disabled( calendar.get('now') ) ) {
                includeToday = ':not(.' + settings.klass.buttonToday + ')'
            }
            picker.$root.find( 'button' + includeToday + ', select' ).attr( 'disabled', false )
        }, 1 ).
        on( 'close', function() {
            picker.$root.find( 'button, select' ).attr( 'disabled', true )
        }, 1 )

} //DatePicker


/**
 * Set a datepicker item object.
 */
DatePicker.prototype.set = function( type, value, options ) {

    var calendar = this,
        calendarItem = calendar.item

    // If the value is `null` just set it immediately.
    if ( value === null ) {
        if ( type == 'clear' ) type = 'select'
        calendarItem[ type ] = value
        return calendar
    }

    // Otherwise go through the queue of methods, and invoke the functions.
    // Update this as the time unit, and set the final value as this item.
    // * In the case of `enable`, keep the queue but set `disable` instead.
    //   And in the case of `flip`, keep the queue but set `enable` instead.
    calendarItem[ ( type == 'enable' ? 'disable' : type == 'flip' ? 'enable' : type ) ] = calendar.queue[ type ].split( ' ' ).map( function( method ) {
        value = calendar[ method ]( type, value, options )
        return value
    }).pop()

    // Check if we need to cascade through more updates.
    if ( type == 'select' ) {
        calendar.set( 'highlight', calendarItem.select, options )
    }
    else if ( type == 'highlight' ) {
        calendar.set( 'view', calendarItem.highlight, options )
    }
    else if ( type.match( /^(flip|min|max|disable|enable)$/ ) ) {
        if ( calendarItem.select && calendar.disabled( calendarItem.select ) ) {
            calendar.set( 'select', calendarItem.select, options )
        }
        if ( calendarItem.highlight && calendar.disabled( calendarItem.highlight ) ) {
            calendar.set( 'highlight', calendarItem.highlight, options )
        }
    }

    return calendar
} //DatePicker.prototype.set


/**
 * Get a datepicker item object.
 */
DatePicker.prototype.get = function( type ) {
    return this.item[ type ]
} //DatePicker.prototype.get


/**
 * Create a picker date object.
 */
DatePicker.prototype.create = function( type, value, options ) {

    var isInfiniteValue,
        calendar = this

    // If there’s no value, use the type as the value.
    value = value === undefined ? type : value


    // If it’s infinity, update the value.
    if ( value == -Infinity || value == Infinity ) {
        isInfiniteValue = value
    }

    // If it’s an object, use the native date object.
    else if ( $.isPlainObject( value ) && _.isInteger( value.pick ) ) {
        value = value.obj
    }

    // If it’s an array, convert it into a date and make sure
    // that it’s a valid date – otherwise default to today.
    else if ( $.isArray( value ) ) {
        value = new Date( value[ 0 ], value[ 1 ], value[ 2 ] )
        value = _.isDate( value ) ? value : calendar.create().obj
    }

    // If it’s a number or date object, make a normalized date.
    else if ( _.isInteger( value ) || _.isDate( value ) ) {
        value = calendar.normalize( new Date( value ), options )
    }

    // If it’s a literal true or any other case, set it to now.
    else /*if ( value === true )*/ {
        value = calendar.now( type, value, options )
    }

    // Return the compiled object.
    return {
        year: isInfiniteValue || value.getFullYear(),
        month: isInfiniteValue || value.getMonth(),
        date: isInfiniteValue || value.getDate(),
        day: isInfiniteValue || value.getDay(),
        obj: isInfiniteValue || value,
        pick: isInfiniteValue || value.getTime()
    }
} //DatePicker.prototype.create


/**
 * Create a range limit object using an array, date object,
 * literal “true”, or integer relative to another time.
 */
DatePicker.prototype.createRange = function( from, to ) {

    var calendar = this,
        createDate = function( date ) {
            if ( date === true || $.isArray( date ) || _.isDate( date ) ) {
                return calendar.create( date )
            }
            return date
        }

    // Create objects if possible.
    if ( !_.isInteger( from ) ) {
        from = createDate( from )
    }
    if ( !_.isInteger( to ) ) {
        to = createDate( to )
    }

    // Create relative dates.
    if ( _.isInteger( from ) && $.isPlainObject( to ) ) {
        from = [ to.year, to.month, to.date + from ];
    }
    else if ( _.isInteger( to ) && $.isPlainObject( from ) ) {
        to = [ from.year, from.month, from.date + to ];
    }

    return {
        from: createDate( from ),
        to: createDate( to )
    }
} //DatePicker.prototype.createRange


/**
 * Check if a date unit falls within a date range object.
 */
DatePicker.prototype.withinRange = function( range, dateUnit ) {
    range = this.createRange(range.from, range.to)
    return dateUnit.pick >= range.from.pick && dateUnit.pick <= range.to.pick
}


/**
 * Check if two date range objects overlap.
 */
DatePicker.prototype.overlapRanges = function( one, two ) {

    var calendar = this

    // Convert the ranges into comparable dates.
    one = calendar.createRange( one.from, one.to )
    two = calendar.createRange( two.from, two.to )

    return calendar.withinRange( one, two.from ) || calendar.withinRange( one, two.to ) ||
        calendar.withinRange( two, one.from ) || calendar.withinRange( two, one.to )
}


/**
 * Get the date today.
 */
DatePicker.prototype.now = function( type, value, options ) {
    value = new Date()
    if ( options && options.rel ) {
        value.setDate( value.getDate() + options.rel )
    }
    return this.normalize( value, options )
}


/**
 * Navigate to next/prev month.
 */
DatePicker.prototype.navigate = function( type, value, options ) {

    var targetDateObject,
        targetYear,
        targetMonth,
        targetDate,
        isTargetArray = $.isArray( value ),
        isTargetObject = $.isPlainObject( value ),
        viewsetObject = this.item.view/*,
        safety = 100*/


    if ( isTargetArray || isTargetObject ) {

        if ( isTargetObject ) {
            targetYear = value.year
            targetMonth = value.month
            targetDate = value.date
        }
        else {
            targetYear = +value[0]
            targetMonth = +value[1]
            targetDate = +value[2]
        }

        // If we’re navigating months but the view is in a different
        // month, navigate to the view’s year and month.
        if ( options && options.nav && viewsetObject && viewsetObject.month !== targetMonth ) {
            targetYear = viewsetObject.year
            targetMonth = viewsetObject.month
        }

        // Figure out the expected target year and month.
        targetDateObject = new Date( targetYear, targetMonth + ( options && options.nav ? options.nav : 0 ), 1 )
        targetYear = targetDateObject.getFullYear()
        targetMonth = targetDateObject.getMonth()

        // If the month we’re going to doesn’t have enough days,
        // keep decreasing the date until we reach the month’s last date.
        while ( /*safety &&*/ new Date( targetYear, targetMonth, targetDate ).getMonth() !== targetMonth ) {
            targetDate -= 1
            /*safety -= 1
            if ( !safety ) {
                throw 'Fell into an infinite loop while navigating to ' + new Date( targetYear, targetMonth, targetDate ) + '.'
            }*/
        }

        value = [ targetYear, targetMonth, targetDate ]
    }

    return value
} //DatePicker.prototype.navigate


/**
 * Normalize a date by setting the hours to midnight.
 */
DatePicker.prototype.normalize = function( value/*, options*/ ) {
    value.setHours( 0, 0, 0, 0 )
    return value
}


/**
 * Measure the range of dates.
 */
DatePicker.prototype.measure = function( type, value/*, options*/ ) {

    var calendar = this

    // If it’s anything false-y, remove the limits.
    if ( !value ) {
        value = type == 'min' ? -Infinity : Infinity
    }

    // If it’s a string, parse it.
    else if ( typeof value == 'string' ) {
        value = calendar.parse( type, value )
    }

    // If it's an integer, get a date relative to today.
    else if ( _.isInteger( value ) ) {
        value = calendar.now( type, value, { rel: value } )
    }

    return value
} ///DatePicker.prototype.measure


/**
 * Create a viewset object based on navigation.
 */
DatePicker.prototype.viewset = function( type, dateObject/*, options*/ ) {
    return this.create([ dateObject.year, dateObject.month, 1 ])
}


/**
 * Validate a date as enabled and shift if needed.
 */
DatePicker.prototype.validate = function( type, dateObject, options ) {

    var calendar = this,

        // Keep a reference to the original date.
        originalDateObject = dateObject,

        // Make sure we have an interval.
        interval = options && options.interval ? options.interval : 1,

        // Check if the calendar enabled dates are inverted.
        isFlippedBase = calendar.item.enable === -1,

        // Check if we have any enabled dates after/before now.
        hasEnabledBeforeTarget, hasEnabledAfterTarget,

        // The min & max limits.
        minLimitObject = calendar.item.min,
        maxLimitObject = calendar.item.max,

        // Check if we’ve reached the limit during shifting.
        reachedMin, reachedMax,

        // Check if the calendar is inverted and at least one weekday is enabled.
        hasEnabledWeekdays = isFlippedBase && calendar.item.disable.filter( function( value ) {

            // If there’s a date, check where it is relative to the target.
            if ( $.isArray( value ) ) {
                var dateTime = calendar.create( value ).pick
                if ( dateTime < dateObject.pick ) hasEnabledBeforeTarget = true
                else if ( dateTime > dateObject.pick ) hasEnabledAfterTarget = true
            }

            // Return only integers for enabled weekdays.
            return _.isInteger( value )
        }).length/*,

        safety = 100*/



    // Cases to validate for:
    // [1] Not inverted and date disabled.
    // [2] Inverted and some dates enabled.
    // [3] Not inverted and out of range.
    //
    // Cases to **not** validate for:
    // • Navigating months.
    // • Not inverted and date enabled.
    // • Inverted and all dates disabled.
    // • ..and anything else.
    if ( !options || (!options.nav && !options.defaultValue) ) if (
        /* 1 */ ( !isFlippedBase && calendar.disabled( dateObject ) ) ||
        /* 2 */ ( isFlippedBase && calendar.disabled( dateObject ) && ( hasEnabledWeekdays || hasEnabledBeforeTarget || hasEnabledAfterTarget ) ) ||
        /* 3 */ ( !isFlippedBase && (dateObject.pick <= minLimitObject.pick || dateObject.pick >= maxLimitObject.pick) )
    ) {


        // When inverted, flip the direction if there aren’t any enabled weekdays
        // and there are no enabled dates in the direction of the interval.
        if ( isFlippedBase && !hasEnabledWeekdays && ( ( !hasEnabledAfterTarget && interval > 0 ) || ( !hasEnabledBeforeTarget && interval < 0 ) ) ) {
            interval *= -1
        }


        // Keep looping until we reach an enabled date.
        while ( /*safety &&*/ calendar.disabled( dateObject ) ) {

            /*safety -= 1
            if ( !safety ) {
                throw 'Fell into an infinite loop while validating ' + dateObject.obj + '.'
            }*/


            // If we’ve looped into the next/prev month with a large interval, return to the original date and flatten the interval.
            if ( Math.abs( interval ) > 1 && ( dateObject.month < originalDateObject.month || dateObject.month > originalDateObject.month ) ) {
                dateObject = originalDateObject
                interval = interval > 0 ? 1 : -1
            }


            // If we’ve reached the min/max limit, reverse the direction, flatten the interval and set it to the limit.
            if ( dateObject.pick <= minLimitObject.pick ) {
                reachedMin = true
                interval = 1
                dateObject = calendar.create([
                    minLimitObject.year,
                    minLimitObject.month,
                    minLimitObject.date + (dateObject.pick === minLimitObject.pick ? 0 : -1)
                ])
            }
            else if ( dateObject.pick >= maxLimitObject.pick ) {
                reachedMax = true
                interval = -1
                dateObject = calendar.create([
                    maxLimitObject.year,
                    maxLimitObject.month,
                    maxLimitObject.date + (dateObject.pick === maxLimitObject.pick ? 0 : 1)
                ])
            }


            // If we’ve reached both limits, just break out of the loop.
            if ( reachedMin && reachedMax ) {
                break
            }


            // Finally, create the shifted date using the interval and keep looping.
            dateObject = calendar.create([ dateObject.year, dateObject.month, dateObject.date + interval ])
        }

    } //endif


    // Return the date object settled on.
    return dateObject
} //DatePicker.prototype.validate


/**
 * Check if a date is disabled.
 */
DatePicker.prototype.disabled = function( dateToVerify ) {

    var
        calendar = this,

        // Filter through the disabled dates to check if this is one.
        isDisabledMatch = calendar.item.disable.filter( function( dateToDisable ) {

            // If the date is a number, match the weekday with 0index and `firstDay` check.
            if ( _.isInteger( dateToDisable ) ) {
                return dateToVerify.day === ( calendar.settings.firstDay ? dateToDisable : dateToDisable - 1 ) % 7
            }

            // If it’s an array or a native JS date, create and match the exact date.
            if ( $.isArray( dateToDisable ) || _.isDate( dateToDisable ) ) {
                return dateToVerify.pick === calendar.create( dateToDisable ).pick
            }

            // If it’s an object, match a date within the “from” and “to” range.
            if ( $.isPlainObject( dateToDisable ) ) {
                return calendar.withinRange( dateToDisable, dateToVerify )
            }
        })

    // If this date matches a disabled date, confirm it’s not inverted.
    isDisabledMatch = isDisabledMatch.length && !isDisabledMatch.filter(function( dateToDisable ) {
        return $.isArray( dateToDisable ) && dateToDisable[3] == 'inverted' ||
            $.isPlainObject( dateToDisable ) && dateToDisable.inverted
    }).length

    // Check the calendar “enabled” flag and respectively flip the
    // disabled state. Then also check if it’s beyond the min/max limits.
    return calendar.item.enable === -1 ? !isDisabledMatch : isDisabledMatch ||
        dateToVerify.pick < calendar.item.min.pick ||
        dateToVerify.pick > calendar.item.max.pick

} //DatePicker.prototype.disabled


/**
 * Parse a string into a usable type.
 */
DatePicker.prototype.parse = function( type, value, options ) {

    var calendar = this,
        parsingObject = {}

    // If it’s already parsed, we’re good.
    if ( !value || typeof value != 'string' ) {
        return value
    }

    // We need a `.format` to parse the value with.
    if ( !( options && options.format ) ) {
        options = options || {}
        options.format = calendar.settings.format
    }

    // Convert the format into an array and then map through it.
    calendar.formats.toArray( options.format ).map( function( label ) {

        var
            // Grab the formatting label.
            formattingLabel = calendar.formats[ label ],

            // The format length is from the formatting label function or the
            // label length without the escaping exclamation (!) mark.
            formatLength = formattingLabel ? _.trigger( formattingLabel, calendar, [ value, parsingObject ] ) : label.replace( /^!/, '' ).length

        // If there's a format label, split the value up to the format length.
        // Then add it to the parsing object with appropriate label.
        if ( formattingLabel ) {
            parsingObject[ label ] = value.substr( 0, formatLength )
        }

        // Update the value as the substring from format length to end.
        value = value.substr( formatLength )
    })

    // Compensate for month 0index.
    return [
        parsingObject.yyyy || parsingObject.yy,
        +( parsingObject.mm || parsingObject.m ) - 1,
        parsingObject.dd || parsingObject.d
    ]
} //DatePicker.prototype.parse


/**
 * Various formats to display the object in.
 */
DatePicker.prototype.formats = (function() {

    // Return the length of the first word in a collection.
    function getWordLengthFromCollection( string, collection, dateObject ) {

        // Grab the first word from the string.
        // Regex pattern from http://stackoverflow.com/q/150033
        var word = string.match( /[^\x00-\x7F]+|\w+/ )[ 0 ]

        // If there's no month index, add it to the date object
        if ( !dateObject.mm && !dateObject.m ) {
            dateObject.m = collection.indexOf( word ) + 1
        }

        // Return the length of the word.
        return word.length
    }

    // Get the length of the first word in a string.
    function getFirstWordLength( string ) {
        return string.match( /\w+/ )[ 0 ].length
    }

    return {

        d: function( string, dateObject ) {

            // If there's string, then get the digits length.
            // Otherwise return the selected date.
            return string ? _.digits( string ) : dateObject.date
        },
        dd: function( string, dateObject ) {

            // If there's a string, then the length is always 2.
            // Otherwise return the selected date with a leading zero.
            return string ? 2 : _.lead( dateObject.date )
        },
        ddd: function( string, dateObject ) {

            // If there's a string, then get the length of the first word.
            // Otherwise return the short selected weekday.
            return string ? getFirstWordLength( string ) : this.settings.weekdaysShort[ dateObject.day ]
        },
        dddd: function( string, dateObject ) {

            // If there's a string, then get the length of the first word.
            // Otherwise return the full selected weekday.
            return string ? getFirstWordLength( string ) : this.settings.weekdaysFull[ dateObject.day ]
        },
        m: function( string, dateObject ) {

            // If there's a string, then get the length of the digits
            // Otherwise return the selected month with 0index compensation.
            return string ? _.digits( string ) : dateObject.month + 1
        },
        mm: function( string, dateObject ) {

            // If there's a string, then the length is always 2.
            // Otherwise return the selected month with 0index and leading zero.
            return string ? 2 : _.lead( dateObject.month + 1 )
        },
        mmm: function( string, dateObject ) {

            var collection = this.settings.monthsShort

            // If there's a string, get length of the relevant month from the short
            // months collection. Otherwise return the selected month from that collection.
            return string ? getWordLengthFromCollection( string, collection, dateObject ) : collection[ dateObject.month ]
        },
        mmmm: function( string, dateObject ) {

            var collection = this.settings.monthsFull

            // If there's a string, get length of the relevant month from the full
            // months collection. Otherwise return the selected month from that collection.
            return string ? getWordLengthFromCollection( string, collection, dateObject ) : collection[ dateObject.month ]
        },
        yy: function( string, dateObject ) {

            // If there's a string, then the length is always 2.
            // Otherwise return the selected year by slicing out the first 2 digits.
            return string ? 2 : ( '' + dateObject.year ).slice( 2 )
        },
        yyyy: function( string, dateObject ) {

            // If there's a string, then the length is always 4.
            // Otherwise return the selected year.
            return string ? 4 : dateObject.year
        },

        // Create an array by splitting the formatting string passed.
        toArray: function( formatString ) { return formatString.split( /(d{1,4}|m{1,4}|y{4}|yy|!.)/g ) },

        // Format an object into a string using the formatting options.
        toString: function ( formatString, itemObject ) {
            var calendar = this
            return calendar.formats.toArray( formatString ).map( function( label ) {
                return _.trigger( calendar.formats[ label ], calendar, [ 0, itemObject ] ) || label.replace( /^!/, '' )
            }).join( '' )
        }
    }
})() //DatePicker.prototype.formats




/**
 * Check if two date units are the exact.
 */
DatePicker.prototype.isDateExact = function( one, two ) {

    var calendar = this

    // When we’re working with weekdays, do a direct comparison.
    if (
        ( _.isInteger( one ) && _.isInteger( two ) ) ||
        ( typeof one == 'boolean' && typeof two == 'boolean' )
     ) {
        return one === two
    }

    // When we’re working with date representations, compare the “pick” value.
    if (
        ( _.isDate( one ) || $.isArray( one ) ) &&
        ( _.isDate( two ) || $.isArray( two ) )
    ) {
        return calendar.create( one ).pick === calendar.create( two ).pick
    }

    // When we’re working with range objects, compare the “from” and “to”.
    if ( $.isPlainObject( one ) && $.isPlainObject( two ) ) {
        return calendar.isDateExact( one.from, two.from ) && calendar.isDateExact( one.to, two.to )
    }

    return false
}


/**
 * Check if two date units overlap.
 */
DatePicker.prototype.isDateOverlap = function( one, two ) {

    var calendar = this,
        firstDay = calendar.settings.firstDay ? 1 : 0

    // When we’re working with a weekday index, compare the days.
    if ( _.isInteger( one ) && ( _.isDate( two ) || $.isArray( two ) ) ) {
        one = one % 7 + firstDay
        return one === calendar.create( two ).day + 1
    }
    if ( _.isInteger( two ) && ( _.isDate( one ) || $.isArray( one ) ) ) {
        two = two % 7 + firstDay
        return two === calendar.create( one ).day + 1
    }

    // When we’re working with range objects, check if the ranges overlap.
    if ( $.isPlainObject( one ) && $.isPlainObject( two ) ) {
        return calendar.overlapRanges( one, two )
    }

    return false
}


/**
 * Flip the “enabled” state.
 */
DatePicker.prototype.flipEnable = function(val) {
    var itemObject = this.item
    itemObject.enable = val || (itemObject.enable == -1 ? 1 : -1)
}


/**
 * Mark a collection of dates as “disabled”.
 */
DatePicker.prototype.deactivate = function( type, datesToDisable ) {

    var calendar = this,
        disabledItems = calendar.item.disable.slice(0)


    // If we’re flipping, that’s all we need to do.
    if ( datesToDisable == 'flip' ) {
        calendar.flipEnable()
    }

    else if ( datesToDisable === false ) {
        calendar.flipEnable(1)
        disabledItems = []
    }

    else if ( datesToDisable === true ) {
        calendar.flipEnable(-1)
        disabledItems = []
    }

    // Otherwise go through the dates to disable.
    else {

        datesToDisable.map(function( unitToDisable ) {

            var matchFound

            // When we have disabled items, check for matches.
            // If something is matched, immediately break out.
            for ( var index = 0; index < disabledItems.length; index += 1 ) {
                if ( calendar.isDateExact( unitToDisable, disabledItems[index] ) ) {
                    matchFound = true
                    break
                }
            }

            // If nothing was found, add the validated unit to the collection.
            if ( !matchFound ) {
                if (
                    _.isInteger( unitToDisable ) ||
                    _.isDate( unitToDisable ) ||
                    $.isArray( unitToDisable ) ||
                    ( $.isPlainObject( unitToDisable ) && unitToDisable.from && unitToDisable.to )
                ) {
                    disabledItems.push( unitToDisable )
                }
            }
        })
    }

    // Return the updated collection.
    return disabledItems
} //DatePicker.prototype.deactivate


/**
 * Mark a collection of dates as “enabled”.
 */
DatePicker.prototype.activate = function( type, datesToEnable ) {

    var calendar = this,
        disabledItems = calendar.item.disable,
        disabledItemsCount = disabledItems.length

    // If we’re flipping, that’s all we need to do.
    if ( datesToEnable == 'flip' ) {
        calendar.flipEnable()
    }

    else if ( datesToEnable === true ) {
        calendar.flipEnable(1)
        disabledItems = []
    }

    else if ( datesToEnable === false ) {
        calendar.flipEnable(-1)
        disabledItems = []
    }

    // Otherwise go through the disabled dates.
    else {

        datesToEnable.map(function( unitToEnable ) {

            var matchFound,
                disabledUnit,
                index,
                isExactRange

            // Go through the disabled items and try to find a match.
            for ( index = 0; index < disabledItemsCount; index += 1 ) {

                disabledUnit = disabledItems[index]

                // When an exact match is found, remove it from the collection.
                if ( calendar.isDateExact( disabledUnit, unitToEnable ) ) {
                    matchFound = disabledItems[index] = null
                    isExactRange = true
                    break
                }

                // When an overlapped match is found, add the “inverted” state to it.
                else if ( calendar.isDateOverlap( disabledUnit, unitToEnable ) ) {
                    if ( $.isPlainObject( unitToEnable ) ) {
                        unitToEnable.inverted = true
                        matchFound = unitToEnable
                    }
                    else if ( $.isArray( unitToEnable ) ) {
                        matchFound = unitToEnable
                        if ( !matchFound[3] ) matchFound.push( 'inverted' )
                    }
                    else if ( _.isDate( unitToEnable ) ) {
                        matchFound = [ unitToEnable.getFullYear(), unitToEnable.getMonth(), unitToEnable.getDate(), 'inverted' ]
                    }
                    break
                }
            }

            // If a match was found, remove a previous duplicate entry.
            if ( matchFound ) for ( index = 0; index < disabledItemsCount; index += 1 ) {
                if ( calendar.isDateExact( disabledItems[index], unitToEnable ) ) {
                    disabledItems[index] = null
                    break
                }
            }

            // In the event that we’re dealing with an exact range of dates,
            // make sure there are no “inverted” dates because of it.
            if ( isExactRange ) for ( index = 0; index < disabledItemsCount; index += 1 ) {
                if ( calendar.isDateOverlap( disabledItems[index], unitToEnable ) ) {
                    disabledItems[index] = null
                    break
                }
            }

            // If something is still matched, add it into the collection.
            if ( matchFound ) {
                disabledItems.push( matchFound )
            }
        })
    }

    // Return the updated collection.
    return disabledItems.filter(function( val ) { return val != null })
} //DatePicker.prototype.activate


/**
 * Create a string for the nodes in the picker.
 */
DatePicker.prototype.nodes = function( isOpen ) {

    var
        calendar = this,
        settings = calendar.settings,
        calendarItem = calendar.item,
        nowObject = calendarItem.now,
        selectedObject = calendarItem.select,
        highlightedObject = calendarItem.highlight,
        viewsetObject = calendarItem.view,
        disabledCollection = calendarItem.disable,
        minLimitObject = calendarItem.min,
        maxLimitObject = calendarItem.max,


        // Create the calendar table head using a copy of weekday labels collection.
        // * We do a copy so we don't mutate the original array.
        tableHead = (function( collection, fullCollection ) {

            // If the first day should be Monday, move Sunday to the end.
            if ( settings.firstDay ) {
                collection.push( collection.shift() )
                fullCollection.push( fullCollection.shift() )
            }

            // Create and return the table head group.
            return _.node(
                'thead',
                _.node(
                    'tr',
                    _.group({
                        min: 0,
                        max: DAYS_IN_WEEK - 1,
                        i: 1,
                        node: 'th',
                        item: function( counter ) {
                            return [
                                collection[ counter ],
                                settings.klass.weekdays,
                                'scope=col title="' + fullCollection[ counter ] + '"'
                            ]
                        }
                    })
                )
            ) //endreturn
        })( ( settings.showWeekdaysFull ? settings.weekdaysFull : settings.weekdaysShort ).slice( 0 ), settings.weekdaysFull.slice( 0 ) ), //tableHead


        // Create the nav for next/prev month.
        createMonthNav = function( next ) {

            // Otherwise, return the created month tag.
            return _.node(
                'div',
                ' ',
                settings.klass[ 'nav' + ( next ? 'Next' : 'Prev' ) ] + (

                    // If the focused month is outside the range, disabled the button.
                    ( next && viewsetObject.year >= maxLimitObject.year && viewsetObject.month >= maxLimitObject.month ) ||
                    ( !next && viewsetObject.year <= minLimitObject.year && viewsetObject.month <= minLimitObject.month ) ?
                    ' ' + settings.klass.navDisabled : ''
                ),
                'data-nav=' + ( next || -1 ) + ' ' +
                _.ariaAttr({
                    role: 'button',
                    controls: calendar.$node[0].id + '_table'
                }) + ' ' +
                'title="' + (next ? settings.labelMonthNext : settings.labelMonthPrev ) + '"'
            ) //endreturn
        }, //createMonthNav


        // Create the month label.
        createMonthLabel = function() {

            var monthsCollection = settings.showMonthsShort ? settings.monthsShort : settings.monthsFull

            // If there are months to select, add a dropdown menu.
            if ( settings.selectMonths ) {

                return _.node( 'select',
                    _.group({
                        min: 0,
                        max: 11,
                        i: 1,
                        node: 'option',
                        item: function( loopedMonth ) {

                            return [

                                // The looped month and no classes.
                                monthsCollection[ loopedMonth ], 0,

                                // Set the value and selected index.
                                'value=' + loopedMonth +
                                ( viewsetObject.month == loopedMonth ? ' selected' : '' ) +
                                (
                                    (
                                        ( viewsetObject.year == minLimitObject.year && loopedMonth < minLimitObject.month ) ||
                                        ( viewsetObject.year == maxLimitObject.year && loopedMonth > maxLimitObject.month )
                                    ) ?
                                    ' disabled' : ''
                                )
                            ]
                        }
                    }),
                    settings.klass.selectMonth,
                    ( isOpen ? '' : 'disabled' ) + ' ' +
                    _.ariaAttr({ controls: calendar.$node[0].id + '_table' }) + ' ' +
                    'title="' + settings.labelMonthSelect + '"'
                )
            }

            // If there's a need for a month selector
            return _.node( 'div', monthsCollection[ viewsetObject.month ], settings.klass.month )
        }, //createMonthLabel


        // Create the year label.
        createYearLabel = function() {

            var focusedYear = viewsetObject.year,

            // If years selector is set to a literal "true", set it to 5. Otherwise
            // divide in half to get half before and half after focused year.
            numberYears = settings.selectYears === true ? 5 : ~~( settings.selectYears / 2 )

            // If there are years to select, add a dropdown menu.
            if ( numberYears ) {

                var
                    minYear = minLimitObject.year,
                    maxYear = maxLimitObject.year,
                    lowestYear = focusedYear - numberYears,
                    highestYear = focusedYear + numberYears

                // If the min year is greater than the lowest year, increase the highest year
                // by the difference and set the lowest year to the min year.
                if ( minYear > lowestYear ) {
                    highestYear += minYear - lowestYear
                    lowestYear = minYear
                }

                // If the max year is less than the highest year, decrease the lowest year
                // by the lower of the two: available and needed years. Then set the
                // highest year to the max year.
                if ( maxYear < highestYear ) {

                    var availableYears = lowestYear - minYear,
                        neededYears = highestYear - maxYear

                    lowestYear -= availableYears > neededYears ? neededYears : availableYears
                    highestYear = maxYear
                }

                return _.node( 'select',
                    _.group({
                        min: lowestYear,
                        max: highestYear,
                        i: 1,
                        node: 'option',
                        item: function( loopedYear ) {
                            return [

                                // The looped year and no classes.
                                loopedYear, 0,

                                // Set the value and selected index.
                                'value=' + loopedYear + ( focusedYear == loopedYear ? ' selected' : '' )
                            ]
                        }
                    }),
                    settings.klass.selectYear,
                    ( isOpen ? '' : 'disabled' ) + ' ' + _.ariaAttr({ controls: calendar.$node[0].id + '_table' }) + ' ' +
                    'title="' + settings.labelYearSelect + '"'
                )
            }

            // Otherwise just return the year focused
            return _.node( 'div', focusedYear, settings.klass.year )
        } //createYearLabel


    // Create and return the entire calendar.
    return _.node(
        'div',
        ( settings.selectYears ? createYearLabel() + createMonthLabel() : createMonthLabel() + createYearLabel() ) +
        createMonthNav() + createMonthNav( 1 ),
        settings.klass.header
    ) + _.node(
        'table',
        tableHead +
        _.node(
            'tbody',
            _.group({
                min: 0,
                max: WEEKS_IN_CALENDAR - 1,
                i: 1,
                node: 'tr',
                item: function( rowCounter ) {

                    // If Monday is the first day and the month starts on Sunday, shift the date back a week.
                    var shiftDateBy = settings.firstDay && calendar.create([ viewsetObject.year, viewsetObject.month, 1 ]).day === 0 ? -7 : 0

                    return [
                        _.group({
                            min: DAYS_IN_WEEK * rowCounter - viewsetObject.day + shiftDateBy + 1, // Add 1 for weekday 0index
                            max: function() {
                                return this.min + DAYS_IN_WEEK - 1
                            },
                            i: 1,
                            node: 'td',
                            item: function( targetDate ) {

                                // Convert the time date from a relative date to a target date.
                                targetDate = calendar.create([ viewsetObject.year, viewsetObject.month, targetDate + ( settings.firstDay ? 1 : 0 ) ])

                                var isSelected = selectedObject && selectedObject.pick == targetDate.pick,
                                    isHighlighted = highlightedObject && highlightedObject.pick == targetDate.pick,
                                    isDisabled = disabledCollection && calendar.disabled( targetDate ) || targetDate.pick < minLimitObject.pick || targetDate.pick > maxLimitObject.pick,
                                    formattedDate = _.trigger( calendar.formats.toString, calendar, [ settings.format, targetDate ] )

                                return [
                                    _.node(
                                        'div',
                                        targetDate.date,
                                        (function( klasses ) {

                                            // Add the `infocus` or `outfocus` classes based on month in view.
                                            klasses.push( viewsetObject.month == targetDate.month ? settings.klass.infocus : settings.klass.outfocus )

                                            // Add the `today` class if needed.
                                            if ( nowObject.pick == targetDate.pick ) {
                                                klasses.push( settings.klass.now )
                                            }

                                            // Add the `selected` class if something's selected and the time matches.
                                            if ( isSelected ) {
                                                klasses.push( settings.klass.selected )
                                            }

                                            // Add the `highlighted` class if something's highlighted and the time matches.
                                            if ( isHighlighted ) {
                                                klasses.push( settings.klass.highlighted )
                                            }

                                            // Add the `disabled` class if something's disabled and the object matches.
                                            if ( isDisabled ) {
                                                klasses.push( settings.klass.disabled )
                                            }

                                            return klasses.join( ' ' )
                                        })([ settings.klass.day ]),
                                        'data-pick=' + targetDate.pick + ' ' + _.ariaAttr({
                                            role: 'gridcell',
                                            label: formattedDate,
                                            selected: isSelected && calendar.$node.val() === formattedDate ? true : null,
                                            activedescendant: isHighlighted ? true : null,
                                            disabled: isDisabled ? true : null
                                        })
                                    ),
                                    '',
                                    _.ariaAttr({ role: 'presentation' })
                                ] //endreturn
                            }
                        })
                    ] //endreturn
                }
            })
        ),
        settings.klass.table,
        'id="' + calendar.$node[0].id + '_table' + '" ' + _.ariaAttr({
            role: 'grid',
            controls: calendar.$node[0].id,
            readonly: true
        })
    ) +

    // * For Firefox forms to submit, make sure to set the buttons’ `type` attributes as “button”.
    _.node(
        'div',
        _.node( 'button', settings.today, settings.klass.buttonToday,
            'type=button data-pick=' + nowObject.pick +
            ( isOpen && !calendar.disabled(nowObject) ? '' : ' disabled' ) + ' ' +
            _.ariaAttr({ controls: calendar.$node[0].id }) ) +
        _.node( 'button', settings.clear, settings.klass.buttonClear,
            'type=button data-clear=1' +
            ( isOpen ? '' : ' disabled' ) + ' ' +
            _.ariaAttr({ controls: calendar.$node[0].id }) ) +
        _.node('button', settings.close, settings.klass.buttonClose,
            'type=button data-close=true ' +
            ( isOpen ? '' : ' disabled' ) + ' ' +
            _.ariaAttr({ controls: calendar.$node[0].id }) ),
        settings.klass.footer
    ) //endreturn
} //DatePicker.prototype.nodes




/**
 * The date picker defaults.
 */
DatePicker.defaults = (function( prefix ) {

    return {

        // The title label to use for the month nav buttons
        labelMonthNext: 'Next month',
        labelMonthPrev: 'Previous month',

        // The title label to use for the dropdown selectors
        labelMonthSelect: 'Select a month',
        labelYearSelect: 'Select a year',

        // Months and weekdays
        monthsFull: [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ],
        monthsShort: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ],
        weekdaysFull: [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ],
        weekdaysShort: [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ],

        // Today and clear
        today: 'Today',
        clear: 'Clear',
        close: 'Close',

        // Picker close behavior
        closeOnSelect: true,
        closeOnClear: true,

        // The format to show on the `input` element
        format: 'yyyy-mm-dd',

        // Classes
        klass: {

            table: prefix + 'table',

            header: prefix + 'header',

            navPrev: prefix + 'nav--prev icon icon-chevron-left',
            navNext: prefix + 'nav--next icon icon-chevron-right',
            navDisabled: prefix + 'nav--disabled',

            month: prefix + 'month',
            year: prefix + 'year',

            selectMonth: prefix + 'select--month',
            selectYear: prefix + 'select--year',

            weekdays: prefix + 'weekday',

            day: prefix + 'day',
            disabled: prefix + 'day--disabled',
            selected: prefix + 'day--selected',
            highlighted: prefix + 'day--highlighted',
            now: prefix + 'day--today',
            infocus: prefix + 'day--infocus',
            outfocus: prefix + 'day--outfocus',

            footer: prefix + 'footer',

            buttonClear: prefix + 'button--clear',
            buttonToday: prefix + 'button--today',
            buttonClose: prefix + 'button--close icon icon-close'
        }
    }
})( Picker.klasses().picker + '__' )





/**
 * Extend the picker to add the date picker.
 */
Picker.extend( 'pickadate', DatePicker )


}));




};


modules['helpcenter/primary/views/addemail'] = function(require, exports, module, modules) {
module.exports = Backbone.View.extend({
	el: $('#addemail-container'),

	events: {
		'submit #add-email': 'email',
		'submit #add-password': 'password'
	},

	/**
	 * @author Bishwanath Jha <bishwanath.jha@kayako.com>
	 * @description Handle email submit
	 */
	email: function(e) {
		e.preventDefault();
		var email = $('#modal-email').val();
		var token = $('#modal-token').val();
		$('#duplicateerrormessage').hide();

		if (email.length) {
			$(e.target).find('input[type="submit"]').prop('disabled', true).addClass('button--loading');
			$.ajax({
				url: _Payload.url + 'account/email',
				type: 'POST',
				data: {email: email, token: token},
				success: function(response) {
					if (response.status === 201) {
						window.location.href = _Payload.url;
						return true;
					}
				},
				error: function(response) {
					// If we have received 401 which means AUTHENTICATION_FAILED, Redirect user to login page
					if (response.status === 401) {
						$('#add-email').fadeOut(200);
						$('#add-password').fadeIn(200);
						return false;
					} else if (response.status === 403) {
						var message = response.responseJSON.notifications.error;
						if (message.length) {
							$('.modal__wrap').fadeOut('200', function() {
								$(this).remove();
								var tpl_error = _.template($('#tpl-password-alert').html());
								var tpl_html = tpl_error({message: message});
								$('.alert-box').remove();
								$('.body').prepend(tpl_html);
								setTimeout(function() {
									$('.alert-box').fadeOut('300', function() {
										$(this).remove();
									});;
								}, 3000);
							});
						} else {
							window.location.href = _Payload.url + 'login';
							return false;
						}

					} else if (response.status === 301 || response.status === 454 || response.status === 404) {
						window.location.href = _Payload.url + 'login';
						return false;
					} else {
						// Otherwise there is some error with email
						$('#modal-email').closest('.textfield__label').addClass('textfield__label--error');
						$(e.target).find('input[type="submit"]').prop('disabled', false).removeClass('button--loading');
					}
				}
			});
		} else {
			$('#modal-email').closest('.textfield__label').addClass('textfield__label--error');
		}
	},
	/**
	 * @author Bishwanath Jha <bishwanath.jha@kayako.com>
	 * @description Handle password submit
	 */
	password: function(e) {
		e.preventDefault();
		$(e.target).find('input[type="submit"]').prop('disabled', true).addClass('button--loading');
		var email = $('#modal-email').val();
		var password = $('#modal-password').val();
		var token = $('#modal-token').val();
		$('#duplicateerrormessage').hide();

		if (password == '') {
			$('#modal-password').closest('.textfield__label').addClass('textfield__label--error');
			return false;
		}

		$.ajax({
			url: _Payload.url + 'account/verify',
			type: 'POST',
			data: {email: email, token: token, password: password},
			success: function(response) {
				if (response.status === 201) {
					window.location.href = _Payload.url;
					return true;
				}
			},
			error: function(response) {
				var status = response.status;
				$(e.target).find('input[type="submit"]').prop('disabled', false).removeClass('button--loading');
				if (status === 401 ) {
					var tpl_error = _.template($('#tpl-password-alert').html());
					var tpl_html = tpl_error({message: response.responseJSON.notifications.warning});
					$('.alert-box').remove();
					$('.modal__body').prepend(tpl_html);
				} else if (response.status === 403) {
					var message = response.responseJSON.notifications.error;
					if (message.length) {
						$('.modal__wrap').fadeOut('200', function() {
							$(this).remove();
							var tpl_error = _.template($('#tpl-password-alert').html());
							var tpl_html = tpl_error({message: message});
							$('.alert-box').remove();
							$('.body').prepend(tpl_html);
							setTimeout(function() {
								$('.alert-box').fadeOut('300', function() {
									$(this).remove();
								});;
							}, 3000);
						});
					} else {
						window.location.href = _Payload.url + 'login';
						return false;
					}

				}
				else if (status === 301 || response.status === 454 || status === 404) {
					window.location.href = _Payload.url + 'login';
					return false;
				}
				// Too many requests
				else if (status === 429) {
					$('.modal__wrap').fadeOut('200', function() {
						$(this).remove();
						var tpl_error = _.template($('#tpl-password-alert').html());
						var tpl_html = tpl_error({message: response.responseJSON.notifications.warning});
						$('.alert-box').remove();
						$('.body').prepend(tpl_html);
						setTimeout(function() {
							$('.alert-box').fadeOut('300', function() {
								$(this).remove();
							});;
						}, 3000);
					});
				} else {
					// Otherwise there is some error with password
					$('#modal-password').closest('.textfield__label').addClass('textfield__label--error');
				}
			}
		});
	}
});
};


modules['helpcenter/primary/plugins/ace'] = function(require, exports, module, modules) {
(function(){function o(n){var i=e;n&&(e[n]||(e[n]={}),i=e[n]);if(!i.define||!i.define.packaged)t.original=i.define,i.define=t,i.define.packaged=!0;if(!i.require||!i.require.packaged)r.original=i.require,i.require=r,i.require.packaged=!0}var ACE_NAMESPACE="",e=function(){return this}();!e&&typeof window!="undefined"&&(e=window);if(!ACE_NAMESPACE&&typeof requirejs!="undefined")return;var t=function(e,n,r){if(typeof e!="string"){t.original?t.original.apply(this,arguments):(console.error("dropping module because define wasn't a string."),console.trace());return}arguments.length==2&&(r=n),t.modules[e]||(t.payloads[e]=r,t.modules[e]=null)};t.modules={},t.payloads={};var n=function(e,t,n){if(typeof t=="string"){var i=s(e,t);if(i!=undefined)return n&&n(),i}else if(Object.prototype.toString.call(t)==="[object Array]"){var o=[];for(var u=0,a=t.length;u<a;++u){var f=s(e,t[u]);if(f==undefined&&r.original)return;o.push(f)}return n&&n.apply(null,o)||!0}},r=function(e,t){var i=n("",e,t);return i==undefined&&r.original?r.original.apply(this,arguments):i},i=function(e,t){if(t.indexOf("!")!==-1){var n=t.split("!");return i(e,n[0])+"!"+i(e,n[1])}if(t.charAt(0)=="."){var r=e.split("/").slice(0,-1).join("/");t=r+"/"+t;while(t.indexOf(".")!==-1&&s!=t){var s=t;t=t.replace(/\/\.\//,"/").replace(/[^\/]+\/\.\.\//,"")}}return t},s=function(e,r){r=i(e,r);var s=t.modules[r];if(!s){s=t.payloads[r];if(typeof s=="function"){var o={},u={id:r,uri:"",exports:o,packaged:!0},a=function(e,t){return n(r,e,t)},f=s(a,o,u);o=f||u.exports,t.modules[r]=o,delete t.payloads[r]}s=t.modules[r]=o||s}return s};o(ACE_NAMESPACE)})(),define("ace/lib/regexp",["require","exports","module"],function(e,t,n){"use strict";function o(e){return(e.global?"g":"")+(e.ignoreCase?"i":"")+(e.multiline?"m":"")+(e.extended?"x":"")+(e.sticky?"y":"")}function u(e,t,n){if(Array.prototype.indexOf)return e.indexOf(t,n);for(var r=n||0;r<e.length;r++)if(e[r]===t)return r;return-1}var r={exec:RegExp.prototype.exec,test:RegExp.prototype.test,match:String.prototype.match,replace:String.prototype.replace,split:String.prototype.split},i=r.exec.call(/()??/,"")[1]===undefined,s=function(){var e=/^/g;return r.test.call(e,""),!e.lastIndex}();if(s&&i)return;RegExp.prototype.exec=function(e){var t=r.exec.apply(this,arguments),n,a;if(typeof e=="string"&&t){!i&&t.length>1&&u(t,"")>-1&&(a=RegExp(this.source,r.replace.call(o(this),"g","")),r.replace.call(e.slice(t.index),a,function(){for(var e=1;e<arguments.length-2;e++)arguments[e]===undefined&&(t[e]=undefined)}));if(this._xregexp&&this._xregexp.captureNames)for(var f=1;f<t.length;f++)n=this._xregexp.captureNames[f-1],n&&(t[n]=t[f]);!s&&this.global&&!t[0].length&&this.lastIndex>t.index&&this.lastIndex--}return t},s||(RegExp.prototype.test=function(e){var t=r.exec.call(this,e);return t&&this.global&&!t[0].length&&this.lastIndex>t.index&&this.lastIndex--,!!t})}),define("ace/lib/es5-shim",["require","exports","module"],function(e,t,n){function r(){}function w(e){try{return Object.defineProperty(e,"sentinel",{}),"sentinel"in e}catch(t){}}function H(e){return e=+e,e!==e?e=0:e!==0&&e!==1/0&&e!==-1/0&&(e=(e>0||-1)*Math.floor(Math.abs(e))),e}function B(e){var t=typeof e;return e===null||t==="undefined"||t==="boolean"||t==="number"||t==="string"}function j(e){var t,n,r;if(B(e))return e;n=e.valueOf;if(typeof n=="function"){t=n.call(e);if(B(t))return t}r=e.toString;if(typeof r=="function"){t=r.call(e);if(B(t))return t}throw new TypeError}Function.prototype.bind||(Function.prototype.bind=function(t){var n=this;if(typeof n!="function")throw new TypeError("Function.prototype.bind called on incompatible "+n);var i=u.call(arguments,1),s=function(){if(this instanceof s){var e=n.apply(this,i.concat(u.call(arguments)));return Object(e)===e?e:this}return n.apply(t,i.concat(u.call(arguments)))};return n.prototype&&(r.prototype=n.prototype,s.prototype=new r,r.prototype=null),s});var i=Function.prototype.call,s=Array.prototype,o=Object.prototype,u=s.slice,a=i.bind(o.toString),f=i.bind(o.hasOwnProperty),l,c,h,p,d;if(d=f(o,"__defineGetter__"))l=i.bind(o.__defineGetter__),c=i.bind(o.__defineSetter__),h=i.bind(o.__lookupGetter__),p=i.bind(o.__lookupSetter__);if([1,2].splice(0).length!=2)if(!function(){function e(e){var t=new Array(e+2);return t[0]=t[1]=0,t}var t=[],n;t.splice.apply(t,e(20)),t.splice.apply(t,e(26)),n=t.length,t.splice(5,0,"XXX"),n+1==t.length;if(n+1==t.length)return!0}())Array.prototype.splice=function(e,t){var n=this.length;e>0?e>n&&(e=n):e==void 0?e=0:e<0&&(e=Math.max(n+e,0)),e+t<n||(t=n-e);var r=this.slice(e,e+t),i=u.call(arguments,2),s=i.length;if(e===n)s&&this.push.apply(this,i);else{var o=Math.min(t,n-e),a=e+o,f=a+s-o,l=n-a,c=n-o;if(f<a)for(var h=0;h<l;++h)this[f+h]=this[a+h];else if(f>a)for(h=l;h--;)this[f+h]=this[a+h];if(s&&e===c)this.length=c,this.push.apply(this,i);else{this.length=c+s;for(h=0;h<s;++h)this[e+h]=i[h]}}return r};else{var v=Array.prototype.splice;Array.prototype.splice=function(e,t){return arguments.length?v.apply(this,[e===void 0?0:e,t===void 0?this.length-e:t].concat(u.call(arguments,2))):[]}}Array.isArray||(Array.isArray=function(t){return a(t)=="[object Array]"});var m=Object("a"),g=m[0]!="a"||!(0 in m);Array.prototype.forEach||(Array.prototype.forEach=function(t){var n=F(this),r=g&&a(this)=="[object String]"?this.split(""):n,i=arguments[1],s=-1,o=r.length>>>0;if(a(t)!="[object Function]")throw new TypeError;while(++s<o)s in r&&t.call(i,r[s],s,n)}),Array.prototype.map||(Array.prototype.map=function(t){var n=F(this),r=g&&a(this)=="[object String]"?this.split(""):n,i=r.length>>>0,s=Array(i),o=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var u=0;u<i;u++)u in r&&(s[u]=t.call(o,r[u],u,n));return s}),Array.prototype.filter||(Array.prototype.filter=function(t){var n=F(this),r=g&&a(this)=="[object String]"?this.split(""):n,i=r.length>>>0,s=[],o,u=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var f=0;f<i;f++)f in r&&(o=r[f],t.call(u,o,f,n)&&s.push(o));return s}),Array.prototype.every||(Array.prototype.every=function(t){var n=F(this),r=g&&a(this)=="[object String]"?this.split(""):n,i=r.length>>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o<i;o++)if(o in r&&!t.call(s,r[o],o,n))return!1;return!0}),Array.prototype.some||(Array.prototype.some=function(t){var n=F(this),r=g&&a(this)=="[object String]"?this.split(""):n,i=r.length>>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o<i;o++)if(o in r&&t.call(s,r[o],o,n))return!0;return!1}),Array.prototype.reduce||(Array.prototype.reduce=function(t){var n=F(this),r=g&&a(this)=="[object String]"?this.split(""):n,i=r.length>>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduce of empty array with no initial value");var s=0,o;if(arguments.length>=2)o=arguments[1];else do{if(s in r){o=r[s++];break}if(++s>=i)throw new TypeError("reduce of empty array with no initial value")}while(!0);for(;s<i;s++)s in r&&(o=t.call(void 0,o,r[s],s,n));return o}),Array.prototype.reduceRight||(Array.prototype.reduceRight=function(t){var n=F(this),r=g&&a(this)=="[object String]"?this.split(""):n,i=r.length>>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduceRight of empty array with no initial value");var s,o=i-1;if(arguments.length>=2)s=arguments[1];else do{if(o in r){s=r[o--];break}if(--o<0)throw new TypeError("reduceRight of empty array with no initial value")}while(!0);do o in this&&(s=t.call(void 0,s,r[o],o,n));while(o--);return s});if(!Array.prototype.indexOf||[0,1].indexOf(1,2)!=-1)Array.prototype.indexOf=function(t){var n=g&&a(this)=="[object String]"?this.split(""):F(this),r=n.length>>>0;if(!r)return-1;var i=0;arguments.length>1&&(i=H(arguments[1])),i=i>=0?i:Math.max(0,r+i);for(;i<r;i++)if(i in n&&n[i]===t)return i;return-1};if(!Array.prototype.lastIndexOf||[0,1].lastIndexOf(0,-3)!=-1)Array.prototype.lastIndexOf=function(t){var n=g&&a(this)=="[object String]"?this.split(""):F(this),r=n.length>>>0;if(!r)return-1;var i=r-1;arguments.length>1&&(i=Math.min(i,H(arguments[1]))),i=i>=0?i:r-Math.abs(i);for(;i>=0;i--)if(i in n&&t===n[i])return i;return-1};Object.getPrototypeOf||(Object.getPrototypeOf=function(t){return t.__proto__||(t.constructor?t.constructor.prototype:o)});if(!Object.getOwnPropertyDescriptor){var y="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function(t,n){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(y+t);if(!f(t,n))return;var r,i,s;r={enumerable:!0,configurable:!0};if(d){var u=t.__proto__;t.__proto__=o;var i=h(t,n),s=p(t,n);t.__proto__=u;if(i||s)return i&&(r.get=i),s&&(r.set=s),r}return r.value=t[n],r}}Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(t){return Object.keys(t)});if(!Object.create){var b;Object.prototype.__proto__===null?b=function(){return{__proto__:null}}:b=function(){var e={};for(var t in e)e[t]=null;return e.constructor=e.hasOwnProperty=e.propertyIsEnumerable=e.isPrototypeOf=e.toLocaleString=e.toString=e.valueOf=e.__proto__=null,e},Object.create=function(t,n){var r;if(t===null)r=b();else{if(typeof t!="object")throw new TypeError("typeof prototype["+typeof t+"] != 'object'");var i=function(){};i.prototype=t,r=new i,r.__proto__=t}return n!==void 0&&Object.defineProperties(r,n),r}}if(Object.defineProperty){var E=w({}),S=typeof document=="undefined"||w(document.createElement("div"));if(!E||!S)var x=Object.defineProperty}if(!Object.defineProperty||x){var T="Property description must be an object: ",N="Object.defineProperty called on non-object: ",C="getters & setters can not be defined on this javascript engine";Object.defineProperty=function(t,n,r){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(N+t);if(typeof r!="object"&&typeof r!="function"||r===null)throw new TypeError(T+r);if(x)try{return x.call(Object,t,n,r)}catch(i){}if(f(r,"value"))if(d&&(h(t,n)||p(t,n))){var s=t.__proto__;t.__proto__=o,delete t[n],t[n]=r.value,t.__proto__=s}else t[n]=r.value;else{if(!d)throw new TypeError(C);f(r,"get")&&l(t,n,r.get),f(r,"set")&&c(t,n,r.set)}return t}}Object.defineProperties||(Object.defineProperties=function(t,n){for(var r in n)f(n,r)&&Object.defineProperty(t,r,n[r]);return t}),Object.seal||(Object.seal=function(t){return t}),Object.freeze||(Object.freeze=function(t){return t});try{Object.freeze(function(){})}catch(k){Object.freeze=function(t){return function(n){return typeof n=="function"?n:t(n)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(t){return t}),Object.isSealed||(Object.isSealed=function(t){return!1}),Object.isFrozen||(Object.isFrozen=function(t){return!1}),Object.isExtensible||(Object.isExtensible=function(t){if(Object(t)===t)throw new TypeError;var n="";while(f(t,n))n+="?";t[n]=!0;var r=f(t,n);return delete t[n],r});if(!Object.keys){var L=!0,A=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],O=A.length;for(var M in{toString:null})L=!1;Object.keys=function I(e){if(typeof e!="object"&&typeof e!="function"||e===null)throw new TypeError("Object.keys called on a non-object");var I=[];for(var t in e)f(e,t)&&I.push(t);if(L)for(var n=0,r=O;n<r;n++){var i=A[n];f(e,i)&&I.push(i)}return I}}Date.now||(Date.now=function(){return(new Date).getTime()});var _="	\n\f\r \u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\ufeff";if(!String.prototype.trim||_.trim()){_="["+_+"]";var D=new RegExp("^"+_+_+"*"),P=new RegExp(_+_+"*$");String.prototype.trim=function(){return String(this).replace(D,"").replace(P,"")}}var F=function(e){if(e==null)throw new TypeError("can't convert "+e+" to object");return Object(e)}}),define("ace/lib/fixoldbrowsers",["require","exports","module","ace/lib/regexp","ace/lib/es5-shim"],function(e,t,n){"use strict";e("./regexp"),e("./es5-shim")}),define("ace/lib/dom",["require","exports","module"],function(e,t,n){"use strict";var r="http://www.w3.org/1999/xhtml";t.getDocumentHead=function(e){return e||(e=document),e.head||e.getElementsByTagName("head")[0]||e.documentElement},t.createElement=function(e,t){return document.createElementNS?document.createElementNS(t||r,e):document.createElement(e)},t.hasCssClass=function(e,t){var n=(e.className+"").split(/\s+/g);return n.indexOf(t)!==-1},t.addCssClass=function(e,n){t.hasCssClass(e,n)||(e.className+=" "+n)},t.removeCssClass=function(e,t){var n=e.className.split(/\s+/g);for(;;){var r=n.indexOf(t);if(r==-1)break;n.splice(r,1)}e.className=n.join(" ")},t.toggleCssClass=function(e,t){var n=e.className.split(/\s+/g),r=!0;for(;;){var i=n.indexOf(t);if(i==-1)break;r=!1,n.splice(i,1)}return r&&n.push(t),e.className=n.join(" "),r},t.setCssClass=function(e,n,r){r?t.addCssClass(e,n):t.removeCssClass(e,n)},t.hasCssString=function(e,t){var n=0,r;t=t||document;if(t.createStyleSheet&&(r=t.styleSheets)){while(n<r.length)if(r[n++].owningElement.id===e)return!0}else if(r=t.getElementsByTagName("style"))while(n<r.length)if(r[n++].id===e)return!0;return!1},t.importCssString=function(n,r,i){i=i||document;if(r&&t.hasCssString(r,i))return null;var s;r&&(n+="\n/*# sourceURL=ace/css/"+r+" */"),i.createStyleSheet?(s=i.createStyleSheet(),s.cssText=n,r&&(s.owningElement.id=r)):(s=t.createElement("style"),s.appendChild(i.createTextNode(n)),r&&(s.id=r),t.getDocumentHead(i).appendChild(s))},t.importCssStylsheet=function(e,n){if(n.createStyleSheet)n.createStyleSheet(e);else{var r=t.createElement("link");r.rel="stylesheet",r.href=e,t.getDocumentHead(n).appendChild(r)}},t.getInnerWidth=function(e){return parseInt(t.computedStyle(e,"paddingLeft"),10)+parseInt(t.computedStyle(e,"paddingRight"),10)+e.clientWidth},t.getInnerHeight=function(e){return parseInt(t.computedStyle(e,"paddingTop"),10)+parseInt(t.computedStyle(e,"paddingBottom"),10)+e.clientHeight},t.scrollbarWidth=function(e){var n=t.createElement("ace_inner");n.style.width="100%",n.style.minWidth="0px",n.style.height="200px",n.style.display="block";var r=t.createElement("ace_outer"),i=r.style;i.position="absolute",i.left="-10000px",i.overflow="hidden",i.width="200px",i.minWidth="0px",i.height="150px",i.display="block",r.appendChild(n);var s=e.documentElement;s.appendChild(r);var o=n.offsetWidth;i.overflow="scroll";var u=n.offsetWidth;return o==u&&(u=r.clientWidth),s.removeChild(r),o-u};if(typeof document=="undefined"){t.importCssString=function(){};return}window.pageYOffset!==undefined?(t.getPageScrollTop=function(){return window.pageYOffset},t.getPageScrollLeft=function(){return window.pageXOffset}):(t.getPageScrollTop=function(){return document.body.scrollTop},t.getPageScrollLeft=function(){return document.body.scrollLeft}),window.getComputedStyle?t.computedStyle=function(e,t){return t?(window.getComputedStyle(e,"")||{})[t]||"":window.getComputedStyle(e,"")||{}}:t.computedStyle=function(e,t){return t?e.currentStyle[t]:e.currentStyle},t.setInnerHtml=function(e,t){var n=e.cloneNode(!1);return n.innerHTML=t,e.parentNode.replaceChild(n,e),n},"textContent"in document.documentElement?(t.setInnerText=function(e,t){e.textContent=t},t.getInnerText=function(e){return e.textContent}):(t.setInnerText=function(e,t){e.innerText=t},t.getInnerText=function(e){return e.innerText}),t.getParentWindow=function(e){return e.defaultView||e.parentWindow}}),define("ace/lib/oop",["require","exports","module"],function(e,t,n){"use strict";t.inherits=function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})},t.mixin=function(e,t){for(var n in t)e[n]=t[n];return e},t.implement=function(e,n){t.mixin(e,n)}}),define("ace/lib/keys",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/oop"],function(e,t,n){"use strict";e("./fixoldbrowsers");var r=e("./oop"),i=function(){var e={MODIFIER_KEYS:{16:"Shift",17:"Ctrl",18:"Alt",224:"Meta"},KEY_MODS:{ctrl:1,alt:2,option:2,shift:4,"super":8,meta:8,command:8,cmd:8},FUNCTION_KEYS:{8:"Backspace",9:"Tab",13:"Return",19:"Pause",27:"Esc",32:"Space",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"Left",38:"Up",39:"Right",40:"Down",44:"Print",45:"Insert",46:"Delete",96:"Numpad0",97:"Numpad1",98:"Numpad2",99:"Numpad3",100:"Numpad4",101:"Numpad5",102:"Numpad6",103:"Numpad7",104:"Numpad8",105:"Numpad9","-13":"NumpadEnter",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"Numlock",145:"Scrolllock"},PRINTABLE_KEYS:{32:" ",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",59:";",61:"=",65:"a",66:"b",67:"c",68:"d",69:"e",70:"f",71:"g",72:"h",73:"i",74:"j",75:"k",76:"l",77:"m",78:"n",79:"o",80:"p",81:"q",82:"r",83:"s",84:"t",85:"u",86:"v",87:"w",88:"x",89:"y",90:"z",107:"+",109:"-",110:".",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'",111:"/",106:"*"}},t,n;for(n in e.FUNCTION_KEYS)t=e.FUNCTION_KEYS[n].toLowerCase(),e[t]=parseInt(n,10);for(n in e.PRINTABLE_KEYS)t=e.PRINTABLE_KEYS[n].toLowerCase(),e[t]=parseInt(n,10);return r.mixin(e,e.MODIFIER_KEYS),r.mixin(e,e.PRINTABLE_KEYS),r.mixin(e,e.FUNCTION_KEYS),e.enter=e["return"],e.escape=e.esc,e.del=e["delete"],e[173]="-",function(){var t=["cmd","ctrl","alt","shift"];for(var n=Math.pow(2,t.length);n--;)e.KEY_MODS[n]=t.filter(function(t){return n&e.KEY_MODS[t]}).join("-")+"-"}(),e.KEY_MODS[0]="",e.KEY_MODS[-1]="input-",e}();r.mixin(t,i),t.keyCodeToString=function(e){var t=i[e];return typeof t!="string"&&(t=String.fromCharCode(e)),t.toLowerCase()}}),define("ace/lib/useragent",["require","exports","module"],function(e,t,n){"use strict";t.OS={LINUX:"LINUX",MAC:"MAC",WINDOWS:"WINDOWS"},t.getOS=function(){return t.isMac?t.OS.MAC:t.isLinux?t.OS.LINUX:t.OS.WINDOWS};if(typeof navigator!="object")return;var r=(navigator.platform.match(/mac|win|linux/i)||["other"])[0].toLowerCase(),i=navigator.userAgent;t.isWin=r=="win",t.isMac=r=="mac",t.isLinux=r=="linux",t.isIE=navigator.appName=="Microsoft Internet Explorer"||navigator.appName.indexOf("MSAppHost")>=0?parseFloat((i.match(/(?:MSIE |Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]):parseFloat((i.match(/(?:Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]),t.isOldIE=t.isIE&&t.isIE<9,t.isGecko=t.isMozilla=(window.Controllers||window.controllers)&&window.navigator.product==="Gecko",t.isOldGecko=t.isGecko&&parseInt((i.match(/rv:(\d+)/)||[])[1],10)<4,t.isOpera=window.opera&&Object.prototype.toString.call(window.opera)=="[object Opera]",t.isWebKit=parseFloat(i.split("WebKit/")[1])||undefined,t.isChrome=parseFloat(i.split(" Chrome/")[1])||undefined,t.isAIR=i.indexOf("AdobeAIR")>=0,t.isIPad=i.indexOf("iPad")>=0,t.isChromeOS=i.indexOf(" CrOS ")>=0,t.isIOS=/iPad|iPhone|iPod/.test(i)&&!window.MSStream,t.isIOS&&(t.isMac=!0)}),define("ace/lib/event",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function a(e,t,n){var a=u(t);if(!i.isMac&&s){t.getModifierState&&(t.getModifierState("OS")||t.getModifierState("Win"))&&(a|=8);if(s.altGr){if((3&a)==3)return;s.altGr=0}if(n===18||n===17){var f="location"in t?t.location:t.keyLocation;if(n===17&&f===1)s[n]==1&&(o=t.timeStamp);else if(n===18&&a===3&&f===2){var l=t.timeStamp-o;l<50&&(s.altGr=!0)}}}n in r.MODIFIER_KEYS&&(n=-1),a&8&&n>=91&&n<=93&&(n=-1);if(!a&&n===13){var f="location"in t?t.location:t.keyLocation;if(f===3){e(t,a,-n);if(t.defaultPrevented)return}}if(i.isChromeOS&&a&8){e(t,a,n);if(t.defaultPrevented)return;a&=-9}return!!a||n in r.FUNCTION_KEYS||n in r.PRINTABLE_KEYS?e(t,a,n):!1}function f(){s=Object.create(null)}var r=e("./keys"),i=e("./useragent"),s=null,o=0;t.addListener=function(e,t,n){if(e.addEventListener)return e.addEventListener(t,n,!1);if(e.attachEvent){var r=function(){n.call(e,window.event)};n._wrapper=r,e.attachEvent("on"+t,r)}},t.removeListener=function(e,t,n){if(e.removeEventListener)return e.removeEventListener(t,n,!1);e.detachEvent&&e.detachEvent("on"+t,n._wrapper||n)},t.stopEvent=function(e){return t.stopPropagation(e),t.preventDefault(e),!1},t.stopPropagation=function(e){e.stopPropagation?e.stopPropagation():e.cancelBubble=!0},t.preventDefault=function(e){e.preventDefault?e.preventDefault():e.returnValue=!1},t.getButton=function(e){return e.type=="dblclick"?0:e.type=="contextmenu"||i.isMac&&e.ctrlKey&&!e.altKey&&!e.shiftKey?2:e.preventDefault?e.button:{1:0,2:2,4:1}[e.button]},t.capture=function(e,n,r){function i(e){n&&n(e),r&&r(e),t.removeListener(document,"mousemove",n,!0),t.removeListener(document,"mouseup",i,!0),t.removeListener(document,"dragstart",i,!0)}return t.addListener(document,"mousemove",n,!0),t.addListener(document,"mouseup",i,!0),t.addListener(document,"dragstart",i,!0),i},t.addTouchMoveListener=function(e,n){if("ontouchmove"in e){var r,i;t.addListener(e,"touchstart",function(e){var t=e.changedTouches[0];r=t.clientX,i=t.clientY}),t.addListener(e,"touchmove",function(e){var t=1,s=e.changedTouches[0];e.wheelX=-(s.clientX-r)/t,e.wheelY=-(s.clientY-i)/t,r=s.clientX,i=s.clientY,n(e)})}},t.addMouseWheelListener=function(e,n){"onmousewheel"in e?t.addListener(e,"mousewheel",function(e){var t=8;e.wheelDeltaX!==undefined?(e.wheelX=-e.wheelDeltaX/t,e.wheelY=-e.wheelDeltaY/t):(e.wheelX=0,e.wheelY=-e.wheelDelta/t),n(e)}):"onwheel"in e?t.addListener(e,"wheel",function(e){var t=.35;switch(e.deltaMode){case e.DOM_DELTA_PIXEL:e.wheelX=e.deltaX*t||0,e.wheelY=e.deltaY*t||0;break;case e.DOM_DELTA_LINE:case e.DOM_DELTA_PAGE:e.wheelX=(e.deltaX||0)*5,e.wheelY=(e.deltaY||0)*5}n(e)}):t.addListener(e,"DOMMouseScroll",function(e){e.axis&&e.axis==e.HORIZONTAL_AXIS?(e.wheelX=(e.detail||0)*5,e.wheelY=0):(e.wheelX=0,e.wheelY=(e.detail||0)*5),n(e)})},t.addMultiMouseDownListener=function(e,n,r,s){function c(e){t.getButton(e)!==0?o=0:e.detail>1?(o++,o>4&&(o=1)):o=1;if(i.isIE){var c=Math.abs(e.clientX-u)>5||Math.abs(e.clientY-a)>5;if(!f||c)o=1;f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),o==1&&(u=e.clientX,a=e.clientY)}e._clicks=o,r[s]("mousedown",e);if(o>4)o=0;else if(o>1)return r[s](l[o],e)}function h(e){o=2,f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),r[s]("mousedown",e),r[s](l[o],e)}var o=0,u,a,f,l={2:"dblclick",3:"tripleclick",4:"quadclick"};Array.isArray(e)||(e=[e]),e.forEach(function(e){t.addListener(e,"mousedown",c),i.isOldIE&&t.addListener(e,"dblclick",h)})};var u=!i.isMac||!i.isOpera||"KeyboardEvent"in window?function(e){return 0|(e.ctrlKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.metaKey?8:0)}:function(e){return 0|(e.metaKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.ctrlKey?8:0)};t.getModifierString=function(e){return r.KEY_MODS[u(e)]},t.addCommandKeyListener=function(e,n){var r=t.addListener;if(i.isOldGecko||i.isOpera&&!("KeyboardEvent"in window)){var o=null;r(e,"keydown",function(e){o=e.keyCode}),r(e,"keypress",function(e){return a(n,e,o)})}else{var u=null;r(e,"keydown",function(e){s[e.keyCode]=(s[e.keyCode]||0)+1;var t=a(n,e,e.keyCode);return u=e.defaultPrevented,t}),r(e,"keypress",function(e){u&&(e.ctrlKey||e.altKey||e.shiftKey||e.metaKey)&&(t.stopEvent(e),u=null)}),r(e,"keyup",function(e){s[e.keyCode]=null}),s||(f(),r(window,"focus",f))}};if(typeof window=="object"&&window.postMessage&&!i.isOldIE){var l=1;t.nextTick=function(e,n){n=n||window;var r="zero-timeout-message-"+l;t.addListener(n,"message",function i(s){s.data==r&&(t.stopPropagation(s),t.removeListener(n,"message",i),e())}),n.postMessage(r,"*")}}t.nextFrame=typeof window=="object"&&(window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame||window.oRequestAnimationFrame),t.nextFrame?t.nextFrame=t.nextFrame.bind(window):t.nextFrame=function(e){setTimeout(e,17)}}),define("ace/lib/lang",["require","exports","module"],function(e,t,n){"use strict";t.last=function(e){return e[e.length-1]},t.stringReverse=function(e){return e.split("").reverse().join("")},t.stringRepeat=function(e,t){var n="";while(t>0){t&1&&(n+=e);if(t>>=1)e+=e}return n};var r=/^\s\s*/,i=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(r,"")},t.stringTrimRight=function(e){return e.replace(i,"")},t.copyObject=function(e){var t={};for(var n in e)t[n]=e[n];return t},t.copyArray=function(e){var t=[];for(var n=0,r=e.length;n<r;n++)e[n]&&typeof e[n]=="object"?t[n]=this.copyObject(e[n]):t[n]=e[n];return t},t.deepCopy=function s(e){if(typeof e!="object"||!e)return e;var t;if(Array.isArray(e)){t=[];for(var n=0;n<e.length;n++)t[n]=s(e[n]);return t}if(Object.prototype.toString.call(e)!=="[object Object]")return e;t={};for(var n in e)t[n]=s(e[n]);return t},t.arrayToMap=function(e){var t={};for(var n=0;n<e.length;n++)t[e[n]]=1;return t},t.createMap=function(e){var t=Object.create(null);for(var n in e)t[n]=e[n];return t},t.arrayRemove=function(e,t){for(var n=0;n<=e.length;n++)t===e[n]&&e.splice(n,1)},t.escapeRegExp=function(e){return e.replace(/([.*+?^${}()|[\]\/\\])/g,"\\$1")},t.escapeHTML=function(e){return e.replace(/&/g,"&#38;").replace(/"/g,"&#34;").replace(/'/g,"&#39;").replace(/</g,"&#60;")},t.getMatchOffsets=function(e,t){var n=[];return e.replace(t,function(e){n.push({offset:arguments[arguments.length-2],length:e.length})}),n},t.deferredCall=function(e){var t=null,n=function(){t=null,e()},r=function(e){return r.cancel(),t=setTimeout(n,e||0),r};return r.schedule=r,r.call=function(){return this.cancel(),e(),r},r.cancel=function(){return clearTimeout(t),t=null,r},r.isPending=function(){return t},r},t.delayedCall=function(e,t){var n=null,r=function(){n=null,e()},i=function(e){n==null&&(n=setTimeout(r,e||t))};return i.delay=function(e){n&&clearTimeout(n),n=setTimeout(r,e||t)},i.schedule=i,i.call=function(){this.cancel(),e()},i.cancel=function(){n&&clearTimeout(n),n=null},i.isPending=function(){return n},i}}),define("ace/keyboard/textinput_ios",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/lib/dom","ace/lib/lang","ace/lib/keys"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=e("../lib/dom"),o=e("../lib/lang"),u=e("../lib/keys"),a=u.KEY_MODS,f=i.isChrome<18,l=i.isIE,c=function(e,t){function x(e){if(m)return;m=!0;if(k)t=0,n=e?0:c.value.length-1;else var t=4,n=5;try{c.setSelectionRange(t,n)}catch(r){}m=!1}function T(){if(m)return;c.value=h,i.isWebKit&&S.schedule()}function R(){clearTimeout(q),q=setTimeout(function(){g&&(c.style.cssText=g,g=""),t.renderer.$keepTextAreaAtCursor==null&&(t.renderer.$keepTextAreaAtCursor=!0,t.renderer.$moveTextAreaToCursor())},0)}var n=this,c=s.createElement("textarea");c.className=i.isIOS?"ace_text-input ace_text-input-ios":"ace_text-input",i.isTouchPad&&c.setAttribute("x-palm-disable-auto-cap",!0),c.setAttribute("wrap","off"),c.setAttribute("autocorrect","off"),c.setAttribute("autocapitalize","off"),c.setAttribute("spellcheck",!1),c.style.opacity="0",e.insertBefore(c,e.firstChild);var h="\n aaaa a\n",p=!1,d=!1,v=!1,m=!1,g="",y=!0;try{var b=document.activeElement===c}catch(w){}r.addListener(c,"blur",function(e){t.onBlur(e),b=!1}),r.addListener(c,"focus",function(e){b=!0,t.onFocus(e),x()}),this.focus=function(){if(g)return c.focus();c.style.position="fixed",c.focus()},this.blur=function(){c.blur()},this.isFocused=function(){return b};var E=o.delayedCall(function(){b&&x(y)}),S=o.delayedCall(function(){m||(c.value=h,b&&x())});i.isWebKit||t.addEventListener("changeSelection",function(){t.selection.isEmpty()!=y&&(y=!y,E.schedule())}),T(),b&&t.onFocus();var N=function(e){return e.selectionStart===0&&e.selectionEnd===e.value.length},C=function(e){N(c)?(t.selectAll(),x()):k&&x(t.selection.isEmpty())},k=null;this.setInputHandler=function(e){k=e},this.getInputHandler=function(){return k};var L=!1,A=function(e){if(c.selectionStart===4&&c.selectionEnd===5)return;k&&(e=k(e),k=null),v?(x(),e&&t.onPaste(e),v=!1):e==h.substr(0)&&c.selectionStart===4?L?t.execCommand("del",{source:"ace"}):t.execCommand("backspace",{source:"ace"}):p||(e.substring(0,9)==h&&e.length>h.length?e=e.substr(9):e.substr(0,4)==h.substr(0,4)?e=e.substr(4,e.length-h.length+1):e.charAt(e.length-1)==h.charAt(0)&&(e=e.slice(0,-1)),e!=h.charAt(0)&&e.charAt(e.length-1)==h.charAt(0)&&(e=e.slice(0,-1)),e&&t.onTextInput(e)),p&&(p=!1),L&&(L=!1)},O=function(e){if(m)return;var t=c.value;A(t),T()},M=function(e,t,n){var r=e.clipboardData||window.clipboardData;if(!r||f)return;var i=l||n?"Text":"text/plain";try{return t?r.setData(i,t)!==!1:r.getData(i)}catch(e){if(!n)return M(e,t,!0)}},_=function(e,n){var s=t.getCopyText();if(!s)return r.preventDefault(e);M(e,s)?(i.isIOS&&(d=n,c.value="\n aa"+s+"a a\n",c.setSelectionRange(4,4+s.length),p={value:s}),n?t.onCut():t.onCopy(),i.isIOS||r.preventDefault(e)):(p=!0,c.value=s,c.select(),setTimeout(function(){p=!1,T(),x(),n?t.onCut():t.onCopy()}))},D=function(e){_(e,!0)},P=function(e){_(e,!1)},H=function(e){var n=M(e);typeof n=="string"?(n&&t.onPaste(n,e),i.isIE&&setTimeout(x),r.preventDefault(e)):(c.value="",v=!0)};r.addCommandKeyListener(c,t.onCommandKey.bind(t)),r.addListener(c,"select",C),r.addListener(c,"input",O),r.addListener(c,"cut",D),r.addListener(c,"copy",P),r.addListener(c,"paste",H);var B=function(e){if(m||!t.onCompositionStart||t.$readOnly)return;m={},m.canUndo=t.session.$undoManager,t.onCompositionStart(),setTimeout(j,0),t.on("mousedown",F),m.canUndo&&!t.selection.isEmpty()&&(t.insert(""),t.session.markUndoGroup(),t.selection.clearSelection()),t.session.markUndoGroup()},j=function(){if(!m||!t.onCompositionUpdate||t.$readOnly)return;var e=c.value.replace(/\x01/g,"");if(m.lastValue===e)return;t.onCompositionUpdate(e),m.lastValue&&t.undo(),m.canUndo&&(m.lastValue=e);if(m.lastValue){var n=t.selection.getRange();t.insert(m.lastValue),t.session.markUndoGroup(),m.range=t.selection.getRange(),t.selection.setRange(n),t.selection.clearSelection()}},F=function(e){if(!t.onCompositionEnd||t.$readOnly)return;var n=m;m=!1;var r=setTimeout(function(){r=null;var e=c.value.replace(/\x01/g,"");if(m)return;e==n.lastValue?T():!n.lastValue&&e&&(T(),A(e))});k=function(i){return r&&clearTimeout(r),i=i.replace(/\x01/g,""),i==n.lastValue?"":(n.lastValue&&r&&t.undo(),i)},t.onCompositionEnd(),t.removeListener("mousedown",F),e.type=="compositionend"&&n.range&&t.selection.setRange(n.range);var s=!!i.isChrome&&i.isChrome>=53||!!i.isWebKit&&i.isWebKit>=603;s&&O()},I=o.delayedCall(j,50);r.addListener(c,"compositionstart",B),i.isGecko?r.addListener(c,"text",function(){I.schedule()}):(r.addListener(c,"keyup",function(){I.schedule()}),r.addListener(c,"keydown",function(){I.schedule()})),r.addListener(c,"compositionend",F),this.getElement=function(){return c},this.setReadOnly=function(e){c.readOnly=e},this.onContextMenu=function(e){L=!0,x(t.selection.isEmpty()),t._emit("nativecontextmenu",{target:t,domEvent:e}),this.moveToMouse(e,!0)},this.moveToMouse=function(e,n){g||(g=c.style.cssText),c.style.cssText=(n?"z-index:100000;":"")+"height:"+c.style.height+";"+(i.isIE?"opacity:0.1;":"");var o=t.container.getBoundingClientRect(),u=s.computedStyle(t.container),a=o.top+(parseInt(u.borderTopWidth)||0),f=o.left+(parseInt(o.borderLeftWidth)||0),l=o.bottom-a-c.clientHeight-2,h=function(e){c.style.left=e.clientX-f-2+"px",c.style.top=Math.min(e.clientY-a-2,l)+"px"};h(e);if(e.type!="mousedown")return;t.renderer.$keepTextAreaAtCursor&&(t.renderer.$keepTextAreaAtCursor=null),clearTimeout(q),i.isWin&&r.capture(t.container,h,R)},this.onContextMenuClose=R;var q,U=function(e){t.textInput.onContextMenu(e),R()};r.addListener(c,"mouseup",U),r.addListener(c,"mousedown",function(e){e.preventDefault(),R()}),r.addListener(t.renderer.scroller,"contextmenu",U),r.addListener(c,"contextmenu",U);if(i.isIOS){var z=null,W=!1;e.addEventListener("keydown",function(e){z&&clearTimeout(z),W=!0}),e.addEventListener("keyup",function(e){z=setTimeout(function(){W=!1},100)});var X=function(e){if(document.activeElement!==c)return;if(W)return;if(d)return setTimeout(function(){d=!1},100);var n=c.selectionStart,r=c.selectionEnd;c.setSelectionRange(4,5);if(n==r)switch(n){case 0:t.onCommandKey(null,0,u.up);break;case 1:t.onCommandKey(null,0,u.home);break;case 2:t.onCommandKey(null,a.option,u.left);break;case 4:t.onCommandKey(null,0,u.left);break;case 5:t.onCommandKey(null,0,u.right);break;case 7:t.onCommandKey(null,a.option,u.right);break;case 8:t.onCommandKey(null,0,u.end);break;case 9:t.onCommandKey(null,0,u.down)}else{switch(r){case 6:t.onCommandKey(null,a.shift,u.right);break;case 7:t.onCommandKey(null,a.shift|a.option,u.right);break;case 8:t.onCommandKey(null,a.shift,u.end);break;case 9:t.onCommandKey(null,a.shift,u.down)}switch(n){case 0:t.onCommandKey(null,a.shift,u.up);break;case 1:t.onCommandKey(null,a.shift,u.home);break;case 2:t.onCommandKey(null,a.shift|a.option,u.left);break;case 3:t.onCommandKey(null,a.shift,u.left)}}};document.addEventListener("selectionchange",X),t.on("destroy",function(){document.removeEventListener("selectionchange",X)})}};t.TextInput=c}),define("ace/keyboard/textinput",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/lib/dom","ace/lib/lang","ace/keyboard/textinput_ios"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=e("../lib/dom"),o=e("../lib/lang"),u=i.isChrome<18,a=i.isIE,f=e("./textinput_ios").TextInput,l=function(e,t){function w(e){if(p)return;p=!0;if(T)t=0,r=e?0:n.value.length-1;else var t=e?2:1,r=2;try{n.setSelectionRange(t,r)}catch(i){}p=!1}function E(){if(p)return;n.value=l,i.isWebKit&&b.schedule()}function F(){clearTimeout(j),j=setTimeout(function(){d&&(n.style.cssText=d,d=""),t.renderer.$keepTextAreaAtCursor==null&&(t.renderer.$keepTextAreaAtCursor=!0,t.renderer.$moveTextAreaToCursor())},0)}if(i.isIOS)return f.call(this,e,t);var n=s.createElement("textarea");n.className="ace_text-input",n.setAttribute("wrap","off"),n.setAttribute("autocorrect","off"),n.setAttribute("autocapitalize","off"),n.setAttribute("spellcheck",!1),n.style.opacity="0",e.insertBefore(n,e.firstChild);var l="\u2028\u2028",c=!1,h=!1,p=!1,d="",v=!0;try{var m=document.activeElement===n}catch(g){}r.addListener(n,"blur",function(e){t.onBlur(e),m=!1}),r.addListener(n,"focus",function(e){m=!0,t.onFocus(e),w()}),this.focus=function(){if(d)return n.focus();var e=n.style.top;n.style.position="fixed",n.style.top="0px",n.focus(),setTimeout(function(){n.style.position="",n.style.top=="0px"&&(n.style.top=e)},0)},this.blur=function(){n.blur()},this.isFocused=function(){return m};var y=o.delayedCall(function(){m&&w(v)}),b=o.delayedCall(function(){p||(n.value=l,m&&w())});i.isWebKit||t.addEventListener("changeSelection",function(){t.selection.isEmpty()!=v&&(v=!v,y.schedule())}),E(),m&&t.onFocus();var S=function(e){return e.selectionStart===0&&e.selectionEnd===e.value.length},x=function(e){c?c=!1:S(n)?(t.selectAll(),w()):T&&w(t.selection.isEmpty())},T=null;this.setInputHandler=function(e){T=e},this.getInputHandler=function(){return T};var N=!1,C=function(e){T&&(e=T(e),T=null),h?(w(),e&&t.onPaste(e),h=!1):e==l.charAt(0)?N?t.execCommand("del",{source:"ace"}):t.execCommand("backspace",{source:"ace"}):(e.substring(0,2)==l?e=e.substr(2):e.charAt(0)==l.charAt(0)?e=e.substr(1):e.charAt(e.length-1)==l.charAt(0)&&(e=e.slice(0,-1)),e.charAt(e.length-1)==l.charAt(0)&&(e=e.slice(0,-1)),e&&t.onTextInput(e)),N&&(N=!1)},k=function(e){if(p)return;var t=n.value;C(t),E()},L=function(e,t,n){var r=e.clipboardData||window.clipboardData;if(!r||u)return;var i=a||n?"Text":"text/plain";try{return t?r.setData(i,t)!==!1:r.getData(i)}catch(e){if(!n)return L(e,t,!0)}},A=function(e,i){var s=t.getCopyText();if(!s)return r.preventDefault(e);L(e,s)?(i?t.onCut():t.onCopy(),r.preventDefault(e)):(c=!0,n.value=s,n.select(),setTimeout(function(){c=!1,E(),w(),i?t.onCut():t.onCopy()}))},O=function(e){A(e,!0)},M=function(e){A(e,!1)},_=function(e){var s=L(e);typeof s=="string"?(s&&t.onPaste(s,e),i.isIE&&setTimeout(w),r.preventDefault(e)):(n.value="",h=!0)};r.addCommandKeyListener(n,t.onCommandKey.bind(t)),r.addListener(n,"select",x),r.addListener(n,"input",k),r.addListener(n,"cut",O),r.addListener(n,"copy",M),r.addListener(n,"paste",_),(!("oncut"in n)||!("oncopy"in n)||!("onpaste"in n))&&r.addListener(e,"keydown",function(e){if(i.isMac&&!e.metaKey||!e.ctrlKey)return;switch(e.keyCode){case 67:M(e);break;case 86:_(e);break;case 88:O(e)}});var D=function(e){if(p||!t.onCompositionStart||t.$readOnly)return;p={},p.canUndo=t.session.$undoManager,t.onCompositionStart(),setTimeout(P,0),t.on("mousedown",H),p.canUndo&&!t.selection.isEmpty()&&(t.insert(""),t.session.markUndoGroup(),t.selection.clearSelection()),t.session.markUndoGroup()},P=function(){if(!p||!t.onCompositionUpdate||t.$readOnly)return;var e=n.value.replace(/\u2028/g,"");if(p.lastValue===e)return;t.onCompositionUpdate(e),p.lastValue&&t.undo(),p.canUndo&&(p.lastValue=e);if(p.lastValue){var r=t.selection.getRange();t.insert(p.lastValue),t.session.markUndoGroup(),p.range=t.selection.getRange(),t.selection.setRange(r),t.selection.clearSelection()}},H=function(e){if(!t.onCompositionEnd||t.$readOnly)return;var r=p;p=!1;var s=setTimeout(function(){s=null;var e=n.value.replace(/\u2028/g,"");if(p)return;e==r.lastValue?E():!r.lastValue&&e&&(E(),C(e))});T=function(n){return s&&clearTimeout(s),n=n.replace(/\u2028/g,""),n==r.lastValue?"":(r.lastValue&&s&&t.undo(),n)},t.onCompositionEnd(),t.removeListener("mousedown",H),e.type=="compositionend"&&r.range&&t.selection.setRange(r.range);var o=!!i.isChrome&&i.isChrome>=53||!!i.isWebKit&&i.isWebKit>=603;o&&k()},B=o.delayedCall(P,50);r.addListener(n,"compositionstart",D),i.isGecko?r.addListener(n,"text",function(){B.schedule()}):(r.addListener(n,"keyup",function(){B.schedule()}),r.addListener(n,"keydown",function(){B.schedule()})),r.addListener(n,"compositionend",H),this.getElement=function(){return n},this.setReadOnly=function(e){n.readOnly=e},this.onContextMenu=function(e){N=!0,w(t.selection.isEmpty()),t._emit("nativecontextmenu",{target:t,domEvent:e}),this.moveToMouse(e,!0)},this.moveToMouse=function(e,o){d||(d=n.style.cssText),n.style.cssText=(o?"z-index:100000;":"")+"height:"+n.style.height+";"+(i.isIE?"opacity:0.1;":"");var u=t.container.getBoundingClientRect(),a=s.computedStyle(t.container),f=u.top+(parseInt(a.borderTopWidth)||0),l=u.left+(parseInt(u.borderLeftWidth)||0),c=u.bottom-f-n.clientHeight-2,h=function(e){n.style.left=e.clientX-l-2+"px",n.style.top=Math.min(e.clientY-f-2,c)+"px"};h(e);if(e.type!="mousedown")return;t.renderer.$keepTextAreaAtCursor&&(t.renderer.$keepTextAreaAtCursor=null),clearTimeout(j),i.isWin&&r.capture(t.container,h,F)},this.onContextMenuClose=F;var j,I=function(e){t.textInput.onContextMenu(e),F()};r.addListener(n,"mouseup",I),r.addListener(n,"mousedown",function(e){e.preventDefault(),F()}),r.addListener(t.renderer.scroller,"contextmenu",I),r.addListener(n,"contextmenu",I)};t.TextInput=l}),define("ace/mouse/default_handlers",["require","exports","module","ace/lib/dom","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";function u(e){e.$clickSelection=null;var t=e.editor;t.setDefaultHandler("mousedown",this.onMouseDown.bind(e)),t.setDefaultHandler("dblclick",this.onDoubleClick.bind(e)),t.setDefaultHandler("tripleclick",this.onTripleClick.bind(e)),t.setDefaultHandler("quadclick",this.onQuadClick.bind(e)),t.setDefaultHandler("mousewheel",this.onMouseWheel.bind(e)),t.setDefaultHandler("touchmove",this.onTouchMove.bind(e));var n=["select","startSelect","selectEnd","selectAllEnd","selectByWordsEnd","selectByLinesEnd","dragWait","dragWaitEnd","focusWait"];n.forEach(function(t){e[t]=this[t]},this),e.selectByLines=this.extendSelectionBy.bind(e,"getLineRange"),e.selectByWords=this.extendSelectionBy.bind(e,"getWordRange")}function a(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}function f(e,t){if(e.start.row==e.end.row)var n=2*t.column-e.start.column-e.end.column;else if(e.start.row==e.end.row-1&&!e.start.column&&!e.end.column)var n=t.column-4;else var n=2*t.row-e.start.row-e.end.row;return n<0?{cursor:e.start,anchor:e.end}:{cursor:e.end,anchor:e.start}}var r=e("../lib/dom"),i=e("../lib/event"),s=e("../lib/useragent"),o=0;(function(){this.onMouseDown=function(e){var t=e.inSelection(),n=e.getDocumentPosition();this.mousedownEvent=e;var r=this.editor,i=e.getButton();if(i!==0){var o=r.getSelectionRange(),u=o.isEmpty();r.$blockScrolling++,(u||i==1)&&r.selection.moveToPosition(n),r.$blockScrolling--,i==2&&(r.textInput.onContextMenu(e.domEvent),s.isMozilla||e.preventDefault());return}this.mousedownEvent.time=Date.now();if(t&&!r.isFocused()){r.focus();if(this.$focusTimout&&!this.$clickSelection&&!r.inMultiSelectMode){this.setState("focusWait"),this.captureMouse(e);return}}return this.captureMouse(e),this.startSelect(n,e.domEvent._clicks>1),e.preventDefault()},this.startSelect=function(e,t){e=e||this.editor.renderer.screenToTextCoordinates(this.x,this.y);var n=this.editor;n.$blockScrolling++,this.mousedownEvent.getShiftKey()?n.selection.selectToPosition(e):t||n.selection.moveToPosition(e),t||this.select(),n.renderer.scroller.setCapture&&n.renderer.scroller.setCapture(),n.setStyle("ace_selecting"),this.setState("select"),n.$blockScrolling--},this.select=function(){var e,t=this.editor,n=t.renderer.screenToTextCoordinates(this.x,this.y);t.$blockScrolling++;if(this.$clickSelection){var r=this.$clickSelection.comparePoint(n);if(r==-1)e=this.$clickSelection.end;else if(r==1)e=this.$clickSelection.start;else{var i=f(this.$clickSelection,n);n=i.cursor,e=i.anchor}t.selection.setSelectionAnchor(e.row,e.column)}t.selection.selectToPosition(n),t.$blockScrolling--,t.renderer.scrollCursorIntoView()},this.extendSelectionBy=function(e){var t,n=this.editor,r=n.renderer.screenToTextCoordinates(this.x,this.y),i=n.selection[e](r.row,r.column);n.$blockScrolling++;if(this.$clickSelection){var s=this.$clickSelection.comparePoint(i.start),o=this.$clickSelection.comparePoint(i.end);if(s==-1&&o<=0){t=this.$clickSelection.end;if(i.end.row!=r.row||i.end.column!=r.column)r=i.start}else if(o==1&&s>=0){t=this.$clickSelection.start;if(i.start.row!=r.row||i.start.column!=r.column)r=i.end}else if(s==-1&&o==1)r=i.end,t=i.start;else{var u=f(this.$clickSelection,r);r=u.cursor,t=u.anchor}n.selection.setSelectionAnchor(t.row,t.column)}n.selection.selectToPosition(r),n.$blockScrolling--,n.renderer.scrollCursorIntoView()},this.selectEnd=this.selectAllEnd=this.selectByWordsEnd=this.selectByLinesEnd=function(){this.$clickSelection=null,this.editor.unsetStyle("ace_selecting"),this.editor.renderer.scroller.releaseCapture&&this.editor.renderer.scroller.releaseCapture()},this.focusWait=function(){var e=a(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y),t=Date.now();(e>o||t-this.mousedownEvent.time>this.$focusTimout)&&this.startSelect(this.mousedownEvent.getDocumentPosition())},this.onDoubleClick=function(e){var t=e.getDocumentPosition(),n=this.editor,r=n.session,i=r.getBracketRange(t);i?(i.isEmpty()&&(i.start.column--,i.end.column++),this.setState("select")):(i=n.selection.getWordRange(t.row,t.column),this.setState("selectByWords")),this.$clickSelection=i,this.select()},this.onTripleClick=function(e){var t=e.getDocumentPosition(),n=this.editor;this.setState("selectByLines");var r=n.getSelectionRange();r.isMultiLine()&&r.contains(t.row,t.column)?(this.$clickSelection=n.selection.getLineRange(r.start.row),this.$clickSelection.end=n.selection.getLineRange(r.end.row).end):this.$clickSelection=n.selection.getLineRange(t.row),this.select()},this.onQuadClick=function(e){var t=this.editor;t.selectAll(),this.$clickSelection=t.getSelectionRange(),this.setState("selectAll")},this.onMouseWheel=function(e){if(e.getAccelKey())return;e.getShiftKey()&&e.wheelY&&!e.wheelX&&(e.wheelX=e.wheelY,e.wheelY=0);var t=e.domEvent.timeStamp,n=t-(this.$lastScrollTime||0),r=this.editor,i=r.renderer.isScrollableBy(e.wheelX*e.speed,e.wheelY*e.speed);if(i||n<200)return this.$lastScrollTime=t,r.renderer.scrollBy(e.wheelX*e.speed,e.wheelY*e.speed),e.stop()},this.onTouchMove=function(e){var t=e.domEvent.timeStamp,n=t-(this.$lastScrollTime||0),r=this.editor,i=r.renderer.isScrollableBy(e.wheelX*e.speed,e.wheelY*e.speed);if(i||n<200)return this.$lastScrollTime=t,r.renderer.scrollBy(e.wheelX*e.speed,e.wheelY*e.speed),e.stop()}}).call(u.prototype),t.DefaultHandlers=u}),define("ace/tooltip",["require","exports","module","ace/lib/oop","ace/lib/dom"],function(e,t,n){"use strict";function s(e){this.isOpen=!1,this.$element=null,this.$parentNode=e}var r=e("./lib/oop"),i=e("./lib/dom");(function(){this.$init=function(){return this.$element=i.createElement("div"),this.$element.className="ace_tooltip",this.$element.style.display="none",this.$parentNode.appendChild(this.$element),this.$element},this.getElement=function(){return this.$element||this.$init()},this.setText=function(e){i.setInnerText(this.getElement(),e)},this.setHtml=function(e){this.getElement().innerHTML=e},this.setPosition=function(e,t){this.getElement().style.left=e+"px",this.getElement().style.top=t+"px"},this.setClassName=function(e){i.addCssClass(this.getElement(),e)},this.show=function(e,t,n){e!=null&&this.setText(e),t!=null&&n!=null&&this.setPosition(t,n),this.isOpen||(this.getElement().style.display="block",this.isOpen=!0)},this.hide=function(){this.isOpen&&(this.getElement().style.display="none",this.isOpen=!1)},this.getHeight=function(){return this.getElement().offsetHeight},this.getWidth=function(){return this.getElement().offsetWidth},this.destroy=function(){this.isOpen=!1,this.$element&&this.$element.parentNode&&this.$element.parentNode.removeChild(this.$element)}}).call(s.prototype),t.Tooltip=s}),define("ace/mouse/default_gutter_handler",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/event","ace/tooltip"],function(e,t,n){"use strict";function u(e){function l(){var r=u.getDocumentPosition().row,s=n.$annotations[r];if(!s)return c();var o=t.session.getLength();if(r==o){var a=t.renderer.pixelToScreenCoordinates(0,u.y).row,l=u.$pos;if(a>t.session.documentToScreenRow(l.row,l.column))return c()}if(f==s)return;f=s.text.join("<br/>"),i.setHtml(f),i.show(),t._signal("showGutterTooltip",i),t.on("mousewheel",c);if(e.$tooltipFollowsMouse)h(u);else{var p=u.domEvent.target,d=p.getBoundingClientRect(),v=i.getElement().style;v.left=d.right+"px",v.top=d.bottom+"px"}}function c(){o&&(o=clearTimeout(o)),f&&(i.hide(),f=null,t._signal("hideGutterTooltip",i),t.removeEventListener("mousewheel",c))}function h(e){i.setPosition(e.x,e.y)}var t=e.editor,n=t.renderer.$gutterLayer,i=new a(t.container);e.editor.setDefaultHandler("guttermousedown",function(r){if(!t.isFocused()||r.getButton()!=0)return;var i=n.getRegion(r);if(i=="foldWidgets")return;var s=r.getDocumentPosition().row,o=t.session.selection;if(r.getShiftKey())o.selectTo(s,0);else{if(r.domEvent.detail==2)return t.selectAll(),r.preventDefault();e.$clickSelection=t.selection.getLineRange(s)}return e.setState("selectByLines"),e.captureMouse(r),r.preventDefault()});var o,u,f;e.editor.setDefaultHandler("guttermousemove",function(t){var n=t.domEvent.target||t.domEvent.srcElement;if(r.hasCssClass(n,"ace_fold-widget"))return c();f&&e.$tooltipFollowsMouse&&h(t),u=t;if(o)return;o=setTimeout(function(){o=null,u&&!e.isMousePressed?l():c()},50)}),s.addListener(t.renderer.$gutter,"mouseout",function(e){u=null;if(!f||o)return;o=setTimeout(function(){o=null,c()},50)}),t.on("changeSession",c)}function a(e){o.call(this,e)}var r=e("../lib/dom"),i=e("../lib/oop"),s=e("../lib/event"),o=e("../tooltip").Tooltip;i.inherits(a,o),function(){this.setPosition=function(e,t){var n=window.innerWidth||document.documentElement.clientWidth,r=window.innerHeight||document.documentElement.clientHeight,i=this.getWidth(),s=this.getHeight();e+=15,t+=15,e+i>n&&(e-=e+i-n),t+s>r&&(t-=20+s),o.prototype.setPosition.call(this,e,t)}}.call(a.prototype),t.GutterHandler=u}),define("ace/mouse/mouse_event",["require","exports","module","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=t.MouseEvent=function(e,t){this.domEvent=e,this.editor=t,this.x=this.clientX=e.clientX,this.y=this.clientY=e.clientY,this.$pos=null,this.$inSelection=null,this.propagationStopped=!1,this.defaultPrevented=!1};(function(){this.stopPropagation=function(){r.stopPropagation(this.domEvent),this.propagationStopped=!0},this.preventDefault=function(){r.preventDefault(this.domEvent),this.defaultPrevented=!0},this.stop=function(){this.stopPropagation(),this.preventDefault()},this.getDocumentPosition=function(){return this.$pos?this.$pos:(this.$pos=this.editor.renderer.screenToTextCoordinates(this.clientX,this.clientY),this.$pos)},this.inSelection=function(){if(this.$inSelection!==null)return this.$inSelection;var e=this.editor,t=e.getSelectionRange();if(t.isEmpty())this.$inSelection=!1;else{var n=this.getDocumentPosition();this.$inSelection=t.contains(n.row,n.column)}return this.$inSelection},this.getButton=function(){return r.getButton(this.domEvent)},this.getShiftKey=function(){return this.domEvent.shiftKey},this.getAccelKey=i.isMac?function(){return this.domEvent.metaKey}:function(){return this.domEvent.ctrlKey}}).call(s.prototype)}),define("ace/mouse/dragdrop_handler",["require","exports","module","ace/lib/dom","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";function f(e){function T(e,n){var r=Date.now(),i=!n||e.row!=n.row,s=!n||e.column!=n.column;if(!S||i||s)t.$blockScrolling+=1,t.moveCursorToPosition(e),t.$blockScrolling-=1,S=r,x={x:p,y:d};else{var o=l(x.x,x.y,p,d);o>a?S=null:r-S>=u&&(t.renderer.scrollCursorIntoView(),S=null)}}function N(e,n){var r=Date.now(),i=t.renderer.layerConfig.lineHeight,s=t.renderer.layerConfig.characterWidth,u=t.renderer.scroller.getBoundingClientRect(),a={x:{left:p-u.left,right:u.right-p},y:{top:d-u.top,bottom:u.bottom-d}},f=Math.min(a.x.left,a.x.right),l=Math.min(a.y.top,a.y.bottom),c={row:e.row,column:e.column};f/s<=2&&(c.column+=a.x.left<a.x.right?-3:2),l/i<=1&&(c.row+=a.y.top<a.y.bottom?-1:1);var h=e.row!=c.row,v=e.column!=c.column,m=!n||e.row!=n.row;h||v&&!m?E?r-E>=o&&t.renderer.scrollCursorIntoView(c):E=r:E=null}function C(){var e=g;g=t.renderer.screenToTextCoordinates(p,d),T(g,e),N(g,e)}function k(){m=t.selection.toOrientedRange(),h=t.session.addMarker(m,"ace_selection",t.getSelectionStyle()),t.clearSelection(),t.isFocused()&&t.renderer.$cursorLayer.setBlinking(!1),clearInterval(v),C(),v=setInterval(C,20),y=0,i.addListener(document,"mousemove",O)}function L(){clearInterval(v),t.session.removeMarker(h),h=null,t.$blockScrolling+=1,t.selection.fromOrientedRange(m),t.$blockScrolling-=1,t.isFocused()&&!w&&t.renderer.$cursorLayer.setBlinking(!t.getReadOnly()),m=null,g=null,y=0,E=null,S=null,i.removeListener(document,"mousemove",O)}function O(){A==null&&(A=setTimeout(function(){A!=null&&h&&L()},20))}function M(e){var t=e.types;return!t||Array.prototype.some.call(t,function(e){return e=="text/plain"||e=="Text"})}function _(e){var t=["copy","copymove","all","uninitialized"],n=["move","copymove","linkmove","all","uninitialized"],r=s.isMac?e.altKey:e.ctrlKey,i="uninitialized";try{i=e.dataTransfer.effectAllowed.toLowerCase()}catch(e){}var o="none";return r&&t.indexOf(i)>=0?o="copy":n.indexOf(i)>=0?o="move":t.indexOf(i)>=0&&(o="copy"),o}var t=e.editor,n=r.createElement("img");n.src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",s.isOpera&&(n.style.cssText="width:1px;height:1px;position:fixed;top:0;left:0;z-index:2147483647;opacity:0;");var f=["dragWait","dragWaitEnd","startDrag","dragReadyEnd","onMouseDrag"];f.forEach(function(t){e[t]=this[t]},this),t.addEventListener("mousedown",this.onMouseDown.bind(e));var c=t.container,h,p,d,v,m,g,y=0,b,w,E,S,x;this.onDragStart=function(e){if(this.cancelDrag||!c.draggable){var r=this;return setTimeout(function(){r.startSelect(),r.captureMouse(e)},0),e.preventDefault()}m=t.getSelectionRange();var i=e.dataTransfer;i.effectAllowed=t.getReadOnly()?"copy":"copyMove",s.isOpera&&(t.container.appendChild(n),n.scrollTop=0),i.setDragImage&&i.setDragImage(n,0,0),s.isOpera&&t.container.removeChild(n),i.clearData(),i.setData("Text",t.session.getTextRange()),w=!0,this.setState("drag")},this.onDragEnd=function(e){c.draggable=!1,w=!1,this.setState(null);if(!t.getReadOnly()){var n=e.dataTransfer.dropEffect;!b&&n=="move"&&t.session.remove(t.getSelectionRange()),t.renderer.$cursorLayer.setBlinking(!0)}this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle("")},this.onDragEnter=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||k(),y++,e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragOver=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||(k(),y++),A!==null&&(A=null),e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragLeave=function(e){y--;if(y<=0&&h)return L(),b=null,i.preventDefault(e)},this.onDrop=function(e){if(!g)return;var n=e.dataTransfer;if(w)switch(b){case"move":m.contains(g.row,g.column)?m={start:g,end:g}:m=t.moveText(m,g);break;case"copy":m=t.moveText(m,g,!0)}else{var r=n.getData("Text");m={start:g,end:t.session.insert(g,r)},t.focus(),b=null}return L(),i.preventDefault(e)},i.addListener(c,"dragstart",this.onDragStart.bind(e)),i.addListener(c,"dragend",this.onDragEnd.bind(e)),i.addListener(c,"dragenter",this.onDragEnter.bind(e)),i.addListener(c,"dragover",this.onDragOver.bind(e)),i.addListener(c,"dragleave",this.onDragLeave.bind(e)),i.addListener(c,"drop",this.onDrop.bind(e));var A=null}function l(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}var r=e("../lib/dom"),i=e("../lib/event"),s=e("../lib/useragent"),o=200,u=200,a=5;(function(){this.dragWait=function(){var e=Date.now()-this.mousedownEvent.time;e>this.editor.getDragDelay()&&this.startDrag()},this.dragWaitEnd=function(){var e=this.editor.container;e.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()),this.selectEnd()},this.dragReadyEnd=function(e){this.editor.renderer.$cursorLayer.setBlinking(!this.editor.getReadOnly()),this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle(""),this.dragWaitEnd()},this.startDrag=function(){this.cancelDrag=!1;var e=this.editor,t=e.container;t.draggable=!0,e.renderer.$cursorLayer.setBlinking(!1),e.setStyle("ace_dragging");var n=s.isWin?"default":"move";e.renderer.setCursorStyle(n),this.setState("dragReady")},this.onMouseDrag=function(e){var t=this.editor.container;if(s.isIE&&this.state=="dragReady"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>3&&t.dragDrop()}if(this.state==="dragWait"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>0&&(t.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()))}},this.onMouseDown=function(e){if(!this.$dragEnabled)return;this.mousedownEvent=e;var t=this.editor,n=e.inSelection(),r=e.getButton(),i=e.domEvent.detail||1;if(i===1&&r===0&&n){if(e.editor.inMultiSelectMode&&(e.getAccelKey()||e.getShiftKey()))return;this.mousedownEvent.time=Date.now();var o=e.domEvent.target||e.domEvent.srcElement;"unselectable"in o&&(o.unselectable="on");if(t.getDragDelay()){if(s.isWebKit){this.cancelDrag=!0;var u=t.container;u.draggable=!0}this.setState("dragWait")}else this.startDrag();this.captureMouse(e,this.onMouseDrag.bind(this)),e.defaultPrevented=!0}}}).call(f.prototype),t.DragdropHandler=f}),define("ace/lib/net",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("./dom");t.get=function(e,t){var n=new XMLHttpRequest;n.open("GET",e,!0),n.onreadystatechange=function(){n.readyState===4&&t(n.responseText)},n.send(null)},t.loadScript=function(e,t){var n=r.getDocumentHead(),i=document.createElement("script");i.src=e,n.appendChild(i),i.onload=i.onreadystatechange=function(e,n){if(n||!i.readyState||i.readyState=="loaded"||i.readyState=="complete")i=i.onload=i.onreadystatechange=null,n||t()}},t.qualifyURL=function(e){var t=document.createElement("a");return t.href=e,t.href}}),define("ace/lib/event_emitter",["require","exports","module"],function(e,t,n){"use strict";var r={},i=function(){this.propagationStopped=!0},s=function(){this.defaultPrevented=!0};r._emit=r._dispatchEvent=function(e,t){this._eventRegistry||(this._eventRegistry={}),this._defaultHandlers||(this._defaultHandlers={});var n=this._eventRegistry[e]||[],r=this._defaultHandlers[e];if(!n.length&&!r)return;if(typeof t!="object"||!t)t={};t.type||(t.type=e),t.stopPropagation||(t.stopPropagation=i),t.preventDefault||(t.preventDefault=s),n=n.slice();for(var o=0;o<n.length;o++){n[o](t,this);if(t.propagationStopped)break}if(r&&!t.defaultPrevented)return r(t,this)},r._signal=function(e,t){var n=(this._eventRegistry||{})[e];if(!n)return;n=n.slice();for(var r=0;r<n.length;r++)n[r](t,this)},r.once=function(e,t){var n=this;t&&this.addEventListener(e,function r(){n.removeEventListener(e,r),t.apply(null,arguments)})},r.setDefaultHandler=function(e,t){var n=this._defaultHandlers;n||(n=this._defaultHandlers={_disabled_:{}});if(n[e]){var r=n[e],i=n._disabled_[e];i||(n._disabled_[e]=i=[]),i.push(r);var s=i.indexOf(t);s!=-1&&i.splice(s,1)}n[e]=t},r.removeDefaultHandler=function(e,t){var n=this._defaultHandlers;if(!n)return;var r=n._disabled_[e];if(n[e]==t){var i=n[e];r&&this.setDefaultHandler(e,r.pop())}else if(r){var s=r.indexOf(t);s!=-1&&r.splice(s,1)}},r.on=r.addEventListener=function(e,t,n){this._eventRegistry=this._eventRegistry||{};var r=this._eventRegistry[e];return r||(r=this._eventRegistry[e]=[]),r.indexOf(t)==-1&&r[n?"unshift":"push"](t),t},r.off=r.removeListener=r.removeEventListener=function(e,t){this._eventRegistry=this._eventRegistry||{};var n=this._eventRegistry[e];if(!n)return;var r=n.indexOf(t);r!==-1&&n.splice(r,1)},r.removeAllListeners=function(e){this._eventRegistry&&(this._eventRegistry[e]=[])},t.EventEmitter=r}),define("ace/lib/app_config",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(e,t,n){"no use strict";function o(e){typeof console!="undefined"&&console.warn&&console.warn.apply(console,arguments)}function u(e,t){var n=new Error(e);n.data=t,typeof console=="object"&&console.error&&console.error(n),setTimeout(function(){throw n})}var r=e("./oop"),i=e("./event_emitter").EventEmitter,s={setOptions:function(e){Object.keys(e).forEach(function(t){this.setOption(t,e[t])},this)},getOptions:function(e){var t={};return e?Array.isArray(e)||(t=e,e=Object.keys(t)):e=Object.keys(this.$options),e.forEach(function(e){t[e]=this.getOption(e)},this),t},setOption:function(e,t){if(this["$"+e]===t)return;var n=this.$options[e];if(!n)return o('misspelled option "'+e+'"');if(n.forwardTo)return this[n.forwardTo]&&this[n.forwardTo].setOption(e,t);n.handlesSet||(this["$"+e]=t),n&&n.set&&n.set.call(this,t)},getOption:function(e){var t=this.$options[e];return t?t.forwardTo?this[t.forwardTo]&&this[t.forwardTo].getOption(e):t&&t.get?t.get.call(this):this["$"+e]:o('misspelled option "'+e+'"')}},a=function(){this.$defaultOptions={}};(function(){r.implement(this,i),this.defineOptions=function(e,t,n){return e.$options||(this.$defaultOptions[t]=e.$options={}),Object.keys(n).forEach(function(t){var r=n[t];typeof r=="string"&&(r={forwardTo:r}),r.name||(r.name=t),e.$options[r.name]=r,"initialValue"in r&&(e["$"+r.name]=r.initialValue)}),r.implement(e,s),this},this.resetOptions=function(e){Object.keys(e.$options).forEach(function(t){var n=e.$options[t];"value"in n&&e.setOption(t,n.value)})},this.setDefaultValue=function(e,t,n){var r=this.$defaultOptions[e]||(this.$defaultOptions[e]={});r[t]&&(r.forwardTo?this.setDefaultValue(r.forwardTo,t,n):r[t].value=n)},this.setDefaultValues=function(e,t){Object.keys(t).forEach(function(n){this.setDefaultValue(e,n,t[n])},this)},this.warn=o,this.reportError=u}).call(a.prototype),t.AppConfig=a}),define("ace/config",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/lib/net","ace/lib/app_config"],function(e,t,n){"no use strict";function f(r){if(!u||!u.document)return;a.packaged=r||e.packaged||n.packaged||u.define&&define.packaged;var i={},s="",o=document.currentScript||document._currentScript,f=o&&o.ownerDocument||document,c=f.getElementsByTagName("script");for(var h=0;h<c.length;h++){var p=c[h],d=p.src||p.getAttribute("src");if(!d)continue;var v=p.attributes;for(var m=0,g=v.length;m<g;m++){var y=v[m];y.name.indexOf("data-ace-")===0&&(i[l(y.name.replace(/^data-ace-/,""))]=y.value)}var b=d.match(/^(.*)\/ace(\-\w+)?\.js(\?|$)/);b&&(s=b[1])}s&&(i.base=i.base||s,i.packaged=!0),i.basePath=i.base,i.workerPath=i.workerPath||i.base,i.modePath=i.modePath||i.base,i.themePath=i.themePath||i.base,delete i.base;for(var w in i)typeof i[w]!="undefined"&&t.set(w,i[w])}function l(e){return e.replace(/-(.)/g,function(e,t){return t.toUpperCase()})}var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./lib/net"),o=e("./lib/app_config").AppConfig;n.exports=t=new o;var u=function(){return this||typeof window!="undefined"&&window}(),a={packaged:!1,workerPath:null,modePath:null,themePath:null,basePath:"",suffix:".js",$moduleUrls:{}};t.get=function(e){if(!a.hasOwnProperty(e))throw new Error("Unknown config key: "+e);return a[e]},t.set=function(e,t){if(!a.hasOwnProperty(e))throw new Error("Unknown config key: "+e);a[e]=t},t.all=function(){return r.copyObject(a)},t.moduleUrl=function(e,t){if(a.$moduleUrls[e])return a.$moduleUrls[e];var n=e.split("/");t=t||n[n.length-2]||"";var r=t=="snippets"?"/":"-",i=n[n.length-1];if(t=="worker"&&r=="-"){var s=new RegExp("^"+t+"[\\-_]|[\\-_]"+t+"$","g");i=i.replace(s,"")}(!i||i==t)&&n.length>1&&(i=n[n.length-2]);var o=a[t+"Path"];return o==null?o=a.basePath:r=="/"&&(t=r=""),o&&o.slice(-1)!="/"&&(o+="/"),o+t+r+i+this.get("suffix")},t.setModuleUrl=function(e,t){return a.$moduleUrls[e]=t},t.$loading={},t.loadModule=function(n,r){var i,o;Array.isArray(n)&&(o=n[0],n=n[1]);try{i=e(n)}catch(u){}if(i&&!t.$loading[n])return r&&r(i);t.$loading[n]||(t.$loading[n]=[]),t.$loading[n].push(r);if(t.$loading[n].length>1)return;var a=function(){e([n],function(e){t._emit("load.module",{name:n,module:e});var r=t.$loading[n];t.$loading[n]=null,r.forEach(function(t){t&&t(e)})})};if(!t.get("packaged"))return a();s.loadScript(t.moduleUrl(n,o),a)},t.init=f}),define("ace/mouse/mouse_handler",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/mouse/default_handlers","ace/mouse/default_gutter_handler","ace/mouse/mouse_event","ace/mouse/dragdrop_handler","ace/config"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=e("./default_handlers").DefaultHandlers,o=e("./default_gutter_handler").GutterHandler,u=e("./mouse_event").MouseEvent,a=e("./dragdrop_handler").DragdropHandler,f=e("../config"),l=function(e){var t=this;this.editor=e,new s(this),new o(this),new a(this);var n=function(t){var n=!document.hasFocus||!document.hasFocus()||!e.isFocused()&&document.activeElement==(e.textInput&&e.textInput.getElement());n&&window.focus(),e.focus()},u=e.renderer.getMouseEventTarget();r.addListener(u,"click",this.onMouseEvent.bind(this,"click")),r.addListener(u,"mousemove",this.onMouseMove.bind(this,"mousemove")),r.addMultiMouseDownListener([u,e.renderer.scrollBarV&&e.renderer.scrollBarV.inner,e.renderer.scrollBarH&&e.renderer.scrollBarH.inner,e.textInput&&e.textInput.getElement()].filter(Boolean),[400,300,250],this,"onMouseEvent"),r.addMouseWheelListener(e.container,this.onMouseWheel.bind(this,"mousewheel")),r.addTouchMoveListener(e.container,this.onTouchMove.bind(this,"touchmove"));var f=e.renderer.$gutter;r.addListener(f,"mousedown",this.onMouseEvent.bind(this,"guttermousedown")),r.addListener(f,"click",this.onMouseEvent.bind(this,"gutterclick")),r.addListener(f,"dblclick",this.onMouseEvent.bind(this,"gutterdblclick")),r.addListener(f,"mousemove",this.onMouseEvent.bind(this,"guttermousemove")),r.addListener(u,"mousedown",n),r.addListener(f,"mousedown",n),i.isIE&&e.renderer.scrollBarV&&(r.addListener(e.renderer.scrollBarV.element,"mousedown",n),r.addListener(e.renderer.scrollBarH.element,"mousedown",n)),e.on("mousemove",function(n){if(t.state||t.$dragDelay||!t.$dragEnabled)return;var r=e.renderer.screenToTextCoordinates(n.x,n.y),i=e.session.selection.getRange(),s=e.renderer;!i.isEmpty()&&i.insideStart(r.row,r.column)?s.setCursorStyle("default"):s.setCursorStyle("")})};(function(){this.onMouseEvent=function(e,t){this.editor._emit(e,new u(t,this.editor))},this.onMouseMove=function(e,t){var n=this.editor._eventRegistry&&this.editor._eventRegistry.mousemove;if(!n||!n.length)return;this.editor._emit(e,new u(t,this.editor))},this.onMouseWheel=function(e,t){var n=new u(t,this.editor);n.speed=this.$scrollSpeed*2,n.wheelX=t.wheelX,n.wheelY=t.wheelY,this.editor._emit(e,n)},this.onTouchMove=function(e,t){var n=new u(t,this.editor);n.speed=1,n.wheelX=t.wheelX,n.wheelY=t.wheelY,this.editor._emit(e,n)},this.setState=function(e){this.state=e},this.captureMouse=function(e,t){this.x=e.x,this.y=e.y,this.isMousePressed=!0;var n=this.editor.renderer;n.$keepTextAreaAtCursor&&(n.$keepTextAreaAtCursor=null);var s=this,o=function(e){if(!e)return;if(i.isWebKit&&!e.which&&s.releaseMouse)return s.releaseMouse();s.x=e.clientX,s.y=e.clientY,t&&t(e),s.mouseEvent=new u(e,s.editor),s.$mouseMoved=!0},a=function(e){clearInterval(l),f(),s[s.state+"End"]&&s[s.state+"End"](e),s.state="",n.$keepTextAreaAtCursor==null&&(n.$keepTextAreaAtCursor=!0,n.$moveTextAreaToCursor()),s.isMousePressed=!1,s.$onCaptureMouseMove=s.releaseMouse=null,e&&s.onMouseEvent("mouseup",e)},f=function(){s[s.state]&&s[s.state](),s.$mouseMoved=!1};if(i.isOldIE&&e.domEvent.type=="dblclick")return setTimeout(function(){a(e)});s.$onCaptureMouseMove=o,s.releaseMouse=r.capture(this.editor.container,o,a);var l=setInterval(f,20)},this.releaseMouse=null,this.cancelContextMenu=function(){var e=function(t){if(t&&t.domEvent&&t.domEvent.type!="contextmenu")return;this.editor.off("nativecontextmenu",e),t&&t.domEvent&&r.stopEvent(t.domEvent)}.bind(this);setTimeout(e,10),this.editor.on("nativecontextmenu",e)}}).call(l.prototype),f.defineOptions(l.prototype,"mouseHandler",{scrollSpeed:{initialValue:2},dragDelay:{initialValue:i.isMac?150:0},dragEnabled:{initialValue:!0},focusTimout:{initialValue:0},tooltipFollowsMouse:{initialValue:!0}}),t.MouseHandler=l}),define("ace/mouse/fold_handler",["require","exports","module"],function(e,t,n){"use strict";function r(e){e.on("click",function(t){var n=t.getDocumentPosition(),r=e.session,i=r.getFoldAt(n.row,n.column,1);i&&(t.getAccelKey()?r.removeFold(i):r.expandFold(i),t.stop())}),e.on("gutterclick",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session;i.foldWidgets&&i.foldWidgets[r]&&e.session.onFoldWidgetClick(r,t),e.isFocused()||e.focus(),t.stop()}}),e.on("gutterdblclick",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session,s=i.getParentFoldRangeData(r,!0),o=s.range||s.firstRange;if(o){r=o.start.row;var u=i.getFoldAt(r,i.getLine(r).length,1);u?i.removeFold(u):(i.addFold("...",o),e.renderer.scrollCursorIntoView({row:o.start.row,column:0}))}t.stop()}})}t.FoldHandler=r}),define("ace/keyboard/keybinding",["require","exports","module","ace/lib/keys","ace/lib/event"],function(e,t,n){"use strict";var r=e("../lib/keys"),i=e("../lib/event"),s=function(e){this.$editor=e,this.$data={editor:e},this.$handlers=[],this.setDefaultHandler(e.commands)};(function(){this.setDefaultHandler=function(e){this.removeKeyboardHandler(this.$defaultHandler),this.$defaultHandler=e,this.addKeyboardHandler(e,0)},this.setKeyboardHandler=function(e){var t=this.$handlers;if(t[t.length-1]==e)return;while(t[t.length-1]&&t[t.length-1]!=this.$defaultHandler)this.removeKeyboardHandler(t[t.length-1]);this.addKeyboardHandler(e,1)},this.addKeyboardHandler=function(e,t){if(!e)return;typeof e=="function"&&!e.handleKeyboard&&(e.handleKeyboard=e);var n=this.$handlers.indexOf(e);n!=-1&&this.$handlers.splice(n,1),t==undefined?this.$handlers.push(e):this.$handlers.splice(t,0,e),n==-1&&e.attach&&e.attach(this.$editor)},this.removeKeyboardHandler=function(e){var t=this.$handlers.indexOf(e);return t==-1?!1:(this.$handlers.splice(t,1),e.detach&&e.detach(this.$editor),!0)},this.getKeyboardHandler=function(){return this.$handlers[this.$handlers.length-1]},this.getStatusText=function(){var e=this.$data,t=e.editor;return this.$handlers.map(function(n){return n.getStatusText&&n.getStatusText(t,e)||""}).filter(Boolean).join(" ")},this.$callKeyboardHandlers=function(e,t,n,r){var s,o=!1,u=this.$editor.commands;for(var a=this.$handlers.length;a--;){s=this.$handlers[a].handleKeyboard(this.$data,e,t,n,r);if(!s||!s.command)continue;s.command=="null"?o=!0:o=u.exec(s.command,this.$editor,s.args,r),o&&r&&e!=-1&&s.passEvent!=1&&s.command.passEvent!=1&&i.stopEvent(r);if(o)break}return!o&&e==-1&&(s={command:"insertstring"},o=u.exec("insertstring",this.$editor,t)),o&&this.$editor._signal&&this.$editor._signal("keyboardActivity",s),o},this.onCommandKey=function(e,t,n){var i=r.keyCodeToString(n);this.$callKeyboardHandlers(t,i,n,e)},this.onTextInput=function(e){this.$callKeyboardHandlers(-1,e)}}).call(s.prototype),t.KeyBinding=s}),define("ace/range",["require","exports","module"],function(e,t,n){"use strict";var r=function(e,t){return e.row-t.row||e.column-t.column},i=function(e,t,n,r){this.start={row:e,column:t},this.end={row:n,column:r}};(function(){this.isEqual=function(e){return this.start.row===e.start.row&&this.end.row===e.end.row&&this.start.column===e.start.column&&this.end.column===e.end.column},this.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(e,t){return this.compare(e,t)==0},this.compareRange=function(e){var t,n=e.end,r=e.start;return t=this.compare(n.row,n.column),t==1?(t=this.compare(r.row,r.column),t==1?2:t==0?1:0):t==-1?-2:(t=this.compare(r.row,r.column),t==-1?-1:t==1?42:0)},this.comparePoint=function(e){return this.compare(e.row,e.column)},this.containsRange=function(e){return this.comparePoint(e.start)==0&&this.comparePoint(e.end)==0},this.intersects=function(e){var t=this.compareRange(e);return t==-1||t==0||t==1},this.isEnd=function(e,t){return this.end.row==e&&this.end.column==t},this.isStart=function(e,t){return this.start.row==e&&this.start.column==t},this.setStart=function(e,t){typeof e=="object"?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},this.setEnd=function(e,t){typeof e=="object"?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},this.inside=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)||this.isStart(e,t)?!1:!0:!1},this.insideStart=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)?!1:!0:!1},this.insideEnd=function(e,t){return this.compare(e,t)==0?this.isStart(e,t)?!1:!0:!1},this.compare=function(e,t){return!this.isMultiLine()&&e===this.start.row?t<this.start.column?-1:t>this.end.column?1:0:e<this.start.row?-1:e>this.end.row?1:this.start.row===e?t>=this.start.column?0:-1:this.end.row===e?t<=this.end.column?0:1:0},this.compareStart=function(e,t){return this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.compareEnd=function(e,t){return this.end.row==e&&this.end.column==t?1:this.compare(e,t)},this.compareInside=function(e,t){return this.end.row==e&&this.end.column==t?1:this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.clipRows=function(e,t){if(this.end.row>t)var n={row:t+1,column:0};else if(this.end.row<e)var n={row:e,column:0};if(this.start.row>t)var r={row:t+1,column:0};else if(this.start.row<e)var r={row:e,column:0};return i.fromPoints(r||this.start,n||this.end)},this.extend=function(e,t){var n=this.compare(e,t);if(n==0)return this;if(n==-1)var r={row:e,column:t};else var s={row:e,column:t};return i.fromPoints(r||this.start,s||this.end)},this.isEmpty=function(){return this.start.row===this.end.row&&this.start.column===this.end.column},this.isMultiLine=function(){return this.start.row!==this.end.row},this.clone=function(){return i.fromPoints(this.start,this.end)},this.collapseRows=function(){return this.end.column==0?new i(this.start.row,0,Math.max(this.start.row,this.end.row-1),0):new i(this.start.row,0,this.end.row,0)},this.toScreenRange=function(e){var t=e.documentToScreenPosition(this.start),n=e.documentToScreenPosition(this.end);return new i(t.row,t.column,n.row,n.column)},this.moveBy=function(e,t){this.start.row+=e,this.start.column+=t,this.end.row+=e,this.end.column+=t}}).call(i.prototype),i.fromPoints=function(e,t){return new i(e.row,e.column,t.row,t.column)},i.comparePoints=r,i.comparePoints=function(e,t){return e.row-t.row||e.column-t.column},t.Range=i}),define("ace/selection",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter","ace/range"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/lang"),s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=function(e){this.session=e,this.doc=e.getDocument(),this.clearSelection(),this.lead=this.selectionLead=this.doc.createAnchor(0,0),this.anchor=this.selectionAnchor=this.doc.createAnchor(0,0);var t=this;this.lead.on("change",function(e){t._emit("changeCursor"),t.$isEmpty||t._emit("changeSelection"),!t.$keepDesiredColumnOnChange&&e.old.column!=e.value.column&&(t.$desiredColumn=null)}),this.selectionAnchor.on("change",function(){t.$isEmpty||t._emit("changeSelection")})};(function(){r.implement(this,s),this.isEmpty=function(){return this.$isEmpty||this.anchor.row==this.lead.row&&this.anchor.column==this.lead.column},this.isMultiLine=function(){return this.isEmpty()?!1:this.getRange().isMultiLine()},this.getCursor=function(){return this.lead.getPosition()},this.setSelectionAnchor=function(e,t){this.anchor.setPosition(e,t),this.$isEmpty&&(this.$isEmpty=!1,this._emit("changeSelection"))},this.getSelectionAnchor=function(){return this.$isEmpty?this.getSelectionLead():this.anchor.getPosition()},this.getSelectionLead=function(){return this.lead.getPosition()},this.shiftSelection=function(e){if(this.$isEmpty){this.moveCursorTo(this.lead.row,this.lead.column+e);return}var t=this.getSelectionAnchor(),n=this.getSelectionLead(),r=this.isBackwards();(!r||t.column!==0)&&this.setSelectionAnchor(t.row,t.column+e),(r||n.column!==0)&&this.$moveSelection(function(){this.moveCursorTo(n.row,n.column+e)})},this.isBackwards=function(){var e=this.anchor,t=this.lead;return e.row>t.row||e.row==t.row&&e.column>t.column},this.getRange=function(){var e=this.anchor,t=this.lead;return this.isEmpty()?o.fromPoints(t,t):this.isBackwards()?o.fromPoints(t,e):o.fromPoints(e,t)},this.clearSelection=function(){this.$isEmpty||(this.$isEmpty=!0,this._emit("changeSelection"))},this.selectAll=function(){var e=this.doc.getLength()-1;this.setSelectionAnchor(0,0),this.moveCursorTo(e,this.doc.getLine(e).length)},this.setRange=this.setSelectionRange=function(e,t){t?(this.setSelectionAnchor(e.end.row,e.end.column),this.selectTo(e.start.row,e.start.column)):(this.setSelectionAnchor(e.start.row,e.start.column),this.selectTo(e.end.row,e.end.column)),this.getRange().isEmpty()&&(this.$isEmpty=!0),this.$desiredColumn=null},this.$moveSelection=function(e){var t=this.lead;this.$isEmpty&&this.setSelectionAnchor(t.row,t.column),e.call(this)},this.selectTo=function(e,t){this.$moveSelection(function(){this.moveCursorTo(e,t)})},this.selectToPosition=function(e){this.$moveSelection(function(){this.moveCursorToPosition(e)})},this.moveTo=function(e,t){this.clearSelection(),this.moveCursorTo(e,t)},this.moveToPosition=function(e){this.clearSelection(),this.moveCursorToPosition(e)},this.selectUp=function(){this.$moveSelection(this.moveCursorUp)},this.selectDown=function(){this.$moveSelection(this.moveCursorDown)},this.selectRight=function(){this.$moveSelection(this.moveCursorRight)},this.selectLeft=function(){this.$moveSelection(this.moveCursorLeft)},this.selectLineStart=function(){this.$moveSelection(this.moveCursorLineStart)},this.selectLineEnd=function(){this.$moveSelection(this.moveCursorLineEnd)},this.selectFileEnd=function(){this.$moveSelection(this.moveCursorFileEnd)},this.selectFileStart=function(){this.$moveSelection(this.moveCursorFileStart)},this.selectWordRight=function(){this.$moveSelection(this.moveCursorWordRight)},this.selectWordLeft=function(){this.$moveSelection(this.moveCursorWordLeft)},this.getWordRange=function(e,t){if(typeof t=="undefined"){var n=e||this.lead;e=n.row,t=n.column}return this.session.getWordRange(e,t)},this.selectWord=function(){this.setSelectionRange(this.getWordRange())},this.selectAWord=function(){var e=this.getCursor(),t=this.session.getAWordRange(e.row,e.column);this.setSelectionRange(t)},this.getLineRange=function(e,t){var n=typeof e=="number"?e:this.lead.row,r,i=this.session.getFoldLine(n);return i?(n=i.start.row,r=i.end.row):r=n,t===!0?new o(n,0,r,this.session.getLine(r).length):new o(n,0,r+1,0)},this.selectLine=function(){this.setSelectionRange(this.getLineRange())},this.moveCursorUp=function(){this.moveCursorBy(-1,0)},this.moveCursorDown=function(){this.moveCursorBy(1,0)},this.wouldMoveIntoSoftTab=function(e,t,n){var r=e.column,i=e.column+t;return n<0&&(r=e.column-t,i=e.column),this.session.isTabStop(e)&&this.doc.getLine(e.row).slice(r,i).split(" ").length-1==t},this.moveCursorLeft=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,-1))this.moveCursorTo(t.start.row,t.start.column);else if(e.column===0)e.row>0&&this.moveCursorTo(e.row-1,this.doc.getLine(e.row-1).length);else{var n=this.session.getTabSize();this.wouldMoveIntoSoftTab(e,n,-1)&&!this.session.getNavigateWithinSoftTabs()?this.moveCursorBy(0,-n):this.moveCursorBy(0,-1)}},this.moveCursorRight=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,1))this.moveCursorTo(t.end.row,t.end.column);else if(this.lead.column==this.doc.getLine(this.lead.row).length)this.lead.row<this.doc.getLength()-1&&this.moveCursorTo(this.lead.row+1,0);else{var n=this.session.getTabSize(),e=this.lead;this.wouldMoveIntoSoftTab(e,n,1)&&!this.session.getNavigateWithinSoftTabs()?this.moveCursorBy(0,n):this.moveCursorBy(0,1)}},this.moveCursorLineStart=function(){var e=this.lead.row,t=this.lead.column,n=this.session.documentToScreenRow(e,t),r=this.session.screenToDocumentPosition(n,0),i=this.session.getDisplayLine(e,null,r.row,r.column),s=i.match(/^\s*/);s[0].length!=t&&!this.session.$useEmacsStyleLineStart&&(r.column+=s[0].length),this.moveCursorToPosition(r)},this.moveCursorLineEnd=function(){var e=this.lead,t=this.session.getDocumentLastRowColumnPosition(e.row,e.column);if(this.lead.column==t.column){var n=this.session.getLine(t.row);if(t.column==n.length){var r=n.search(/\s+$/);r>0&&(t.column=r)}}this.moveCursorTo(t.row,t.column)},this.moveCursorFileEnd=function(){var e=this.doc.getLength()-1,t=this.doc.getLine(e).length;this.moveCursorTo(e,t)},this.moveCursorFileStart=function(){this.moveCursorTo(0,0)},this.moveCursorLongWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i;this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;var s=this.session.getFoldAt(e,t,1);if(s){this.moveCursorTo(s.end.row,s.end.column);return}if(i=this.session.nonTokenRe.exec(r))t+=this.session.nonTokenRe.lastIndex,this.session.nonTokenRe.lastIndex=0,r=n.substring(t);if(t>=n.length){this.moveCursorTo(e,n.length),this.moveCursorRight(),e<this.doc.getLength()-1&&this.moveCursorWordRight();return}if(i=this.session.tokenRe.exec(r))t+=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0;this.moveCursorTo(e,t)},this.moveCursorLongWordLeft=function(){var e=this.lead.row,t=this.lead.column,n;if(n=this.session.getFoldAt(e,t,-1)){this.moveCursorTo(n.start.row,n.start.column);return}var r=this.session.getFoldStringAt(e,t,-1);r==null&&(r=this.doc.getLine(e).substring(0,t));var s=i.stringReverse(r),o;this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;if(o=this.session.nonTokenRe.exec(s))t-=this.session.nonTokenRe.lastIndex,s=s.slice(this.session.nonTokenRe.lastIndex),this.session.nonTokenRe.lastIndex=0;if(t<=0){this.moveCursorTo(e,0),this.moveCursorLeft(),e>0&&this.moveCursorWordLeft();return}if(o=this.session.tokenRe.exec(s))t-=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0;this.moveCursorTo(e,t)},this.$shortWordEndIndex=function(e){var t,n=0,r,i=/\s/,s=this.session.tokenRe;s.lastIndex=0;if(t=this.session.tokenRe.exec(e))n=this.session.tokenRe.lastIndex;else{while((r=e[n])&&i.test(r))n++;if(n<1){s.lastIndex=0;while((r=e[n])&&!s.test(r)){s.lastIndex=0,n++;if(i.test(r)){if(n>2){n--;break}while((r=e[n])&&i.test(r))n++;if(n>2)break}}}}return s.lastIndex=0,n},this.moveCursorShortWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i=this.session.getFoldAt(e,t,1);if(i)return this.moveCursorTo(i.end.row,i.end.column);if(t==n.length){var s=this.doc.getLength();do e++,r=this.doc.getLine(e);while(e<s&&/^\s*$/.test(r));/^\s+/.test(r)||(r=""),t=0}var o=this.$shortWordEndIndex(r);this.moveCursorTo(e,t+o)},this.moveCursorShortWordLeft=function(){var e=this.lead.row,t=this.lead.column,n;if(n=this.session.getFoldAt(e,t,-1))return this.moveCursorTo(n.start.row,n.start.column);var r=this.session.getLine(e).substring(0,t);if(t===0){do e--,r=this.doc.getLine(e);while(e>0&&/^\s*$/.test(r));t=r.length,/\s+$/.test(r)||(r="")}var s=i.stringReverse(r),o=this.$shortWordEndIndex(s);return this.moveCursorTo(e,t-o)},this.moveCursorWordRight=function(){this.session.$selectLongWords?this.moveCursorLongWordRight():this.moveCursorShortWordRight()},this.moveCursorWordLeft=function(){this.session.$selectLongWords?this.moveCursorLongWordLeft():this.moveCursorShortWordLeft()},this.moveCursorBy=function(e,t){var n=this.session.documentToScreenPosition(this.lead.row,this.lead.column);t===0&&(this.$desiredColumn?n.column=this.$desiredColumn:this.$desiredColumn=n.column);var r=this.session.screenToDocumentPosition(n.row+e,n.column);e!==0&&t===0&&r.row===this.lead.row&&r.column===this.lead.column&&this.session.lineWidgets&&this.session.lineWidgets[r.row]&&(r.row>0||e>0)&&r.row++,this.moveCursorTo(r.row,r.column+t,t===0)},this.moveCursorToPosition=function(e){this.moveCursorTo(e.row,e.column)},this.moveCursorTo=function(e,t,n){var r=this.session.getFoldAt(e,t,1);r&&(e=r.start.row,t=r.start.column),this.$keepDesiredColumnOnChange=!0,this.lead.setPosition(e,t),this.$keepDesiredColumnOnChange=!1,n||(this.$desiredColumn=null)},this.moveCursorToScreen=function(e,t,n){var r=this.session.screenToDocumentPosition(e,t);this.moveCursorTo(r.row,r.column,n)},this.detach=function(){this.lead.detach(),this.anchor.detach(),this.session=this.doc=null},this.fromOrientedRange=function(e){this.setSelectionRange(e,e.cursor==e.start),this.$desiredColumn=e.desiredColumn||this.$desiredColumn},this.toOrientedRange=function(e){var t=this.getRange();return e?(e.start.column=t.start.column,e.start.row=t.start.row,e.end.column=t.end.column,e.end.row=t.end.row):e=t,e.cursor=this.isBackwards()?e.start:e.end,e.desiredColumn=this.$desiredColumn,e},this.getRangeOfMovements=function(e){var t=this.getCursor();try{e(this);var n=this.getCursor();return o.fromPoints(t,n)}catch(r){return o.fromPoints(t,t)}finally{this.moveCursorToPosition(t)}},this.toJSON=function(){if(this.rangeCount)var e=this.ranges.map(function(e){var t=e.clone();return t.isBackwards=e.cursor==e.start,t});else{var e=this.getRange();e.isBackwards=this.isBackwards()}return e},this.fromJSON=function(e){if(e.start==undefined){if(this.rangeList){this.toSingleRange(e[0]);for(var t=e.length;t--;){var n=o.fromPoints(e[t].start,e[t].end);e[t].isBackwards&&(n.cursor=n.start),this.addRange(n,!0)}return}e=e[0]}this.rangeList&&this.toSingleRange(e),this.setSelectionRange(e,e.isBackwards)},this.isEqual=function(e){if((e.length||this.rangeCount)&&e.length!=this.rangeCount)return!1;if(!e.length||!this.ranges)return this.getRange().isEqual(e);for(var t=this.ranges.length;t--;)if(!this.ranges[t].isEqual(e[t]))return!1;return!0}}).call(u.prototype),t.Selection=u}),define("ace/tokenizer",["require","exports","module","ace/config"],function(e,t,n){"use strict";var r=e("./config"),i=2e3,s=function(e){this.states=e,this.regExps={},this.matchMappings={};for(var t in this.states){var n=this.states[t],r=[],i=0,s=this.matchMappings[t]={defaultToken:"text"},o="g",u=[];for(var a=0;a<n.length;a++){var f=n[a];f.defaultToken&&(s.defaultToken=f.defaultToken),f.caseInsensitive&&(o="gi");if(f.regex==null)continue;f.regex instanceof RegExp&&(f.regex=f.regex.toString().slice(1,-1));var l=f.regex,c=(new RegExp("(?:("+l+")|(.))")).exec("a").length-2;Array.isArray(f.token)?f.token.length==1||c==1?f.token=f.token[0]:c-1!=f.token.length?(this.reportError("number of classes and regexp groups doesn't match",{rule:f,groupCount:c-1}),f.token=f.token[0]):(f.tokenArray=f.token,f.token=null,f.onMatch=this.$arrayTokens):typeof f.token=="function"&&!f.onMatch&&(c>1?f.onMatch=this.$applyToken:f.onMatch=f.token),c>1&&(/\\\d/.test(f.regex)?l=f.regex.replace(/\\([0-9]+)/g,function(e,t){return"\\"+(parseInt(t,10)+i+1)}):(c=1,l=this.removeCapturingGroups(f.regex)),!f.splitRegex&&typeof f.token!="string"&&u.push(f)),s[i]=a,i+=c,r.push(l),f.onMatch||(f.onMatch=null)}r.length||(s[0]=0,r.push("$")),u.forEach(function(e){e.splitRegex=this.createSplitterRegexp(e.regex,o)},this),this.regExps[t]=new RegExp("("+r.join(")|(")+")|($)",o)}};(function(){this.$setMaxTokenCount=function(e){i=e|0},this.$applyToken=function(e){var t=this.splitRegex.exec(e).slice(1),n=this.token.apply(this,t);if(typeof n=="string")return[{type:n,value:e}];var r=[];for(var i=0,s=n.length;i<s;i++)t[i]&&(r[r.length]={type:n[i],value:t[i]});return r},this.$arrayTokens=function(e){if(!e)return[];var t=this.splitRegex.exec(e);if(!t)return"text";var n=[],r=this.tokenArray;for(var i=0,s=r.length;i<s;i++)t[i+1]&&(n[n.length]={type:r[i],value:t[i+1]});return n},this.removeCapturingGroups=function(e){var t=e.replace(/\[(?:\\.|[^\]])*?\]|\\.|\(\?[:=!]|(\()/g,function(e,t){return t?"(?:":e});return t},this.createSplitterRegexp=function(e,t){if(e.indexOf("(?=")!=-1){var n=0,r=!1,i={};e.replace(/(\\.)|(\((?:\?[=!])?)|(\))|([\[\]])/g,function(e,t,s,o,u,a){return r?r=u!="]":u?r=!0:o?(n==i.stack&&(i.end=a+1,i.stack=-1),n--):s&&(n++,s.length!=1&&(i.stack=n,i.start=a)),e}),i.end!=null&&/^\)*$/.test(e.substr(i.end))&&(e=e.substring(0,i.start)+e.substr(i.end))}return e.charAt(0)!="^"&&(e="^"+e),e.charAt(e.length-1)!="$"&&(e+="$"),new RegExp(e,(t||"").replace("g",""))},this.getLineTokens=function(e,t){if(t&&typeof t!="string"){var n=t.slice(0);t=n[0],t==="#tmp"&&(n.shift(),t=n.shift())}else var n=[];var r=t||"start",s=this.states[r];s||(r="start",s=this.states[r]);var o=this.matchMappings[r],u=this.regExps[r];u.lastIndex=0;var a,f=[],l=0,c=0,h={type:null,value:""};while(a=u.exec(e)){var p=o.defaultToken,d=null,v=a[0],m=u.lastIndex;if(m-v.length>l){var g=e.substring(l,m-v.length);h.type==p?h.value+=g:(h.type&&f.push(h),h={type:p,value:g})}for(var y=0;y<a.length-2;y++){if(a[y+1]===undefined)continue;d=s[o[y]],d.onMatch?p=d.onMatch(v,r,n,e):p=d.token,d.next&&(typeof d.next=="string"?r=d.next:r=d.next(r,n),s=this.states[r],s||(this.reportError("state doesn't exist",r),r="start",s=this.states[r]),o=this.matchMappings[r],l=m,u=this.regExps[r],u.lastIndex=m),d.consumeLineEnd&&(l=m);break}if(v)if(typeof p=="string")!!d&&d.merge===!1||h.type!==p?(h.type&&f.push(h),h={type:p,value:v}):h.value+=v;else if(p){h.type&&f.push(h),h={type:null,value:""};for(var y=0;y<p.length;y++)f.push(p[y])}if(l==e.length)break;l=m;if(c++>i){c>2*e.length&&this.reportError("infinite loop with in ace tokenizer",{startState:t,line:e});while(l<e.length)h.type&&f.push(h),h={value:e.substring(l,l+=2e3),type:"overflow"};r="start",n=[];break}}return h.type&&f.push(h),n.length>1&&n[0]!==r&&n.unshift("#tmp",r),{tokens:f,state:n.length?n:r}},this.reportError=r.reportError}).call(s.prototype),t.Tokenizer=s}),define("ace/mode/text_highlight_rules",["require","exports","module","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../lib/lang"),i=function(){this.$rules={start:[{token:"empty_line",regex:"^$"},{defaultToken:"text"}]}};(function(){this.addRules=function(e,t){if(!t){for(var n in e)this.$rules[n]=e[n];return}for(var n in e){var r=e[n];for(var i=0;i<r.length;i++){var s=r[i];if(s.next||s.onMatch)typeof s.next=="string"&&s.next.indexOf(t)!==0&&(s.next=t+s.next),s.nextState&&s.nextState.indexOf(t)!==0&&(s.nextState=t+s.nextState)}this.$rules[t+n]=r}},this.getRules=function(){return this.$rules},this.embedRules=function(e,t,n,i,s){var o=typeof e=="function"?(new e).getRules():e;if(i)for(var u=0;u<i.length;u++)i[u]=t+i[u];else{i=[];for(var a in o)i.push(t+a)}this.addRules(o,t);if(n){var f=Array.prototype[s?"push":"unshift"];for(var u=0;u<i.length;u++)f.apply(this.$rules[i[u]],r.deepCopy(n))}this.$embeds||(this.$embeds=[]),this.$embeds.push(t)},this.getEmbeds=function(){return this.$embeds};var e=function(e,t){return(e!="start"||t.length)&&t.unshift(this.nextState,e),this.nextState},t=function(e,t){return t.shift(),t.shift()||"start"};this.normalizeRules=function(){function i(s){var o=r[s];o.processed=!0;for(var u=0;u<o.length;u++){var a=o[u],f=null;Array.isArray(a)&&(f=a,a={}),!a.regex&&a.start&&(a.regex=a.start,a.next||(a.next=[]),a.next.push({defaultToken:a.token},{token:a.token+".end",regex:a.end||a.start,next:"pop"}),a.token=a.token+".start",a.push=!0);var l=a.next||a.push;if(l&&Array.isArray(l)){var c=a.stateName;c||(c=a.token,typeof c!="string"&&(c=c[0]||""),r[c]&&(c+=n++)),r[c]=l,a.next=c,i(c)}else l=="pop"&&(a.next=t);a.push&&(a.nextState=a.next||a.push,a.next=e,delete a.push);if(a.rules)for(var h in a.rules)r[h]?r[h].push&&r[h].push.apply(r[h],a.rules[h]):r[h]=a.rules[h];var p=typeof a=="string"?a:a.include;p&&(Array.isArray(p)?f=p.map(function(e){return r[e]}):f=r[p]);if(f){var d=[u,1].concat(f);a.noEscape&&(d=d.filter(function(e){return!e.next})),o.splice.apply(o,d),u--}a.keywordMap&&(a.token=this.createKeywordMapper(a.keywordMap,a.defaultToken||"text",a.caseInsensitive),delete a.defaultToken)}}var n=0,r=this.$rules;Object.keys(r).forEach(i,this)},this.createKeywordMapper=function(e,t,n,r){var i=Object.create(null);return Object.keys(e).forEach(function(t){var s=e[t];n&&(s=s.toLowerCase());var o=s.split(r||"|");for(var u=o.length;u--;)i[o[u]]=t}),Object.getPrototypeOf(i)&&(i.__proto__=null),this.$keywordList=Object.keys(i),e=null,n?function(e){return i[e.toLowerCase()]||t}:function(e){return i[e]||t}},this.getKeywords=function(){return this.$keywords}}).call(i.prototype),t.TextHighlightRules=i}),define("ace/mode/behaviour",["require","exports","module"],function(e,t,n){"use strict";var r=function(){this.$behaviours={}};(function(){this.add=function(e,t,n){switch(undefined){case this.$behaviours:this.$behaviours={};case this.$behaviours[e]:this.$behaviours[e]={}}this.$behaviours[e][t]=n},this.addBehaviours=function(e){for(var t in e)for(var n in e[t])this.add(t,n,e[t][n])},this.remove=function(e){this.$behaviours&&this.$behaviours[e]&&delete this.$behaviours[e]},this.inherit=function(e,t){if(typeof e=="function")var n=(new e).getBehaviours(t);else var n=e.getBehaviours(t);this.addBehaviours(n)},this.getBehaviours=function(e){if(!e)return this.$behaviours;var t={};for(var n=0;n<e.length;n++)this.$behaviours[e[n]]&&(t[e[n]]=this.$behaviours[e[n]]);return t}}).call(r.prototype),t.Behaviour=r}),define("ace/token_iterator",["require","exports","module"],function(e,t,n){"use strict";var r=function(e,t,n){this.$session=e,this.$row=t,this.$rowTokens=e.getTokens(t);var r=e.getTokenAt(t,n);this.$tokenIndex=r?r.index:-1};(function(){this.stepBackward=function(){this.$tokenIndex-=1;while(this.$tokenIndex<0){this.$row-=1;if(this.$row<0)return this.$row=0,null;this.$rowTokens=this.$session.getTokens(this.$row),this.$tokenIndex=this.$rowTokens.length-1}return this.$rowTokens[this.$tokenIndex]},this.stepForward=function(){this.$tokenIndex+=1;var e;while(this.$tokenIndex>=this.$rowTokens.length){this.$row+=1,e||(e=this.$session.getLength());if(this.$row>=e)return this.$row=e-1,null;this.$rowTokens=this.$session.getTokens(this.$row),this.$tokenIndex=0}return this.$rowTokens[this.$tokenIndex]},this.getCurrentToken=function(){return this.$rowTokens[this.$tokenIndex]},this.getCurrentTokenRow=function(){return this.$row},this.getCurrentTokenColumn=function(){var e=this.$rowTokens,t=this.$tokenIndex,n=e[t].start;if(n!==undefined)return n;n=0;while(t>0)t-=1,n+=e[t].value.length;return n},this.getCurrentTokenPosition=function(){return{row:this.$row,column:this.getCurrentTokenColumn()}}}).call(r.prototype),t.TokenIterator=r}),define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),u=["text","paren.rparen","punctuation.operator"],a=["text","paren.rparen","punctuation.operator","comment"],f,l={},c={'"':'"',"'":"'"},h=function(e){var t=-1;e.multiSelect&&(t=e.selection.index,l.rangeCount!=e.multiSelect.rangeCount&&(l={rangeCount:e.multiSelect.rangeCount}));if(l[t])return f=l[t];f=l[t]={autoInsertedBrackets:0,autoInsertedRow:-1,autoInsertedLineEnd:"",maybeInsertedBrackets:0,maybeInsertedRow:-1,maybeInsertedLineStart:"",maybeInsertedLineEnd:""}},p=function(e,t,n,r){var i=e.end.row-e.start.row;return{text:n+t+r,selection:[0,e.start.column+1,i,e.end.column+(i?0:1)]}},d=function(e){this.add("braces","insertion",function(t,n,r,i,s){var u=r.getCursorPosition(),a=i.doc.getLine(u.row);if(s=="{"){h(r);var l=r.getSelectionRange(),c=i.doc.getTextRange(l);if(c!==""&&c!=="{"&&r.getWrapBehavioursEnabled())return p(l,c,"{","}");if(d.isSaneInsertion(r,i))return/[\]\}\)]/.test(a[u.column])||r.inMultiSelectMode||e&&e.braces?(d.recordAutoInsert(r,i,"}"),{text:"{}",selection:[1,1]}):(d.recordMaybeInsert(r,i,"{"),{text:"{",selection:[1,1]})}else if(s=="}"){h(r);var v=a.substring(u.column,u.column+1);if(v=="}"){var m=i.$findOpeningBracket("}",{column:u.column+1,row:u.row});if(m!==null&&d.isAutoInsertedClosing(u,a,s))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else{if(s=="\n"||s=="\r\n"){h(r);var g="";d.isMaybeInsertedClosing(u,a)&&(g=o.stringRepeat("}",f.maybeInsertedBrackets),d.clearMaybeInsertedClosing());var v=a.substring(u.column,u.column+1);if(v==="}"){var y=i.findMatchingBracket({row:u.row,column:u.column+1},"}");if(!y)return null;var b=this.$getIndent(i.getLine(y.row))}else{if(!g){d.clearMaybeInsertedClosing();return}var b=this.$getIndent(a)}var w=b+i.getTabString();return{text:"\n"+w+"\n"+b+g,selection:[1,w.length,1,w.length]}}d.clearMaybeInsertedClosing()}}),this.add("braces","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="{"){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.end.column,i.end.column+1);if(u=="}")return i.end.column++,i;f.maybeInsertedBrackets--}}),this.add("parens","insertion",function(e,t,n,r,i){if(i=="("){h(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return p(s,o,"(",")");if(d.isSaneInsertion(n,r))return d.recordAutoInsert(n,r,")"),{text:"()",selection:[1,1]}}else if(i==")"){h(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==")"){var l=r.$findOpeningBracket(")",{column:u.column+1,row:u.row});if(l!==null&&d.isAutoInsertedClosing(u,a,i))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("parens","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="("){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==")")return i.end.column++,i}}),this.add("brackets","insertion",function(e,t,n,r,i){if(i=="["){h(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return p(s,o,"[","]");if(d.isSaneInsertion(n,r))return d.recordAutoInsert(n,r,"]"),{text:"[]",selection:[1,1]}}else if(i=="]"){h(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f=="]"){var l=r.$findOpeningBracket("]",{column:u.column+1,row:u.row});if(l!==null&&d.isAutoInsertedClosing(u,a,i))return d.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("brackets","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="["){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u=="]")return i.end.column++,i}}),this.add("string_dquotes","insertion",function(e,t,n,r,i){var s=r.$mode.$quotes||c;if(i.length==1&&s[i]){if(this.lineCommentStart&&this.lineCommentStart.indexOf(i)!=-1)return;h(n);var o=i,u=n.getSelectionRange(),a=r.doc.getTextRange(u);if(a!==""&&(a.length!=1||!s[a])&&n.getWrapBehavioursEnabled())return p(u,a,o,o);if(!a){var f=n.getCursorPosition(),l=r.doc.getLine(f.row),d=l.substring(f.column-1,f.column),v=l.substring(f.column,f.column+1),m=r.getTokenAt(f.row,f.column),g=r.getTokenAt(f.row,f.column+1);if(d=="\\"&&m&&/escape/.test(m.type))return null;var y=m&&/string|escape/.test(m.type),b=!g||/string|escape/.test(g.type),w;if(v==o)w=y!==b,w&&/string\.end/.test(g.type)&&(w=!1);else{if(y&&!b)return null;if(y&&b)return null;var E=r.$mode.tokenRe;E.lastIndex=0;var S=E.test(d);E.lastIndex=0;var x=E.test(d);if(S||x)return null;if(v&&!/[\s;,.})\]\\]/.test(v))return null;w=!0}return{text:w?o+o:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}})};d.isSaneInsertion=function(e,t){var n=e.getCursorPosition(),r=new s(t,n.row,n.column);if(!this.$matchTokenType(r.getCurrentToken()||"text",u)){var i=new s(t,n.row,n.column+1);if(!this.$matchTokenType(i.getCurrentToken()||"text",u))return!1}return r.stepForward(),r.getCurrentTokenRow()!==n.row||this.$matchTokenType(r.getCurrentToken()||"text",a)},d.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},d.recordAutoInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,i,f.autoInsertedLineEnd[0])||(f.autoInsertedBrackets=0),f.autoInsertedRow=r.row,f.autoInsertedLineEnd=n+i.substr(r.column),f.autoInsertedBrackets++},d.recordMaybeInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,i)||(f.maybeInsertedBrackets=0),f.maybeInsertedRow=r.row,f.maybeInsertedLineStart=i.substr(0,r.column)+n,f.maybeInsertedLineEnd=i.substr(r.column),f.maybeInsertedBrackets++},d.isAutoInsertedClosing=function(e,t,n){return f.autoInsertedBrackets>0&&e.row===f.autoInsertedRow&&n===f.autoInsertedLineEnd[0]&&t.substr(e.column)===f.autoInsertedLineEnd},d.isMaybeInsertedClosing=function(e,t){return f.maybeInsertedBrackets>0&&e.row===f.maybeInsertedRow&&t.substr(e.column)===f.maybeInsertedLineEnd&&t.substr(0,e.column)==f.maybeInsertedLineStart},d.popAutoInsertedClosing=function(){f.autoInsertedLineEnd=f.autoInsertedLineEnd.substr(1),f.autoInsertedBrackets--},d.clearMaybeInsertedClosing=function(){f&&(f.maybeInsertedBrackets=0,f.maybeInsertedRow=-1)},r.inherits(d,i),t.CstyleBehaviour=d}),define("ace/unicode",["require","exports","module"],function(e,t,n){"use strict";function r(e){var n=/\w{4}/g;for(var r in e)t.packages[r]=e[r].replace(n,"\\u$&")}t.packages={},r({L:"0041-005A0061-007A00AA00B500BA00C0-00D600D8-00F600F8-02C102C6-02D102E0-02E402EC02EE0370-037403760377037A-037D03860388-038A038C038E-03A103A3-03F503F7-0481048A-05250531-055605590561-058705D0-05EA05F0-05F20621-064A066E066F0671-06D306D506E506E606EE06EF06FA-06FC06FF07100712-072F074D-07A507B107CA-07EA07F407F507FA0800-0815081A082408280904-0939093D09500958-0961097109720979-097F0985-098C098F09900993-09A809AA-09B009B209B6-09B909BD09CE09DC09DD09DF-09E109F009F10A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A59-0A5C0A5E0A72-0A740A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABD0AD00AE00AE10B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3D0B5C0B5D0B5F-0B610B710B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BD00C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D0C580C590C600C610C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBD0CDE0CE00CE10D05-0D0C0D0E-0D100D12-0D280D2A-0D390D3D0D600D610D7A-0D7F0D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60E01-0E300E320E330E40-0E460E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB00EB20EB30EBD0EC0-0EC40EC60EDC0EDD0F000F40-0F470F49-0F6C0F88-0F8B1000-102A103F1050-1055105A-105D106110651066106E-10701075-1081108E10A0-10C510D0-10FA10FC1100-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A1380-138F13A0-13F41401-166C166F-167F1681-169A16A0-16EA1700-170C170E-17111720-17311740-17511760-176C176E-17701780-17B317D717DC1820-18771880-18A818AA18B0-18F51900-191C1950-196D1970-19741980-19AB19C1-19C71A00-1A161A20-1A541AA71B05-1B331B45-1B4B1B83-1BA01BAE1BAF1C00-1C231C4D-1C4F1C5A-1C7D1CE9-1CEC1CEE-1CF11D00-1DBF1E00-1F151F18-1F1D1F20-1F451F48-1F4D1F50-1F571F591F5B1F5D1F5F-1F7D1F80-1FB41FB6-1FBC1FBE1FC2-1FC41FC6-1FCC1FD0-1FD31FD6-1FDB1FE0-1FEC1FF2-1FF41FF6-1FFC2071207F2090-209421022107210A-211321152119-211D212421262128212A-212D212F-2139213C-213F2145-2149214E218321842C00-2C2E2C30-2C5E2C60-2CE42CEB-2CEE2D00-2D252D30-2D652D6F2D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDE2E2F300530063031-3035303B303C3041-3096309D-309F30A1-30FA30FC-30FF3105-312D3131-318E31A0-31B731F0-31FF3400-4DB54E00-9FCBA000-A48CA4D0-A4FDA500-A60CA610-A61FA62AA62BA640-A65FA662-A66EA67F-A697A6A0-A6E5A717-A71FA722-A788A78BA78CA7FB-A801A803-A805A807-A80AA80C-A822A840-A873A882-A8B3A8F2-A8F7A8FBA90A-A925A930-A946A960-A97CA984-A9B2A9CFAA00-AA28AA40-AA42AA44-AA4BAA60-AA76AA7AAA80-AAAFAAB1AAB5AAB6AAB9-AABDAAC0AAC2AADB-AADDABC0-ABE2AC00-D7A3D7B0-D7C6D7CB-D7FBF900-FA2DFA30-FA6DFA70-FAD9FB00-FB06FB13-FB17FB1DFB1F-FB28FB2A-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FBB1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFBFE70-FE74FE76-FEFCFF21-FF3AFF41-FF5AFF66-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC",Ll:"0061-007A00AA00B500BA00DF-00F600F8-00FF01010103010501070109010B010D010F01110113011501170119011B011D011F01210123012501270129012B012D012F01310133013501370138013A013C013E014001420144014601480149014B014D014F01510153015501570159015B015D015F01610163016501670169016B016D016F0171017301750177017A017C017E-0180018301850188018C018D019201950199-019B019E01A101A301A501A801AA01AB01AD01B001B401B601B901BA01BD-01BF01C601C901CC01CE01D001D201D401D601D801DA01DC01DD01DF01E101E301E501E701E901EB01ED01EF01F001F301F501F901FB01FD01FF02010203020502070209020B020D020F02110213021502170219021B021D021F02210223022502270229022B022D022F02310233-0239023C023F0240024202470249024B024D024F-02930295-02AF037103730377037B-037D039003AC-03CE03D003D103D5-03D703D903DB03DD03DF03E103E303E503E703E903EB03ED03EF-03F303F503F803FB03FC0430-045F04610463046504670469046B046D046F04710473047504770479047B047D047F0481048B048D048F04910493049504970499049B049D049F04A104A304A504A704A904AB04AD04AF04B104B304B504B704B904BB04BD04BF04C204C404C604C804CA04CC04CE04CF04D104D304D504D704D904DB04DD04DF04E104E304E504E704E904EB04ED04EF04F104F304F504F704F904FB04FD04FF05010503050505070509050B050D050F05110513051505170519051B051D051F0521052305250561-05871D00-1D2B1D62-1D771D79-1D9A1E011E031E051E071E091E0B1E0D1E0F1E111E131E151E171E191E1B1E1D1E1F1E211E231E251E271E291E2B1E2D1E2F1E311E331E351E371E391E3B1E3D1E3F1E411E431E451E471E491E4B1E4D1E4F1E511E531E551E571E591E5B1E5D1E5F1E611E631E651E671E691E6B1E6D1E6F1E711E731E751E771E791E7B1E7D1E7F1E811E831E851E871E891E8B1E8D1E8F1E911E931E95-1E9D1E9F1EA11EA31EA51EA71EA91EAB1EAD1EAF1EB11EB31EB51EB71EB91EBB1EBD1EBF1EC11EC31EC51EC71EC91ECB1ECD1ECF1ED11ED31ED51ED71ED91EDB1EDD1EDF1EE11EE31EE51EE71EE91EEB1EED1EEF1EF11EF31EF51EF71EF91EFB1EFD1EFF-1F071F10-1F151F20-1F271F30-1F371F40-1F451F50-1F571F60-1F671F70-1F7D1F80-1F871F90-1F971FA0-1FA71FB0-1FB41FB61FB71FBE1FC2-1FC41FC61FC71FD0-1FD31FD61FD71FE0-1FE71FF2-1FF41FF61FF7210A210E210F2113212F21342139213C213D2146-2149214E21842C30-2C5E2C612C652C662C682C6A2C6C2C712C732C742C76-2C7C2C812C832C852C872C892C8B2C8D2C8F2C912C932C952C972C992C9B2C9D2C9F2CA12CA32CA52CA72CA92CAB2CAD2CAF2CB12CB32CB52CB72CB92CBB2CBD2CBF2CC12CC32CC52CC72CC92CCB2CCD2CCF2CD12CD32CD52CD72CD92CDB2CDD2CDF2CE12CE32CE42CEC2CEE2D00-2D25A641A643A645A647A649A64BA64DA64FA651A653A655A657A659A65BA65DA65FA663A665A667A669A66BA66DA681A683A685A687A689A68BA68DA68FA691A693A695A697A723A725A727A729A72BA72DA72F-A731A733A735A737A739A73BA73DA73FA741A743A745A747A749A74BA74DA74FA751A753A755A757A759A75BA75DA75FA761A763A765A767A769A76BA76DA76FA771-A778A77AA77CA77FA781A783A785A787A78CFB00-FB06FB13-FB17FF41-FF5A",Lu:"0041-005A00C0-00D600D8-00DE01000102010401060108010A010C010E01100112011401160118011A011C011E01200122012401260128012A012C012E01300132013401360139013B013D013F0141014301450147014A014C014E01500152015401560158015A015C015E01600162016401660168016A016C016E017001720174017601780179017B017D018101820184018601870189-018B018E-0191019301940196-0198019C019D019F01A001A201A401A601A701A901AC01AE01AF01B1-01B301B501B701B801BC01C401C701CA01CD01CF01D101D301D501D701D901DB01DE01E001E201E401E601E801EA01EC01EE01F101F401F6-01F801FA01FC01FE02000202020402060208020A020C020E02100212021402160218021A021C021E02200222022402260228022A022C022E02300232023A023B023D023E02410243-02460248024A024C024E03700372037603860388-038A038C038E038F0391-03A103A3-03AB03CF03D2-03D403D803DA03DC03DE03E003E203E403E603E803EA03EC03EE03F403F703F903FA03FD-042F04600462046404660468046A046C046E04700472047404760478047A047C047E0480048A048C048E04900492049404960498049A049C049E04A004A204A404A604A804AA04AC04AE04B004B204B404B604B804BA04BC04BE04C004C104C304C504C704C904CB04CD04D004D204D404D604D804DA04DC04DE04E004E204E404E604E804EA04EC04EE04F004F204F404F604F804FA04FC04FE05000502050405060508050A050C050E05100512051405160518051A051C051E0520052205240531-055610A0-10C51E001E021E041E061E081E0A1E0C1E0E1E101E121E141E161E181E1A1E1C1E1E1E201E221E241E261E281E2A1E2C1E2E1E301E321E341E361E381E3A1E3C1E3E1E401E421E441E461E481E4A1E4C1E4E1E501E521E541E561E581E5A1E5C1E5E1E601E621E641E661E681E6A1E6C1E6E1E701E721E741E761E781E7A1E7C1E7E1E801E821E841E861E881E8A1E8C1E8E1E901E921E941E9E1EA01EA21EA41EA61EA81EAA1EAC1EAE1EB01EB21EB41EB61EB81EBA1EBC1EBE1EC01EC21EC41EC61EC81ECA1ECC1ECE1ED01ED21ED41ED61ED81EDA1EDC1EDE1EE01EE21EE41EE61EE81EEA1EEC1EEE1EF01EF21EF41EF61EF81EFA1EFC1EFE1F08-1F0F1F18-1F1D1F28-1F2F1F38-1F3F1F48-1F4D1F591F5B1F5D1F5F1F68-1F6F1FB8-1FBB1FC8-1FCB1FD8-1FDB1FE8-1FEC1FF8-1FFB21022107210B-210D2110-211221152119-211D212421262128212A-212D2130-2133213E213F214521832C00-2C2E2C602C62-2C642C672C692C6B2C6D-2C702C722C752C7E-2C802C822C842C862C882C8A2C8C2C8E2C902C922C942C962C982C9A2C9C2C9E2CA02CA22CA42CA62CA82CAA2CAC2CAE2CB02CB22CB42CB62CB82CBA2CBC2CBE2CC02CC22CC42CC62CC82CCA2CCC2CCE2CD02CD22CD42CD62CD82CDA2CDC2CDE2CE02CE22CEB2CEDA640A642A644A646A648A64AA64CA64EA650A652A654A656A658A65AA65CA65EA662A664A666A668A66AA66CA680A682A684A686A688A68AA68CA68EA690A692A694A696A722A724A726A728A72AA72CA72EA732A734A736A738A73AA73CA73EA740A742A744A746A748A74AA74CA74EA750A752A754A756A758A75AA75CA75EA760A762A764A766A768A76AA76CA76EA779A77BA77DA77EA780A782A784A786A78BFF21-FF3A",Lt:"01C501C801CB01F21F88-1F8F1F98-1F9F1FA8-1FAF1FBC1FCC1FFC",Lm:"02B0-02C102C6-02D102E0-02E402EC02EE0374037A0559064006E506E607F407F507FA081A0824082809710E460EC610FC17D718431AA71C78-1C7D1D2C-1D611D781D9B-1DBF2071207F2090-20942C7D2D6F2E2F30053031-3035303B309D309E30FC-30FEA015A4F8-A4FDA60CA67FA717-A71FA770A788A9CFAA70AADDFF70FF9EFF9F",Lo:"01BB01C0-01C3029405D0-05EA05F0-05F20621-063F0641-064A066E066F0671-06D306D506EE06EF06FA-06FC06FF07100712-072F074D-07A507B107CA-07EA0800-08150904-0939093D09500958-096109720979-097F0985-098C098F09900993-09A809AA-09B009B209B6-09B909BD09CE09DC09DD09DF-09E109F009F10A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A59-0A5C0A5E0A72-0A740A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABD0AD00AE00AE10B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3D0B5C0B5D0B5F-0B610B710B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BD00C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D0C580C590C600C610C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBD0CDE0CE00CE10D05-0D0C0D0E-0D100D12-0D280D2A-0D390D3D0D600D610D7A-0D7F0D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60E01-0E300E320E330E40-0E450E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB00EB20EB30EBD0EC0-0EC40EDC0EDD0F000F40-0F470F49-0F6C0F88-0F8B1000-102A103F1050-1055105A-105D106110651066106E-10701075-1081108E10D0-10FA1100-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A1380-138F13A0-13F41401-166C166F-167F1681-169A16A0-16EA1700-170C170E-17111720-17311740-17511760-176C176E-17701780-17B317DC1820-18421844-18771880-18A818AA18B0-18F51900-191C1950-196D1970-19741980-19AB19C1-19C71A00-1A161A20-1A541B05-1B331B45-1B4B1B83-1BA01BAE1BAF1C00-1C231C4D-1C4F1C5A-1C771CE9-1CEC1CEE-1CF12135-21382D30-2D652D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDE3006303C3041-3096309F30A1-30FA30FF3105-312D3131-318E31A0-31B731F0-31FF3400-4DB54E00-9FCBA000-A014A016-A48CA4D0-A4F7A500-A60BA610-A61FA62AA62BA66EA6A0-A6E5A7FB-A801A803-A805A807-A80AA80C-A822A840-A873A882-A8B3A8F2-A8F7A8FBA90A-A925A930-A946A960-A97CA984-A9B2AA00-AA28AA40-AA42AA44-AA4BAA60-AA6FAA71-AA76AA7AAA80-AAAFAAB1AAB5AAB6AAB9-AABDAAC0AAC2AADBAADCABC0-ABE2AC00-D7A3D7B0-D7C6D7CB-D7FBF900-FA2DFA30-FA6DFA70-FAD9FB1DFB1F-FB28FB2A-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FBB1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFBFE70-FE74FE76-FEFCFF66-FF6FFF71-FF9DFFA0-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC",M:"0300-036F0483-04890591-05BD05BF05C105C205C405C505C70610-061A064B-065E067006D6-06DC06DE-06E406E706E806EA-06ED07110730-074A07A6-07B007EB-07F30816-0819081B-08230825-08270829-082D0900-0903093C093E-094E0951-0955096209630981-098309BC09BE-09C409C709C809CB-09CD09D709E209E30A01-0A030A3C0A3E-0A420A470A480A4B-0A4D0A510A700A710A750A81-0A830ABC0ABE-0AC50AC7-0AC90ACB-0ACD0AE20AE30B01-0B030B3C0B3E-0B440B470B480B4B-0B4D0B560B570B620B630B820BBE-0BC20BC6-0BC80BCA-0BCD0BD70C01-0C030C3E-0C440C46-0C480C4A-0C4D0C550C560C620C630C820C830CBC0CBE-0CC40CC6-0CC80CCA-0CCD0CD50CD60CE20CE30D020D030D3E-0D440D46-0D480D4A-0D4D0D570D620D630D820D830DCA0DCF-0DD40DD60DD8-0DDF0DF20DF30E310E34-0E3A0E47-0E4E0EB10EB4-0EB90EBB0EBC0EC8-0ECD0F180F190F350F370F390F3E0F3F0F71-0F840F860F870F90-0F970F99-0FBC0FC6102B-103E1056-1059105E-10601062-10641067-106D1071-10741082-108D108F109A-109D135F1712-17141732-1734175217531772177317B6-17D317DD180B-180D18A91920-192B1930-193B19B0-19C019C819C91A17-1A1B1A55-1A5E1A60-1A7C1A7F1B00-1B041B34-1B441B6B-1B731B80-1B821BA1-1BAA1C24-1C371CD0-1CD21CD4-1CE81CED1CF21DC0-1DE61DFD-1DFF20D0-20F02CEF-2CF12DE0-2DFF302A-302F3099309AA66F-A672A67CA67DA6F0A6F1A802A806A80BA823-A827A880A881A8B4-A8C4A8E0-A8F1A926-A92DA947-A953A980-A983A9B3-A9C0AA29-AA36AA43AA4CAA4DAA7BAAB0AAB2-AAB4AAB7AAB8AABEAABFAAC1ABE3-ABEAABECABEDFB1EFE00-FE0FFE20-FE26",Mn:"0300-036F0483-04870591-05BD05BF05C105C205C405C505C70610-061A064B-065E067006D6-06DC06DF-06E406E706E806EA-06ED07110730-074A07A6-07B007EB-07F30816-0819081B-08230825-08270829-082D0900-0902093C0941-0948094D0951-095509620963098109BC09C1-09C409CD09E209E30A010A020A3C0A410A420A470A480A4B-0A4D0A510A700A710A750A810A820ABC0AC1-0AC50AC70AC80ACD0AE20AE30B010B3C0B3F0B41-0B440B4D0B560B620B630B820BC00BCD0C3E-0C400C46-0C480C4A-0C4D0C550C560C620C630CBC0CBF0CC60CCC0CCD0CE20CE30D41-0D440D4D0D620D630DCA0DD2-0DD40DD60E310E34-0E3A0E47-0E4E0EB10EB4-0EB90EBB0EBC0EC8-0ECD0F180F190F350F370F390F71-0F7E0F80-0F840F860F870F90-0F970F99-0FBC0FC6102D-10301032-10371039103A103D103E10581059105E-10601071-1074108210851086108D109D135F1712-17141732-1734175217531772177317B7-17BD17C617C9-17D317DD180B-180D18A91920-19221927192819321939-193B1A171A181A561A58-1A5E1A601A621A65-1A6C1A73-1A7C1A7F1B00-1B031B341B36-1B3A1B3C1B421B6B-1B731B801B811BA2-1BA51BA81BA91C2C-1C331C361C371CD0-1CD21CD4-1CE01CE2-1CE81CED1DC0-1DE61DFD-1DFF20D0-20DC20E120E5-20F02CEF-2CF12DE0-2DFF302A-302F3099309AA66FA67CA67DA6F0A6F1A802A806A80BA825A826A8C4A8E0-A8F1A926-A92DA947-A951A980-A982A9B3A9B6-A9B9A9BCAA29-AA2EAA31AA32AA35AA36AA43AA4CAAB0AAB2-AAB4AAB7AAB8AABEAABFAAC1ABE5ABE8ABEDFB1EFE00-FE0FFE20-FE26",Mc:"0903093E-09400949-094C094E0982098309BE-09C009C709C809CB09CC09D70A030A3E-0A400A830ABE-0AC00AC90ACB0ACC0B020B030B3E0B400B470B480B4B0B4C0B570BBE0BBF0BC10BC20BC6-0BC80BCA-0BCC0BD70C01-0C030C41-0C440C820C830CBE0CC0-0CC40CC70CC80CCA0CCB0CD50CD60D020D030D3E-0D400D46-0D480D4A-0D4C0D570D820D830DCF-0DD10DD8-0DDF0DF20DF30F3E0F3F0F7F102B102C10311038103B103C105610571062-10641067-106D108310841087-108C108F109A-109C17B617BE-17C517C717C81923-19261929-192B193019311933-193819B0-19C019C819C91A19-1A1B1A551A571A611A631A641A6D-1A721B041B351B3B1B3D-1B411B431B441B821BA11BA61BA71BAA1C24-1C2B1C341C351CE11CF2A823A824A827A880A881A8B4-A8C3A952A953A983A9B4A9B5A9BAA9BBA9BD-A9C0AA2FAA30AA33AA34AA4DAA7BABE3ABE4ABE6ABE7ABE9ABEAABEC",Me:"0488048906DE20DD-20E020E2-20E4A670-A672",N:"0030-003900B200B300B900BC-00BE0660-066906F0-06F907C0-07C90966-096F09E6-09EF09F4-09F90A66-0A6F0AE6-0AEF0B66-0B6F0BE6-0BF20C66-0C6F0C78-0C7E0CE6-0CEF0D66-0D750E50-0E590ED0-0ED90F20-0F331040-10491090-10991369-137C16EE-16F017E0-17E917F0-17F91810-18191946-194F19D0-19DA1A80-1A891A90-1A991B50-1B591BB0-1BB91C40-1C491C50-1C5920702074-20792080-20892150-21822185-21892460-249B24EA-24FF2776-27932CFD30073021-30293038-303A3192-31953220-32293251-325F3280-328932B1-32BFA620-A629A6E6-A6EFA830-A835A8D0-A8D9A900-A909A9D0-A9D9AA50-AA59ABF0-ABF9FF10-FF19",Nd:"0030-00390660-066906F0-06F907C0-07C90966-096F09E6-09EF0A66-0A6F0AE6-0AEF0B66-0B6F0BE6-0BEF0C66-0C6F0CE6-0CEF0D66-0D6F0E50-0E590ED0-0ED90F20-0F291040-10491090-109917E0-17E91810-18191946-194F19D0-19DA1A80-1A891A90-1A991B50-1B591BB0-1BB91C40-1C491C50-1C59A620-A629A8D0-A8D9A900-A909A9D0-A9D9AA50-AA59ABF0-ABF9FF10-FF19",Nl:"16EE-16F02160-21822185-218830073021-30293038-303AA6E6-A6EF",No:"00B200B300B900BC-00BE09F4-09F90BF0-0BF20C78-0C7E0D70-0D750F2A-0F331369-137C17F0-17F920702074-20792080-20892150-215F21892460-249B24EA-24FF2776-27932CFD3192-31953220-32293251-325F3280-328932B1-32BFA830-A835",P:"0021-00230025-002A002C-002F003A003B003F0040005B-005D005F007B007D00A100AB00B700BB00BF037E0387055A-055F0589058A05BE05C005C305C605F305F40609060A060C060D061B061E061F066A-066D06D40700-070D07F7-07F90830-083E0964096509700DF40E4F0E5A0E5B0F04-0F120F3A-0F3D0F850FD0-0FD4104A-104F10FB1361-13681400166D166E169B169C16EB-16ED1735173617D4-17D617D8-17DA1800-180A1944194519DE19DF1A1E1A1F1AA0-1AA61AA8-1AAD1B5A-1B601C3B-1C3F1C7E1C7F1CD32010-20272030-20432045-20512053-205E207D207E208D208E2329232A2768-277527C527C627E6-27EF2983-299829D8-29DB29FC29FD2CF9-2CFC2CFE2CFF2E00-2E2E2E302E313001-30033008-30113014-301F3030303D30A030FBA4FEA4FFA60D-A60FA673A67EA6F2-A6F7A874-A877A8CEA8CFA8F8-A8FAA92EA92FA95FA9C1-A9CDA9DEA9DFAA5C-AA5FAADEAADFABEBFD3EFD3FFE10-FE19FE30-FE52FE54-FE61FE63FE68FE6AFE6BFF01-FF03FF05-FF0AFF0C-FF0FFF1AFF1BFF1FFF20FF3B-FF3DFF3FFF5BFF5DFF5F-FF65",Pd:"002D058A05BE140018062010-20152E172E1A301C303030A0FE31FE32FE58FE63FF0D",Ps:"0028005B007B0F3A0F3C169B201A201E2045207D208D23292768276A276C276E27702772277427C527E627E827EA27EC27EE2983298529872989298B298D298F299129932995299729D829DA29FC2E222E242E262E283008300A300C300E3010301430163018301A301DFD3EFE17FE35FE37FE39FE3BFE3DFE3FFE41FE43FE47FE59FE5BFE5DFF08FF3BFF5BFF5FFF62",Pe:"0029005D007D0F3B0F3D169C2046207E208E232A2769276B276D276F27712773277527C627E727E927EB27ED27EF298429862988298A298C298E2990299229942996299829D929DB29FD2E232E252E272E293009300B300D300F3011301530173019301B301E301FFD3FFE18FE36FE38FE3AFE3CFE3EFE40FE42FE44FE48FE5AFE5CFE5EFF09FF3DFF5DFF60FF63",Pi:"00AB2018201B201C201F20392E022E042E092E0C2E1C2E20",Pf:"00BB2019201D203A2E032E052E0A2E0D2E1D2E21",Pc:"005F203F20402054FE33FE34FE4D-FE4FFF3F",Po:"0021-00230025-0027002A002C002E002F003A003B003F0040005C00A100B700BF037E0387055A-055F058905C005C305C605F305F40609060A060C060D061B061E061F066A-066D06D40700-070D07F7-07F90830-083E0964096509700DF40E4F0E5A0E5B0F04-0F120F850FD0-0FD4104A-104F10FB1361-1368166D166E16EB-16ED1735173617D4-17D617D8-17DA1800-18051807-180A1944194519DE19DF1A1E1A1F1AA0-1AA61AA8-1AAD1B5A-1B601C3B-1C3F1C7E1C7F1CD3201620172020-20272030-2038203B-203E2041-20432047-205120532055-205E2CF9-2CFC2CFE2CFF2E002E012E06-2E082E0B2E0E-2E162E182E192E1B2E1E2E1F2E2A-2E2E2E302E313001-3003303D30FBA4FEA4FFA60D-A60FA673A67EA6F2-A6F7A874-A877A8CEA8CFA8F8-A8FAA92EA92FA95FA9C1-A9CDA9DEA9DFAA5C-AA5FAADEAADFABEBFE10-FE16FE19FE30FE45FE46FE49-FE4CFE50-FE52FE54-FE57FE5F-FE61FE68FE6AFE6BFF01-FF03FF05-FF07FF0AFF0CFF0EFF0FFF1AFF1BFF1FFF20FF3CFF61FF64FF65",S:"0024002B003C-003E005E0060007C007E00A2-00A900AC00AE-00B100B400B600B800D700F702C2-02C502D2-02DF02E5-02EB02ED02EF-02FF03750384038503F604820606-0608060B060E060F06E906FD06FE07F609F209F309FA09FB0AF10B700BF3-0BFA0C7F0CF10CF20D790E3F0F01-0F030F13-0F170F1A-0F1F0F340F360F380FBE-0FC50FC7-0FCC0FCE0FCF0FD5-0FD8109E109F13601390-139917DB194019E0-19FF1B61-1B6A1B74-1B7C1FBD1FBF-1FC11FCD-1FCF1FDD-1FDF1FED-1FEF1FFD1FFE20442052207A-207C208A-208C20A0-20B8210021012103-21062108210921142116-2118211E-2123212521272129212E213A213B2140-2144214A-214D214F2190-2328232B-23E82400-24262440-244A249C-24E92500-26CD26CF-26E126E326E8-26FF2701-27042706-2709270C-27272729-274B274D274F-27522756-275E2761-276727942798-27AF27B1-27BE27C0-27C427C7-27CA27CC27D0-27E527F0-29822999-29D729DC-29FB29FE-2B4C2B50-2B592CE5-2CEA2E80-2E992E9B-2EF32F00-2FD52FF0-2FFB300430123013302030363037303E303F309B309C319031913196-319F31C0-31E33200-321E322A-32503260-327F328A-32B032C0-32FE3300-33FF4DC0-4DFFA490-A4C6A700-A716A720A721A789A78AA828-A82BA836-A839AA77-AA79FB29FDFCFDFDFE62FE64-FE66FE69FF04FF0BFF1C-FF1EFF3EFF40FF5CFF5EFFE0-FFE6FFE8-FFEEFFFCFFFD",Sm:"002B003C-003E007C007E00AC00B100D700F703F60606-060820442052207A-207C208A-208C2140-2144214B2190-2194219A219B21A021A321A621AE21CE21CF21D221D421F4-22FF2308-230B23202321237C239B-23B323DC-23E125B725C125F8-25FF266F27C0-27C427C7-27CA27CC27D0-27E527F0-27FF2900-29822999-29D729DC-29FB29FE-2AFF2B30-2B442B47-2B4CFB29FE62FE64-FE66FF0BFF1C-FF1EFF5CFF5EFFE2FFE9-FFEC",Sc:"002400A2-00A5060B09F209F309FB0AF10BF90E3F17DB20A0-20B8A838FDFCFE69FF04FFE0FFE1FFE5FFE6",Sk:"005E006000A800AF00B400B802C2-02C502D2-02DF02E5-02EB02ED02EF-02FF0375038403851FBD1FBF-1FC11FCD-1FCF1FDD-1FDF1FED-1FEF1FFD1FFE309B309CA700-A716A720A721A789A78AFF3EFF40FFE3",So:"00A600A700A900AE00B000B60482060E060F06E906FD06FE07F609FA0B700BF3-0BF80BFA0C7F0CF10CF20D790F01-0F030F13-0F170F1A-0F1F0F340F360F380FBE-0FC50FC7-0FCC0FCE0FCF0FD5-0FD8109E109F13601390-1399194019E0-19FF1B61-1B6A1B74-1B7C210021012103-21062108210921142116-2118211E-2123212521272129212E213A213B214A214C214D214F2195-2199219C-219F21A121A221A421A521A7-21AD21AF-21CD21D021D121D321D5-21F32300-2307230C-231F2322-2328232B-237B237D-239A23B4-23DB23E2-23E82400-24262440-244A249C-24E92500-25B625B8-25C025C2-25F72600-266E2670-26CD26CF-26E126E326E8-26FF2701-27042706-2709270C-27272729-274B274D274F-27522756-275E2761-276727942798-27AF27B1-27BE2800-28FF2B00-2B2F2B452B462B50-2B592CE5-2CEA2E80-2E992E9B-2EF32F00-2FD52FF0-2FFB300430123013302030363037303E303F319031913196-319F31C0-31E33200-321E322A-32503260-327F328A-32B032C0-32FE3300-33FF4DC0-4DFFA490-A4C6A828-A82BA836A837A839AA77-AA79FDFDFFE4FFE8FFEDFFEEFFFCFFFD",Z:"002000A01680180E2000-200A20282029202F205F3000",Zs:"002000A01680180E2000-200A202F205F3000",Zl:"2028",Zp:"2029",C:"0000-001F007F-009F00AD03780379037F-0383038B038D03A20526-05300557055805600588058B-059005C8-05CF05EB-05EF05F5-0605061C061D0620065F06DD070E070F074B074C07B2-07BF07FB-07FF082E082F083F-08FF093A093B094F095609570973-097809800984098D098E0991099209A909B109B3-09B509BA09BB09C509C609C909CA09CF-09D609D8-09DB09DE09E409E509FC-0A000A040A0B-0A0E0A110A120A290A310A340A370A3A0A3B0A3D0A43-0A460A490A4A0A4E-0A500A52-0A580A5D0A5F-0A650A76-0A800A840A8E0A920AA90AB10AB40ABA0ABB0AC60ACA0ACE0ACF0AD1-0ADF0AE40AE50AF00AF2-0B000B040B0D0B0E0B110B120B290B310B340B3A0B3B0B450B460B490B4A0B4E-0B550B58-0B5B0B5E0B640B650B72-0B810B840B8B-0B8D0B910B96-0B980B9B0B9D0BA0-0BA20BA5-0BA70BAB-0BAD0BBA-0BBD0BC3-0BC50BC90BCE0BCF0BD1-0BD60BD8-0BE50BFB-0C000C040C0D0C110C290C340C3A-0C3C0C450C490C4E-0C540C570C5A-0C5F0C640C650C70-0C770C800C810C840C8D0C910CA90CB40CBA0CBB0CC50CC90CCE-0CD40CD7-0CDD0CDF0CE40CE50CF00CF3-0D010D040D0D0D110D290D3A-0D3C0D450D490D4E-0D560D58-0D5F0D640D650D76-0D780D800D810D840D97-0D990DB20DBC0DBE0DBF0DC7-0DC90DCB-0DCE0DD50DD70DE0-0DF10DF5-0E000E3B-0E3E0E5C-0E800E830E850E860E890E8B0E8C0E8E-0E930E980EA00EA40EA60EA80EA90EAC0EBA0EBE0EBF0EC50EC70ECE0ECF0EDA0EDB0EDE-0EFF0F480F6D-0F700F8C-0F8F0F980FBD0FCD0FD9-0FFF10C6-10CF10FD-10FF1249124E124F12571259125E125F1289128E128F12B112B612B712BF12C112C612C712D7131113161317135B-135E137D-137F139A-139F13F5-13FF169D-169F16F1-16FF170D1715-171F1737-173F1754-175F176D17711774-177F17B417B517DE17DF17EA-17EF17FA-17FF180F181A-181F1878-187F18AB-18AF18F6-18FF191D-191F192C-192F193C-193F1941-1943196E196F1975-197F19AC-19AF19CA-19CF19DB-19DD1A1C1A1D1A5F1A7D1A7E1A8A-1A8F1A9A-1A9F1AAE-1AFF1B4C-1B4F1B7D-1B7F1BAB-1BAD1BBA-1BFF1C38-1C3A1C4A-1C4C1C80-1CCF1CF3-1CFF1DE7-1DFC1F161F171F1E1F1F1F461F471F4E1F4F1F581F5A1F5C1F5E1F7E1F7F1FB51FC51FD41FD51FDC1FF01FF11FF51FFF200B-200F202A-202E2060-206F20722073208F2095-209F20B9-20CF20F1-20FF218A-218F23E9-23FF2427-243F244B-245F26CE26E226E4-26E727002705270A270B2728274C274E2753-2755275F27602795-279727B027BF27CB27CD-27CF2B4D-2B4F2B5A-2BFF2C2F2C5F2CF2-2CF82D26-2D2F2D66-2D6E2D70-2D7F2D97-2D9F2DA72DAF2DB72DBF2DC72DCF2DD72DDF2E32-2E7F2E9A2EF4-2EFF2FD6-2FEF2FFC-2FFF3040309730983100-3104312E-3130318F31B8-31BF31E4-31EF321F32FF4DB6-4DBF9FCC-9FFFA48D-A48FA4C7-A4CFA62C-A63FA660A661A674-A67BA698-A69FA6F8-A6FFA78D-A7FAA82C-A82FA83A-A83FA878-A87FA8C5-A8CDA8DA-A8DFA8FC-A8FFA954-A95EA97D-A97FA9CEA9DA-A9DDA9E0-A9FFAA37-AA3FAA4EAA4FAA5AAA5BAA7C-AA7FAAC3-AADAAAE0-ABBFABEEABEFABFA-ABFFD7A4-D7AFD7C7-D7CAD7FC-F8FFFA2EFA2FFA6EFA6FFADA-FAFFFB07-FB12FB18-FB1CFB37FB3DFB3FFB42FB45FBB2-FBD2FD40-FD4FFD90FD91FDC8-FDEFFDFEFDFFFE1A-FE1FFE27-FE2FFE53FE67FE6C-FE6FFE75FEFD-FF00FFBF-FFC1FFC8FFC9FFD0FFD1FFD8FFD9FFDD-FFDFFFE7FFEF-FFFBFFFEFFFF",Cc:"0000-001F007F-009F",Cf:"00AD0600-060306DD070F17B417B5200B-200F202A-202E2060-2064206A-206FFEFFFFF9-FFFB",Co:"E000-F8FF",Cs:"D800-DFFF",Cn:"03780379037F-0383038B038D03A20526-05300557055805600588058B-059005C8-05CF05EB-05EF05F5-05FF06040605061C061D0620065F070E074B074C07B2-07BF07FB-07FF082E082F083F-08FF093A093B094F095609570973-097809800984098D098E0991099209A909B109B3-09B509BA09BB09C509C609C909CA09CF-09D609D8-09DB09DE09E409E509FC-0A000A040A0B-0A0E0A110A120A290A310A340A370A3A0A3B0A3D0A43-0A460A490A4A0A4E-0A500A52-0A580A5D0A5F-0A650A76-0A800A840A8E0A920AA90AB10AB40ABA0ABB0AC60ACA0ACE0ACF0AD1-0ADF0AE40AE50AF00AF2-0B000B040B0D0B0E0B110B120B290B310B340B3A0B3B0B450B460B490B4A0B4E-0B550B58-0B5B0B5E0B640B650B72-0B810B840B8B-0B8D0B910B96-0B980B9B0B9D0BA0-0BA20BA5-0BA70BAB-0BAD0BBA-0BBD0BC3-0BC50BC90BCE0BCF0BD1-0BD60BD8-0BE50BFB-0C000C040C0D0C110C290C340C3A-0C3C0C450C490C4E-0C540C570C5A-0C5F0C640C650C70-0C770C800C810C840C8D0C910CA90CB40CBA0CBB0CC50CC90CCE-0CD40CD7-0CDD0CDF0CE40CE50CF00CF3-0D010D040D0D0D110D290D3A-0D3C0D450D490D4E-0D560D58-0D5F0D640D650D76-0D780D800D810D840D97-0D990DB20DBC0DBE0DBF0DC7-0DC90DCB-0DCE0DD50DD70DE0-0DF10DF5-0E000E3B-0E3E0E5C-0E800E830E850E860E890E8B0E8C0E8E-0E930E980EA00EA40EA60EA80EA90EAC0EBA0EBE0EBF0EC50EC70ECE0ECF0EDA0EDB0EDE-0EFF0F480F6D-0F700F8C-0F8F0F980FBD0FCD0FD9-0FFF10C6-10CF10FD-10FF1249124E124F12571259125E125F1289128E128F12B112B612B712BF12C112C612C712D7131113161317135B-135E137D-137F139A-139F13F5-13FF169D-169F16F1-16FF170D1715-171F1737-173F1754-175F176D17711774-177F17DE17DF17EA-17EF17FA-17FF180F181A-181F1878-187F18AB-18AF18F6-18FF191D-191F192C-192F193C-193F1941-1943196E196F1975-197F19AC-19AF19CA-19CF19DB-19DD1A1C1A1D1A5F1A7D1A7E1A8A-1A8F1A9A-1A9F1AAE-1AFF1B4C-1B4F1B7D-1B7F1BAB-1BAD1BBA-1BFF1C38-1C3A1C4A-1C4C1C80-1CCF1CF3-1CFF1DE7-1DFC1F161F171F1E1F1F1F461F471F4E1F4F1F581F5A1F5C1F5E1F7E1F7F1FB51FC51FD41FD51FDC1FF01FF11FF51FFF2065-206920722073208F2095-209F20B9-20CF20F1-20FF218A-218F23E9-23FF2427-243F244B-245F26CE26E226E4-26E727002705270A270B2728274C274E2753-2755275F27602795-279727B027BF27CB27CD-27CF2B4D-2B4F2B5A-2BFF2C2F2C5F2CF2-2CF82D26-2D2F2D66-2D6E2D70-2D7F2D97-2D9F2DA72DAF2DB72DBF2DC72DCF2DD72DDF2E32-2E7F2E9A2EF4-2EFF2FD6-2FEF2FFC-2FFF3040309730983100-3104312E-3130318F31B8-31BF31E4-31EF321F32FF4DB6-4DBF9FCC-9FFFA48D-A48FA4C7-A4CFA62C-A63FA660A661A674-A67BA698-A69FA6F8-A6FFA78D-A7FAA82C-A82FA83A-A83FA878-A87FA8C5-A8CDA8DA-A8DFA8FC-A8FFA954-A95EA97D-A97FA9CEA9DA-A9DDA9E0-A9FFAA37-AA3FAA4EAA4FAA5AAA5BAA7C-AA7FAAC3-AADAAAE0-ABBFABEEABEFABFA-ABFFD7A4-D7AFD7C7-D7CAD7FC-D7FFFA2EFA2FFA6EFA6FFADA-FAFFFB07-FB12FB18-FB1CFB37FB3DFB3FFB42FB45FBB2-FBD2FD40-FD4FFD90FD91FDC8-FDEFFDFEFDFFFE1A-FE1FFE27-FE2FFE53FE67FE6C-FE6FFE75FEFDFEFEFF00FFBF-FFC1FFC8FFC9FFD0FFD1FFD8FFD9FFDD-FFDFFFE7FFEF-FFF8FFFEFFFF"})}),define("ace/mode/text",["require","exports","module","ace/tokenizer","ace/mode/text_highlight_rules","ace/mode/behaviour/cstyle","ace/unicode","ace/lib/lang","ace/token_iterator","ace/range"],function(e,t,n){"use strict";var r=e("../tokenizer").Tokenizer,i=e("./text_highlight_rules").TextHighlightRules,s=e("./behaviour/cstyle").CstyleBehaviour,o=e("../unicode"),u=e("../lib/lang"),a=e("../token_iterator").TokenIterator,f=e("../range").Range,l=function(){this.HighlightRules=i};(function(){this.$defaultBehaviour=new s,this.tokenRe=new RegExp("^["+o.packages.L+o.packages.Mn+o.packages.Mc+o.packages.Nd+o.packages.Pc+"\\$_]+","g"),this.nonTokenRe=new RegExp("^(?:[^"+o.packages.L+o.packages.Mn+o.packages.Mc+o.packages.Nd+o.packages.Pc+"\\$_]|\\s])+","g"),this.getTokenizer=function(){return this.$tokenizer||(this.$highlightRules=this.$highlightRules||new this.HighlightRules(this.$highlightRuleConfig),this.$tokenizer=new r(this.$highlightRules.getRules())),this.$tokenizer},this.lineCommentStart="",this.blockComment="",this.toggleCommentLines=function(e,t,n,r){function w(e){for(var t=n;t<=r;t++)e(i.getLine(t),t)}var i=t.doc,s=!0,o=!0,a=Infinity,f=t.getTabSize(),l=!1;if(!this.lineCommentStart){if(!this.blockComment)return!1;var c=this.blockComment.start,h=this.blockComment.end,p=new RegExp("^(\\s*)(?:"+u.escapeRegExp(c)+")"),d=new RegExp("(?:"+u.escapeRegExp(h)+")\\s*$"),v=function(e,t){if(g(e,t))return;if(!s||/\S/.test(e))i.insertInLine({row:t,column:e.length},h),i.insertInLine({row:t,column:a},c)},m=function(e,t){var n;(n=e.match(d))&&i.removeInLine(t,e.length-n[0].length,e.length),(n=e.match(p))&&i.removeInLine(t,n[1].length,n[0].length)},g=function(e,n){if(p.test(e))return!0;var r=t.getTokens(n);for(var i=0;i<r.length;i++)if(r[i].type==="comment")return!0}}else{if(Array.isArray(this.lineCommentStart))var p=this.lineCommentStart.map(u.escapeRegExp).join("|"),c=this.lineCommentStart[0];else var p=u.escapeRegExp(this.lineCommentStart),c=this.lineCommentStart;p=new RegExp("^(\\s*)(?:"+p+") ?"),l=t.getUseSoftTabs();var m=function(e,t){var n=e.match(p);if(!n)return;var r=n[1].length,s=n[0].length;!b(e,r,s)&&n[0][s-1]==" "&&s--,i.removeInLine(t,r,s)},y=c+" ",v=function(e,t){if(!s||/\S/.test(e))b(e,a,a)?i.insertInLine({row:t,column:a},y):i.insertInLine({row:t,column:a},c)},g=function(e,t){return p.test(e)},b=function(e,t,n){var r=0;while(t--&&e.charAt(t)==" ")r++;if(r%f!=0)return!1;var r=0;while(e.charAt(n++)==" ")r++;return f>2?r%f!=f-1:r%f==0}}var E=Infinity;w(function(e,t){var n=e.search(/\S/);n!==-1?(n<a&&(a=n),o&&!g(e,t)&&(o=!1)):E>e.length&&(E=e.length)}),a==Infinity&&(a=E,s=!1,o=!1),l&&a%f!=0&&(a=Math.floor(a/f)*f),w(o?m:v)},this.toggleBlockComment=function(e,t,n,r){var i=this.blockComment;if(!i)return;!i.start&&i[0]&&(i=i[0]);var s=new a(t,r.row,r.column),o=s.getCurrentToken(),u=t.selection,l=t.selection.toOrientedRange(),c,h;if(o&&/comment/.test(o.type)){var p,d;while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.start);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;p=new f(m,g,m,g+i.start.length);break}o=s.stepBackward()}var s=new a(t,r.row,r.column),o=s.getCurrentToken();while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.end);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;d=new f(m,g,m,g+i.end.length);break}o=s.stepForward()}d&&t.remove(d),p&&(t.remove(p),c=p.start.row,h=-i.start.length)}else h=i.start.length,c=n.start.row,t.insert(n.end,i.end),t.insert(n.start,i.start);l.start.row==c&&(l.start.column+=h),l.end.row==c&&(l.end.column+=h),t.selection.fromOrientedRange(l)},this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.autoOutdent=function(e,t,n){},this.$getIndent=function(e){return e.match(/^\s*/)[0]},this.createWorker=function(e){return null},this.createModeDelegates=function(e){this.$embeds=[],this.$modes={};for(var t in e)e[t]&&(this.$embeds.push(t),this.$modes[t]=new e[t]);var n=["toggleBlockComment","toggleCommentLines","getNextLineIndent","checkOutdent","autoOutdent","transformAction","getCompletions"];for(var t=0;t<n.length;t++)(function(e){var r=n[t],i=e[r];e[n[t]]=function(){return this.$delegator(r,arguments,i)}})(this)},this.$delegator=function(e,t,n){var r=t[0];typeof r!="string"&&(r=r[0]);for(var i=0;i<this.$embeds.length;i++){if(!this.$modes[this.$embeds[i]])continue;var s=r.split(this.$embeds[i]);if(!s[0]&&s[1]){t[0]=s[1];var o=this.$modes[this.$embeds[i]];return o[e].apply(o,t)}}var u=n.apply(this,t);return n?u:undefined},this.transformAction=function(e,t,n,r,i){if(this.$behaviour){var s=this.$behaviour.getBehaviours();for(var o in s)if(s[o][t]){var u=s[o][t].apply(this,arguments);if(u)return u}}},this.getKeywords=function(e){if(!this.completionKeywords){var t=this.$tokenizer.rules,n=[];for(var r in t){var i=t[r];for(var s=0,o=i.length;s<o;s++)if(typeof i[s].token=="string")/keyword|support|storage/.test(i[s].token)&&n.push(i[s].regex);else if(typeof i[s].token=="object")for(var u=0,a=i[s].token.length;u<a;u++)if(/keyword|support|storage/.test(i[s].token[u])){var r=i[s].regex.match(/\(.+?\)/g)[u];n.push(r.substr(1,r.length-2))}}this.completionKeywords=n}return e?n.concat(this.$keywordList||[]):this.$keywordList},this.$createKeywordList=function(){return this.$highlightRules||this.getTokenizer(),this.$keywordList=this.$highlightRules.$keywordList||[]},this.getCompletions=function(e,t,n,r){var i=this.$keywordList||this.$createKeywordList();return i.map(function(e){return{name:e,value:e,score:0,meta:"keyword"}})},this.$id="ace/mode/text"}).call(l.prototype),t.Mode=l}),define("ace/apply_delta",["require","exports","module"],function(e,t,n){"use strict";function r(e,t){throw console.log("Invalid Delta:",e),"Invalid Delta: "+t}function i(e,t){return t.row>=0&&t.row<e.length&&t.column>=0&&t.column<=e[t.row].length}function s(e,t){t.action!="insert"&&t.action!="remove"&&r(t,"delta.action must be 'insert' or 'remove'"),t.lines instanceof Array||r(t,"delta.lines must be an Array"),(!t.start||!t.end)&&r(t,"delta.start/end must be an present");var n=t.start;i(e,t.start)||r(t,"delta.start must be contained in document");var s=t.end;t.action=="remove"&&!i(e,s)&&r(t,"delta.end must contained in document for 'remove' actions");var o=s.row-n.row,u=s.column-(o==0?n.column:0);(o!=t.lines.length-1||t.lines[o].length!=u)&&r(t,"delta.range must match delta lines")}t.applyDelta=function(e,t,n){var r=t.start.row,i=t.start.column,s=e[r]||"";switch(t.action){case"insert":var o=t.lines;if(o.length===1)e[r]=s.substring(0,i)+t.lines[0]+s.substring(i);else{var u=[r,1].concat(t.lines);e.splice.apply(e,u),e[r]=s.substring(0,i)+e[r],e[r+t.lines.length-1]+=s.substring(i)}break;case"remove":var a=t.end.column,f=t.end.row;r===f?e[r]=s.substring(0,i)+s.substring(a):e.splice(r,f-r+1,s.substring(0,i)+e[f].substring(a))}}}),define("ace/anchor",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/event_emitter").EventEmitter,s=t.Anchor=function(e,t,n){this.$onChange=this.onChange.bind(this),this.attach(e),typeof n=="undefined"?this.setPosition(t.row,t.column):this.setPosition(t,n)};(function(){function e(e,t,n){var r=n?e.column<=t.column:e.column<t.column;return e.row<t.row||e.row==t.row&&r}function t(t,n,r){var i=t.action=="insert",s=(i?1:-1)*(t.end.row-t.start.row),o=(i?1:-1)*(t.end.column-t.start.column),u=t.start,a=i?u:t.end;return e(n,u,r)?{row:n.row,column:n.column}:e(a,n,!r)?{row:n.row+s,column:n.column+(n.row==a.row?o:0)}:{row:u.row,column:u.column}}r.implement(this,i),this.getPosition=function(){return this.$clipPositionToDocument(this.row,this.column)},this.getDocument=function(){return this.document},this.$insertRight=!1,this.onChange=function(e){if(e.start.row==e.end.row&&e.start.row!=this.row)return;if(e.start.row>this.row)return;var n=t(e,{row:this.row,column:this.column},this.$insertRight);this.setPosition(n.row,n.column,!0)},this.setPosition=function(e,t,n){var r;n?r={row:e,column:t}:r=this.$clipPositionToDocument(e,t);if(this.row==r.row&&this.column==r.column)return;var i={row:this.row,column:this.column};this.row=r.row,this.column=r.column,this._signal("change",{old:i,value:r})},this.detach=function(){this.document.removeEventListener("change",this.$onChange)},this.attach=function(e){this.document=e||this.document,this.document.on("change",this.$onChange)},this.$clipPositionToDocument=function(e,t){var n={};return e>=this.document.getLength()?(n.row=Math.max(0,this.document.getLength()-1),n.column=this.document.getLine(n.row).length):e<0?(n.row=0,n.column=0):(n.row=e,n.column=Math.min(this.document.getLine(n.row).length,Math.max(0,t))),t<0&&(n.column=0),n}}).call(s.prototype)}),define("ace/document",["require","exports","module","ace/lib/oop","ace/apply_delta","ace/lib/event_emitter","ace/range","ace/anchor"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./apply_delta").applyDelta,s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=e("./anchor").Anchor,a=function(e){this.$lines=[""],e.length===0?this.$lines=[""]:Array.isArray(e)?this.insertMergedLines({row:0,column:0},e):this.insert({row:0,column:0},e)};(function(){r.implement(this,s),this.setValue=function(e){var t=this.getLength()-1;this.remove(new o(0,0,t,this.getLine(t).length)),this.insert({row:0,column:0},e)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(e,t){return new u(this,e,t)},"aaa".split(/a/).length===0?this.$split=function(e){return e.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(e){return e.split(/\r\n|\r|\n/)},this.$detectNewLine=function(e){var t=e.match(/^.*?(\r\n|\r|\n)/m);this.$autoNewLine=t?t[1]:"\n",this._signal("changeNewLineMode")},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";default:return this.$autoNewLine||"\n"}},this.$autoNewLine="",this.$newLineMode="auto",this.setNewLineMode=function(e){if(this.$newLineMode===e)return;this.$newLineMode=e,this._signal("changeNewLineMode")},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(e){return e=="\r\n"||e=="\r"||e=="\n"},this.getLine=function(e){return this.$lines[e]||""},this.getLines=function(e,t){return this.$lines.slice(e,t+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(e){return this.getLinesForRange(e).join(this.getNewLineCharacter())},this.getLinesForRange=function(e){var t;if(e.start.row===e.end.row)t=[this.getLine(e.start.row).substring(e.start.column,e.end.column)];else{t=this.getLines(e.start.row,e.end.row),t[0]=(t[0]||"").substring(e.start.column);var n=t.length-1;e.end.row-e.start.row==n&&(t[n]=t[n].substring(0,e.end.column))}return t},this.insertLines=function(e,t){return console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead."),this.insertFullLines(e,t)},this.removeLines=function(e,t){return console.warn("Use of document.removeLines is deprecated. Use the removeFullLines method instead."),this.removeFullLines(e,t)},this.insertNewLine=function(e){return console.warn("Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead."),this.insertMergedLines(e,["",""])},this.insert=function(e,t){return this.getLength()<=1&&this.$detectNewLine(t),this.insertMergedLines(e,this.$split(t))},this.insertInLine=function(e,t){var n=this.clippedPos(e.row,e.column),r=this.pos(e.row,e.column+t.length);return this.applyDelta({start:n,end:r,action:"insert",lines:[t]},!0),this.clonePos(r)},this.clippedPos=function(e,t){var n=this.getLength();e===undefined?e=n:e<0?e=0:e>=n&&(e=n-1,t=undefined);var r=this.getLine(e);return t==undefined&&(t=r.length),t=Math.min(Math.max(t,0),r.length),{row:e,column:t}},this.clonePos=function(e){return{row:e.row,column:e.column}},this.pos=function(e,t){return{row:e,column:t}},this.$clipPosition=function(e){var t=this.getLength();return e.row>=t?(e.row=Math.max(0,t-1),e.column=this.getLine(t-1).length):(e.row=Math.max(0,e.row),e.column=Math.min(Math.max(e.column,0),this.getLine(e.row).length)),e},this.insertFullLines=function(e,t){e=Math.min(Math.max(e,0),this.getLength());var n=0;e<this.getLength()?(t=t.concat([""]),n=0):(t=[""].concat(t),e--,n=this.$lines[e].length),this.insertMergedLines({row:e,column:n},t)},this.insertMergedLines=function(e,t){var n=this.clippedPos(e.row,e.column),r={row:n.row+t.length-1,column:(t.length==1?n.column:0)+t[t.length-1].length};return this.applyDelta({start:n,end:r,action:"insert",lines:t}),this.clonePos(r)},this.remove=function(e){var t=this.clippedPos(e.start.row,e.start.column),n=this.clippedPos(e.end.row,e.end.column);return this.applyDelta({start:t,end:n,action:"remove",lines:this.getLinesForRange({start:t,end:n})}),this.clonePos(t)},this.removeInLine=function(e,t,n){var r=this.clippedPos(e,t),i=this.clippedPos(e,n);return this.applyDelta({start:r,end:i,action:"remove",lines:this.getLinesForRange({start:r,end:i})},!0),this.clonePos(r)},this.removeFullLines=function(e,t){e=Math.min(Math.max(0,e),this.getLength()-1),t=Math.min(Math.max(0,t),this.getLength()-1);var n=t==this.getLength()-1&&e>0,r=t<this.getLength()-1,i=n?e-1:e,s=n?this.getLine(i).length:0,u=r?t+1:t,a=r?0:this.getLine(u).length,f=new o(i,s,u,a),l=this.$lines.slice(e,t+1);return this.applyDelta({start:f.start,end:f.end,action:"remove",lines:this.getLinesForRange(f)}),l},this.removeNewLine=function(e){e<this.getLength()-1&&e>=0&&this.applyDelta({start:this.pos(e,this.getLine(e).length),end:this.pos(e+1,0),action:"remove",lines:["",""]})},this.replace=function(e,t){e instanceof o||(e=o.fromPoints(e.start,e.end));if(t.length===0&&e.isEmpty())return e.start;if(t==this.getTextRange(e))return e.end;this.remove(e);var n;return t?n=this.insert(e.start,t):n=e.start,n},this.applyDeltas=function(e){for(var t=0;t<e.length;t++)this.applyDelta(e[t])},this.revertDeltas=function(e){for(var t=e.length-1;t>=0;t--)this.revertDelta(e[t])},this.applyDelta=function(e,t){var n=e.action=="insert";if(n?e.lines.length<=1&&!e.lines[0]:!o.comparePoints(e.start,e.end))return;n&&e.lines.length>2e4&&this.$splitAndapplyLargeDelta(e,2e4),i(this.$lines,e,t),this._signal("change",e)},this.$splitAndapplyLargeDelta=function(e,t){var n=e.lines,r=n.length,i=e.start.row,s=e.start.column,o=0,u=0;do{o=u,u+=t-1;var a=n.slice(o,u);if(u>r){e.lines=a,e.start.row=i+o,e.start.column=s;break}a.push(""),this.applyDelta({start:this.pos(i+o,s),end:this.pos(i+u,s=0),action:e.action,lines:a},!0)}while(!0)},this.revertDelta=function(e){this.applyDelta({start:this.clonePos(e.start),end:this.clonePos(e.end),action:e.action=="insert"?"remove":"insert",lines:e.lines.slice()})},this.indexToPosition=function(e,t){var n=this.$lines||this.getAllLines(),r=this.getNewLineCharacter().length;for(var i=t||0,s=n.length;i<s;i++){e-=n[i].length+r;if(e<0)return{row:i,column:e+n[i].length+r}}return{row:s-1,column:n[s-1].length}},this.positionToIndex=function(e,t){var n=this.$lines||this.getAllLines(),r=this.getNewLineCharacter().length,i=0,s=Math.min(e.row,n.length);for(var o=t||0;o<s;++o)i+=n[o].length+r;return i+e.column}}).call(a.prototype),t.Document=a}),define("ace/background_tokenizer",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/event_emitter").EventEmitter,s=function(e,t){this.running=!1,this.lines=[],this.states=[],this.currentLine=0,this.tokenizer=e;var n=this;this.$worker=function(){if(!n.running)return;var e=new Date,t=n.currentLine,r=-1,i=n.doc,s=t;while(n.lines[t])t++;var o=i.getLength(),u=0;n.running=!1;while(t<o){n.$tokenizeRow(t),r=t;do t++;while(n.lines[t]);u++;if(u%5===0&&new Date-e>20){n.running=setTimeout(n.$worker,20);break}}n.currentLine=t,r==-1&&(r=t),s<=r&&n.fireUpdateEvent(s,r)}};(function(){r.implement(this,i),this.setTokenizer=function(e){this.tokenizer=e,this.lines=[],this.states=[],this.start(0)},this.setDocument=function(e){this.doc=e,this.lines=[],this.states=[],this.stop()},this.fireUpdateEvent=function(e,t){var n={first:e,last:t};this._signal("update",{data:n})},this.start=function(e){this.currentLine=Math.min(e||0,this.currentLine,this.doc.getLength()),this.lines.splice(this.currentLine,this.lines.length),this.states.splice(this.currentLine,this.states.length),this.stop(),this.running=setTimeout(this.$worker,700)},this.scheduleStart=function(){this.running||(this.running=setTimeout(this.$worker,700))},this.$updateOnChange=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.lines[t]=null;else if(e.action=="remove")this.lines.splice(t,n+1,null),this.states.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.lines.splice.apply(this.lines,r),this.states.splice.apply(this.states,r)}this.currentLine=Math.min(t,this.currentLine,this.doc.getLength()),this.stop()},this.stop=function(){this.running&&clearTimeout(this.running),this.running=!1},this.getTokens=function(e){return this.lines[e]||this.$tokenizeRow(e)},this.getState=function(e){return this.currentLine==e&&this.$tokenizeRow(e),this.states[e]||"start"},this.$tokenizeRow=function(e){var t=this.doc.getLine(e),n=this.states[e-1],r=this.tokenizer.getLineTokens(t,n,e);return this.states[e]+""!=r.state+""?(this.states[e]=r.state,this.lines[e+1]=null,this.currentLine>e+1&&(this.currentLine=e+1)):this.currentLine==e&&(this.currentLine=e+1),this.lines[e]=r.tokens}}).call(s.prototype),t.BackgroundTokenizer=s}),define("ace/search_highlight",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(e,t,n){this.setRegexp(e),this.clazz=t,this.type=n||"text"};(function(){this.MAX_RANGES=500,this.setRegexp=function(e){if(this.regExp+""==e+"")return;this.regExp=e,this.cache=[]},this.update=function(e,t,n,i){if(!this.regExp)return;var o=i.firstRow,u=i.lastRow;for(var a=o;a<=u;a++){var f=this.cache[a];f==null&&(f=r.getMatchOffsets(n.getLine(a),this.regExp),f.length>this.MAX_RANGES&&(f=f.slice(0,this.MAX_RANGES)),f=f.map(function(e){return new s(a,e.offset,a,e.offset+e.length)}),this.cache[a]=f.length?f:"");for(var l=f.length;l--;)t.drawSingleLineMarker(e,f[l].toScreenRange(n),this.clazz,i)}}}).call(o.prototype),t.SearchHighlight=o}),define("ace/edit_session/fold_line",["require","exports","module","ace/range"],function(e,t,n){"use strict";function i(e,t){this.foldData=e,Array.isArray(t)?this.folds=t:t=this.folds=[t];var n=t[t.length-1];this.range=new r(t[0].start.row,t[0].start.column,n.end.row,n.end.column),this.start=this.range.start,this.end=this.range.end,this.folds.forEach(function(e){e.setFoldLine(this)},this)}var r=e("../range").Range;(function(){this.shiftRow=function(e){this.start.row+=e,this.end.row+=e,this.folds.forEach(function(t){t.start.row+=e,t.end.row+=e})},this.addFold=function(e){if(e.sameRow){if(e.start.row<this.startRow||e.endRow>this.endRow)throw new Error("Can't add a fold to this FoldLine as it has no connection");this.folds.push(e),this.folds.sort(function(e,t){return-e.range.compareEnd(t.start.row,t.start.column)}),this.range.compareEnd(e.start.row,e.start.column)>0?(this.end.row=e.end.row,this.end.column=e.end.column):this.range.compareStart(e.end.row,e.end.column)<0&&(this.start.row=e.start.row,this.start.column=e.start.column)}else if(e.start.row==this.end.row)this.folds.push(e),this.end.row=e.end.row,this.end.column=e.end.column;else{if(e.end.row!=this.start.row)throw new Error("Trying to add fold to FoldRow that doesn't have a matching row");this.folds.unshift(e),this.start.row=e.start.row,this.start.column=e.start.column}e.foldLine=this},this.containsRow=function(e){return e>=this.start.row&&e<=this.end.row},this.walk=function(e,t,n){var r=0,i=this.folds,s,o,u,a=!0;t==null&&(t=this.end.row,n=this.end.column);for(var f=0;f<i.length;f++){s=i[f],o=s.range.compareStart(t,n);if(o==-1){e(null,t,n,r,a);return}u=e(null,s.start.row,s.start.column,r,a),u=!u&&e(s.placeholder,s.start.row,s.start.column,r);if(u||o===0)return;a=!s.sameRow,r=s.end.column}e(null,t,n,r,a)},this.getNextFoldTo=function(e,t){var n,r;for(var i=0;i<this.folds.length;i++){n=this.folds[i],r=n.range.compareEnd(e,t);if(r==-1)return{fold:n,kind:"after"};if(r===0)return{fold:n,kind:"inside"}}return null},this.addRemoveChars=function(e,t,n){var r=this.getNextFoldTo(e,t),i,s;if(r){i=r.fold;if(r.kind=="inside"&&i.start.column!=t&&i.start.row!=e)window.console&&window.console.log(e,t,i);else if(i.start.row==e){s=this.folds;var o=s.indexOf(i);o===0&&(this.start.column+=n);for(o;o<s.length;o++){i=s[o],i.start.column+=n;if(!i.sameRow)return;i.end.column+=n}this.end.column+=n}}},this.split=function(e,t){var n=this.getNextFoldTo(e,t);if(!n||n.kind=="inside")return null;var r=n.fold,s=this.folds,o=this.foldData,u=s.indexOf(r),a=s[u-1];this.end.row=a.end.row,this.end.column=a.end.column,s=s.splice(u,s.length-u);var f=new i(o,s);return o.splice(o.indexOf(this)+1,0,f),f},this.merge=function(e){var t=e.folds;for(var n=0;n<t.length;n++)this.addFold(t[n]);var r=this.foldData;r.splice(r.indexOf(e),1)},this.toString=function(){var e=[this.range.toString()+": ["];return this.folds.forEach(function(t){e.push("  "+t.toString())}),e.push("]"),e.join("\n")},this.idxToPosition=function(e){var t=0;for(var n=0;n<this.folds.length;n++){var r=this.folds[n];e-=r.start.column-t;if(e<0)return{row:r.start.row,column:r.start.column+e};e-=r.placeholder.length;if(e<0)return r.start;t=r.end.column}return{row:this.end.row,column:this.end.column+e}}}).call(i.prototype),t.FoldLine=i}),define("ace/range_list",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("./range").Range,i=r.comparePoints,s=function(){this.ranges=[]};(function(){this.comparePoints=i,this.pointIndex=function(e,t,n){var r=this.ranges;for(var s=n||0;s<r.length;s++){var o=r[s],u=i(e,o.end);if(u>0)continue;var a=i(e,o.start);return u===0?t&&a!==0?-s-2:s:a>0||a===0&&!t?s:-s-1}return-s-1},this.add=function(e){var t=!e.isEmpty(),n=this.pointIndex(e.start,t);n<0&&(n=-n-1);var r=this.pointIndex(e.end,t,n);return r<0?r=-r-1:r++,this.ranges.splice(n,r-n,e)},this.addList=function(e){var t=[];for(var n=e.length;n--;)t.push.apply(t,this.add(e[n]));return t},this.substractPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges.splice(t,1)},this.merge=function(){var e=[],t=this.ranges;t=t.sort(function(e,t){return i(e.start,t.start)});var n=t[0],r;for(var s=1;s<t.length;s++){r=n,n=t[s];var o=i(r.end,n.start);if(o<0)continue;if(o==0&&!r.isEmpty()&&!n.isEmpty())continue;i(r.end,n.end)<0&&(r.end.row=n.end.row,r.end.column=n.end.column),t.splice(s,1),e.push(n),n=r,s--}return this.ranges=t,e},this.contains=function(e,t){return this.pointIndex({row:e,column:t})>=0},this.containsPoint=function(e){return this.pointIndex(e)>=0},this.rangeAtPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges[t]},this.clipRows=function(e,t){var n=this.ranges;if(n[0].start.row>t||n[n.length-1].start.row<e)return[];var r=this.pointIndex({row:e,column:0});r<0&&(r=-r-1);var i=this.pointIndex({row:t,column:0},r);i<0&&(i=-i-1);var s=[];for(var o=r;o<i;o++)s.push(n[o]);return s},this.removeAll=function(){return this.ranges.splice(0,this.ranges.length)},this.attach=function(e){this.session&&this.detach(),this.session=e,this.onChange=this.$onChange.bind(this),this.session.on("change",this.onChange)},this.detach=function(){if(!this.session)return;this.session.removeListener("change",this.onChange),this.session=null},this.$onChange=function(e){if(e.action=="insert")var t=e.start,n=e.end;else var n=e.start,t=e.end;var r=t.row,i=n.row,s=i-r,o=-t.column+n.column,u=this.ranges;for(var a=0,f=u.length;a<f;a++){var l=u[a];if(l.end.row<r)continue;if(l.start.row>r)break;l.start.row==r&&l.start.column>=t.column&&(l.start.column!=t.column||!this.$insertRight)&&(l.start.column+=o,l.start.row+=s);if(l.end.row==r&&l.end.column>=t.column){if(l.end.column==t.column&&this.$insertRight)continue;l.end.column==t.column&&o>0&&a<f-1&&l.end.column>l.start.column&&l.end.column==u[a+1].start.column&&(l.end.column-=o),l.end.column+=o,l.end.row+=s}}if(s!=0&&a<f)for(;a<f;a++){var l=u[a];l.start.row+=s,l.end.row+=s}}}).call(s.prototype),t.RangeList=s}),define("ace/edit_session/fold",["require","exports","module","ace/range","ace/range_list","ace/lib/oop"],function(e,t,n){"use strict";function u(e,t){e.row-=t.row,e.row==0&&(e.column-=t.column)}function a(e,t){u(e.start,t),u(e.end,t)}function f(e,t){e.row==0&&(e.column+=t.column),e.row+=t.row}function l(e,t){f(e.start,t),f(e.end,t)}var r=e("../range").Range,i=e("../range_list").RangeList,s=e("../lib/oop"),o=t.Fold=function(e,t){this.foldLine=null,this.placeholder=t,this.range=e,this.start=e.start,this.end=e.end,this.sameRow=e.start.row==e.end.row,this.subFolds=this.ranges=[]};s.inherits(o,i),function(){this.toString=function(){return'"'+this.placeholder+'" '+this.range.toString()},this.setFoldLine=function(e){this.foldLine=e,this.subFolds.forEach(function(t){t.setFoldLine(e)})},this.clone=function(){var e=this.range.clone(),t=new o(e,this.placeholder);return this.subFolds.forEach(function(e){t.subFolds.push(e.clone())}),t.collapseChildren=this.collapseChildren,t},this.addSubFold=function(e){if(this.range.isEqual(e))return;if(!this.range.containsRange(e))throw new Error("A fold can't intersect already existing fold"+e.range+this.range);a(e,this.start);var t=e.start.row,n=e.start.column;for(var r=0,i=-1;r<this.subFolds.length;r++){i=this.subFolds[r].range.compare(t,n);if(i!=1)break}var s=this.subFolds[r];if(i==0)return s.addSubFold(e);var t=e.range.end.row,n=e.range.end.column;for(var o=r,i=-1;o<this.subFolds.length;o++){i=this.subFolds[o].range.compare(t,n);if(i!=1)break}var u=this.subFolds[o];if(i==0)throw new Error("A fold can't intersect already existing fold"+e.range+this.range);var f=this.subFolds.splice(r,o-r,e);return e.setFoldLine(this.foldLine),e},this.restoreRange=function(e){return l(e,this.start)}}.call(o.prototype)}),define("ace/edit_session/folding",["require","exports","module","ace/range","ace/edit_session/fold_line","ace/edit_session/fold","ace/token_iterator"],function(e,t,n){"use strict";function u(){this.getFoldAt=function(e,t,n){var r=this.getFoldLine(e);if(!r)return null;var i=r.folds;for(var s=0;s<i.length;s++){var o=i[s];if(o.range.contains(e,t)){if(n==1&&o.range.isEnd(e,t))continue;if(n==-1&&o.range.isStart(e,t))continue;return o}}},this.getFoldsInRange=function(e){var t=e.start,n=e.end,r=this.$foldData,i=[];t.column+=1,n.column-=1;for(var s=0;s<r.length;s++){var o=r[s].range.compareRange(e);if(o==2)continue;if(o==-2)break;var u=r[s].folds;for(var a=0;a<u.length;a++){var f=u[a];o=f.range.compareRange(e);if(o==-2)break;if(o==2)continue;if(o==42)break;i.push(f)}}return t.column-=1,n.column+=1,i},this.getFoldsInRangeList=function(e){if(Array.isArray(e)){var t=[];e.forEach(function(e){t=t.concat(this.getFoldsInRange(e))},this)}else var t=this.getFoldsInRange(e);return t},this.getAllFolds=function(){var e=[],t=this.$foldData;for(var n=0;n<t.length;n++)for(var r=0;r<t[n].folds.length;r++)e.push(t[n].folds[r]);return e},this.getFoldStringAt=function(e,t,n,r){r=r||this.getFoldLine(e);if(!r)return null;var i={end:{column:0}},s,o;for(var u=0;u<r.folds.length;u++){o=r.folds[u];var a=o.range.compareEnd(e,t);if(a==-1){s=this.getLine(o.start.row).substring(i.end.column,o.start.column);break}if(a===0)return null;i=o}return s||(s=this.getLine(o.start.row).substring(i.end.column)),n==-1?s.substring(0,t-i.end.column):n==1?s.substring(t-i.end.column):s},this.getFoldLine=function(e,t){var n=this.$foldData,r=0;t&&(r=n.indexOf(t)),r==-1&&(r=0);for(r;r<n.length;r++){var i=n[r];if(i.start.row<=e&&i.end.row>=e)return i;if(i.end.row>e)return null}return null},this.getNextFoldLine=function(e,t){var n=this.$foldData,r=0;t&&(r=n.indexOf(t)),r==-1&&(r=0);for(r;r<n.length;r++){var i=n[r];if(i.end.row>=e)return i}return null},this.getFoldedRowCount=function(e,t){var n=this.$foldData,r=t-e+1;for(var i=0;i<n.length;i++){var s=n[i],o=s.end.row,u=s.start.row;if(o>=t){u<t&&(u>=e?r-=t-u:r=0);break}o>=e&&(u>=e?r-=o-u:r-=o-e+1)}return r},this.$addFoldLine=function(e){return this.$foldData.push(e),this.$foldData.sort(function(e,t){return e.start.row-t.start.row}),e},this.addFold=function(e,t){var n=this.$foldData,r=!1,o;e instanceof s?o=e:(o=new s(t,e),o.collapseChildren=t.collapseChildren),this.$clipRangeToDocument(o.range);var u=o.start.row,a=o.start.column,f=o.end.row,l=o.end.column;if(u<f||u==f&&a<=l-2){var c=this.getFoldAt(u,a,1),h=this.getFoldAt(f,l,-1);if(c&&h==c)return c.addSubFold(o);c&&!c.range.isStart(u,a)&&this.removeFold(c),h&&!h.range.isEnd(f,l)&&this.removeFold(h);var p=this.getFoldsInRange(o.range);p.length>0&&(this.removeFolds(p),p.forEach(function(e){o.addSubFold(e)}));for(var d=0;d<n.length;d++){var v=n[d];if(f==v.start.row){v.addFold(o),r=!0;break}if(u==v.end.row){v.addFold(o),r=!0;if(!o.sameRow){var m=n[d+1];if(m&&m.start.row==f){v.merge(m);break}}break}if(f<=v.start.row)break}return r||(v=this.$addFoldLine(new i(this.$foldData,o))),this.$useWrapMode?this.$updateWrapData(v.start.row,v.start.row):this.$updateRowLengthCache(v.start.row,v.start.row),this.$modified=!0,this._signal("changeFold",{data:o,action:"add"}),o}throw new Error("The range has to be at least 2 characters width")},this.addFolds=function(e){e.forEach(function(e){this.addFold(e)},this)},this.removeFold=function(e){var t=e.foldLine,n=t.start.row,r=t.end.row,i=this.$foldData,s=t.folds;if(s.length==1)i.splice(i.indexOf(t),1);else if(t.range.isEnd(e.end.row,e.end.column))s.pop(),t.end.row=s[s.length-1].end.row,t.end.column=s[s.length-1].end.column;else if(t.range.isStart(e.start.row,e.start.column))s.shift(),t.start.row=s[0].start.row,t.start.column=s[0].start.column;else if(e.sameRow)s.splice(s.indexOf(e),1);else{var o=t.split(e.start.row,e.start.column);s=o.folds,s.shift(),o.start.row=s[0].start.row,o.start.column=s[0].start.column}this.$updating||(this.$useWrapMode?this.$updateWrapData(n,r):this.$updateRowLengthCache(n,r)),this.$modified=!0,this._signal("changeFold",{data:e,action:"remove"})},this.removeFolds=function(e){var t=[];for(var n=0;n<e.length;n++)t.push(e[n]);t.forEach(function(e){this.removeFold(e)},this),this.$modified=!0},this.expandFold=function(e){this.removeFold(e),e.subFolds.forEach(function(t){e.restoreRange(t),this.addFold(t)},this),e.collapseChildren>0&&this.foldAll(e.start.row+1,e.end.row,e.collapseChildren-1),e.subFolds=[]},this.expandFolds=function(e){e.forEach(function(e){this.expandFold(e)},this)},this.unfold=function(e,t){var n,i;e==null?(n=new r(0,0,this.getLength(),0),t=!0):typeof e=="number"?n=new r(e,0,e,this.getLine(e).length):"row"in e?n=r.fromPoints(e,e):n=e,i=this.getFoldsInRangeList(n);if(t)this.removeFolds(i);else{var s=i;while(s.length)this.expandFolds(s),s=this.getFoldsInRangeList(n)}if(i.length)return i},this.isRowFolded=function(e,t){return!!this.getFoldLine(e,t)},this.getRowFoldEnd=function(e,t){var n=this.getFoldLine(e,t);return n?n.end.row:e},this.getRowFoldStart=function(e,t){var n=this.getFoldLine(e,t);return n?n.start.row:e},this.getFoldDisplayLine=function(e,t,n,r,i){r==null&&(r=e.start.row),i==null&&(i=0),t==null&&(t=e.end.row),n==null&&(n=this.getLine(t).length);var s=this.doc,o="";return e.walk(function(e,t,n,u){if(t<r)return;if(t==r){if(n<i)return;u=Math.max(i,u)}e!=null?o+=e:o+=s.getLine(t).substring(u,n)},t,n),o},this.getDisplayLine=function(e,t,n,r){var i=this.getFoldLine(e);if(!i){var s;return s=this.doc.getLine(e),s.substring(r||0,t||s.length)}return this.getFoldDisplayLine(i,e,t,n,r)},this.$cloneFoldData=function(){var e=[];return e=this.$foldData.map(function(t){var n=t.folds.map(function(e){return e.clone()});return new i(e,n)}),e},this.toggleFold=function(e){var t=this.selection,n=t.getRange(),r,i;if(n.isEmpty()){var s=n.start;r=this.getFoldAt(s.row,s.column);if(r){this.expandFold(r);return}(i=this.findMatchingBracket(s))?n.comparePoint(i)==1?n.end=i:(n.start=i,n.start.column++,n.end.column--):(i=this.findMatchingBracket({row:s.row,column:s.column+1}))?(n.comparePoint(i)==1?n.end=i:n.start=i,n.start.column++):n=this.getCommentFoldRange(s.row,s.column)||n}else{var o=this.getFoldsInRange(n);if(e&&o.length){this.expandFolds(o);return}o.length==1&&(r=o[0])}r||(r=this.getFoldAt(n.start.row,n.start.column));if(r&&r.range.toString()==n.toString()){this.expandFold(r);return}var u="...";if(!n.isMultiLine()){u=this.getTextRange(n);if(u.length<4)return;u=u.trim().substring(0,2)+".."}this.addFold(u,n)},this.getCommentFoldRange=function(e,t,n){var i=new o(this,e,t),s=i.getCurrentToken(),u=s.type;if(s&&/^comment|string/.test(u)){u=u.match(/comment|string/)[0],u=="comment"&&(u+="|doc-start");var a=new RegExp(u),f=new r;if(n!=1){do s=i.stepBackward();while(s&&a.test(s.type));i.stepForward()}f.start.row=i.getCurrentTokenRow(),f.start.column=i.getCurrentTokenColumn()+2,i=new o(this,e,t);if(n!=-1){var l=-1;do{s=i.stepForward();if(l==-1){var c=this.getState(i.$row);a.test(c)||(l=i.$row)}else if(i.$row>l)break}while(s&&a.test(s.type));s=i.stepBackward()}else s=i.getCurrentToken();return f.end.row=i.getCurrentTokenRow(),f.end.column=i.getCurrentTokenColumn()+s.value.length-2,f}},this.foldAll=function(e,t,n){n==undefined&&(n=1e5);var r=this.foldWidgets;if(!r)return;t=t||this.getLength(),e=e||0;for(var i=e;i<t;i++){r[i]==null&&(r[i]=this.getFoldWidget(i));if(r[i]!="start")continue;var s=this.getFoldWidgetRange(i);if(s&&s.isMultiLine()&&s.end.row<=t&&s.start.row>=e){i=s.end.row;try{var o=this.addFold("...",s);o&&(o.collapseChildren=n)}catch(u){}}}},this.$foldStyles={manual:1,markbegin:1,markbeginend:1},this.$foldStyle="markbegin",this.setFoldStyle=function(e){if(!this.$foldStyles[e])throw new Error("invalid fold style: "+e+"["+Object.keys(this.$foldStyles).join(", ")+"]");if(this.$foldStyle==e)return;this.$foldStyle=e,e=="manual"&&this.unfold();var t=this.$foldMode;this.$setFolding(null),this.$setFolding(t)},this.$setFolding=function(e){if(this.$foldMode==e)return;this.$foldMode=e,this.off("change",this.$updateFoldWidgets),this.off("tokenizerUpdate",this.$tokenizerUpdateFoldWidgets),this._signal("changeAnnotation");if(!e||this.$foldStyle=="manual"){this.foldWidgets=null;return}this.foldWidgets=[],this.getFoldWidget=e.getFoldWidget.bind(e,this,this.$foldStyle),this.getFoldWidgetRange=e.getFoldWidgetRange.bind(e,this,this.$foldStyle),this.$updateFoldWidgets=this.updateFoldWidgets.bind(this),this.$tokenizerUpdateFoldWidgets=this.tokenizerUpdateFoldWidgets.bind(this),this.on("change",this.$updateFoldWidgets),this.on("tokenizerUpdate",this.$tokenizerUpdateFoldWidgets)},this.getParentFoldRangeData=function(e,t){var n=this.foldWidgets;if(!n||t&&n[e])return{};var r=e-1,i;while(r>=0){var s=n[r];s==null&&(s=n[r]=this.getFoldWidget(r));if(s=="start"){var o=this.getFoldWidgetRange(r);i||(i=o);if(o&&o.end.row>=e)break}r--}return{range:r!==-1&&o,firstRange:i}},this.onFoldWidgetClick=function(e,t){t=t.domEvent;var n={children:t.shiftKey,all:t.ctrlKey||t.metaKey,siblings:t.altKey},r=this.$toggleFoldWidget(e,n);if(!r){var i=t.target||t.srcElement;i&&/ace_fold-widget/.test(i.className)&&(i.className+=" ace_invalid")}},this.$toggleFoldWidget=function(e,t){if(!this.getFoldWidget)return;var n=this.getFoldWidget(e),r=this.getLine(e),i=n==="end"?-1:1,s=this.getFoldAt(e,i===-1?0:r.length,i);if(s)return t.children||t.all?this.removeFold(s):this.expandFold(s),s;var o=this.getFoldWidgetRange(e,!0);if(o&&!o.isMultiLine()){s=this.getFoldAt(o.start.row,o.start.column,1);if(s&&o.isEqual(s.range))return this.removeFold(s),s}if(t.siblings){var u=this.getParentFoldRangeData(e);if(u.range)var a=u.range.start.row+1,f=u.range.end.row;this.foldAll(a,f,t.all?1e4:0)}else t.children?(f=o?o.end.row:this.getLength(),this.foldAll(e+1,f,t.all?1e4:0)):o&&(t.all&&(o.collapseChildren=1e4),this.addFold("...",o));return o},this.toggleFoldWidget=function(e){var t=this.selection.getCursor().row;t=this.getRowFoldStart(t);var n=this.$toggleFoldWidget(t,{});if(n)return;var r=this.getParentFoldRangeData(t,!0);n=r.range||r.firstRange;if(n){t=n.start.row;var i=this.getFoldAt(t,this.getLine(t).length,1);i?this.removeFold(i):this.addFold("...",n)}},this.updateFoldWidgets=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.foldWidgets[t]=null;else if(e.action=="remove")this.foldWidgets.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.foldWidgets.splice.apply(this.foldWidgets,r)}},this.tokenizerUpdateFoldWidgets=function(e){var t=e.data;t.first!=t.last&&this.foldWidgets.length>t.first&&this.foldWidgets.splice(t.first,this.foldWidgets.length)}}var r=e("../range").Range,i=e("./fold_line").FoldLine,s=e("./fold").Fold,o=e("../token_iterator").TokenIterator;t.Folding=u}),define("ace/edit_session/bracket_match",["require","exports","module","ace/token_iterator","ace/range"],function(e,t,n){"use strict";function s(){this.findMatchingBracket=function(e,t){if(e.column==0)return null;var n=t||this.getLine(e.row).charAt(e.column-1);if(n=="")return null;var r=n.match(/([\(\[\{])|([\)\]\}])/);return r?r[1]?this.$findClosingBracket(r[1],e):this.$findOpeningBracket(r[2],e):null},this.getBracketRange=function(e){var t=this.getLine(e.row),n=!0,r,s=t.charAt(e.column-1),o=s&&s.match(/([\(\[\{])|([\)\]\}])/);o||(s=t.charAt(e.column),e={row:e.row,column:e.column+1},o=s&&s.match(/([\(\[\{])|([\)\]\}])/),n=!1);if(!o)return null;if(o[1]){var u=this.$findClosingBracket(o[1],e);if(!u)return null;r=i.fromPoints(e,u),n||(r.end.column++,r.start.column--),r.cursor=r.end}else{var u=this.$findOpeningBracket(o[2],e);if(!u)return null;r=i.fromPoints(u,e),n||(r.start.column++,r.end.column--),r.cursor=r.start}return r},this.$brackets={")":"(","(":")","]":"[","[":"]","{":"}","}":"{"},this.$findOpeningBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("rparen",".paren").replace(/\b(?:end)\b/,"(?:start|begin|end)")+")+"));var a=t.column-o.getCurrentTokenColumn()-2,f=u.value;for(;;){while(a>=0){var l=f.charAt(a);if(l==i){s-=1;if(s==0)return{row:o.getCurrentTokenRow(),column:a+o.getCurrentTokenColumn()}}else l==e&&(s+=1);a-=1}do u=o.stepBackward();while(u&&!n.test(u.type));if(u==null)break;f=u.value,a=f.length-1}return null},this.$findClosingBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("lparen",".paren").replace(/\b(?:start|begin)\b/,"(?:start|begin|end)")+")+"));var a=t.column-o.getCurrentTokenColumn();for(;;){var f=u.value,l=f.length;while(a<l){var c=f.charAt(a);if(c==i){s-=1;if(s==0)return{row:o.getCurrentTokenRow(),column:a+o.getCurrentTokenColumn()}}else c==e&&(s+=1);a+=1}do u=o.stepForward();while(u&&!n.test(u.type));if(u==null)break;a=0}return null}}var r=e("../token_iterator").TokenIterator,i=e("../range").Range;t.BracketMatch=s}),define("ace/edit_session",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/config","ace/lib/event_emitter","ace/selection","ace/mode/text","ace/range","ace/document","ace/background_tokenizer","ace/search_highlight","ace/edit_session/folding","ace/edit_session/bracket_match"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/lang"),s=e("./config"),o=e("./lib/event_emitter").EventEmitter,u=e("./selection").Selection,a=e("./mode/text").Mode,f=e("./range").Range,l=e("./document").Document,c=e("./background_tokenizer").BackgroundTokenizer,h=e("./search_highlight").SearchHighlight,p=function(e,t){this.$breakpoints=[],this.$decorations=[],this.$frontMarkers={},this.$backMarkers={},this.$markerId=1,this.$undoSelect=!0,this.$foldData=[],this.id="session"+ ++p.$uid,this.$foldData.toString=function(){return this.join("\n")},this.on("changeFold",this.onChangeFold.bind(this)),this.$onChange=this.onChange.bind(this);if(typeof e!="object"||!e.getLine)e=new l(e);this.setDocument(e),this.selection=new u(this),s.resetOptions(this),this.setMode(t),s._signal("session",this)};p.$uid=0,function(){function m(e){return e<4352?!1:e>=4352&&e<=4447||e>=4515&&e<=4519||e>=4602&&e<=4607||e>=9001&&e<=9002||e>=11904&&e<=11929||e>=11931&&e<=12019||e>=12032&&e<=12245||e>=12272&&e<=12283||e>=12288&&e<=12350||e>=12353&&e<=12438||e>=12441&&e<=12543||e>=12549&&e<=12589||e>=12593&&e<=12686||e>=12688&&e<=12730||e>=12736&&e<=12771||e>=12784&&e<=12830||e>=12832&&e<=12871||e>=12880&&e<=13054||e>=13056&&e<=19903||e>=19968&&e<=42124||e>=42128&&e<=42182||e>=43360&&e<=43388||e>=44032&&e<=55203||e>=55216&&e<=55238||e>=55243&&e<=55291||e>=63744&&e<=64255||e>=65040&&e<=65049||e>=65072&&e<=65106||e>=65108&&e<=65126||e>=65128&&e<=65131||e>=65281&&e<=65376||e>=65504&&e<=65510}r.implement(this,o),this.setDocument=function(e){this.doc&&this.doc.removeListener("change",this.$onChange),this.doc=e,e.on("change",this.$onChange),this.bgTokenizer&&this.bgTokenizer.setDocument(this.getDocument()),this.resetCaches()},this.getDocument=function(){return this.doc},this.$resetRowCache=function(e){if(!e){this.$docRowCache=[],this.$screenRowCache=[];return}var t=this.$docRowCache.length,n=this.$getRowCacheIndex(this.$docRowCache,e)+1;t>n&&(this.$docRowCache.splice(n,t),this.$screenRowCache.splice(n,t))},this.$getRowCacheIndex=function(e,t){var n=0,r=e.length-1;while(n<=r){var i=n+r>>1,s=e[i];if(t>s)n=i+1;else{if(!(t<s))return i;r=i-1}}return n-1},this.resetCaches=function(){this.$modified=!0,this.$wrapData=[],this.$rowLengthCache=[],this.$resetRowCache(0),this.bgTokenizer&&this.bgTokenizer.start(0)},this.onChangeFold=function(e){var t=e.data;this.$resetRowCache(t.start.row)},this.onChange=function(e){this.$modified=!0,this.$resetRowCache(e.start.row);var t=this.$updateInternalDataOnChange(e);!this.$fromUndo&&this.$undoManager&&!e.ignore&&(this.$deltasDoc.push(e),t&&t.length!=0&&this.$deltasFold.push({action:"removeFolds",folds:t}),this.$informUndoManager.schedule()),this.bgTokenizer&&this.bgTokenizer.$updateOnChange(e),this._signal("change",e)},this.setValue=function(e){this.doc.setValue(e),this.selection.moveTo(0,0),this.$resetRowCache(0),this.$deltas=[],this.$deltasDoc=[],this.$deltasFold=[],this.setUndoManager(this.$undoManager),this.getUndoManager().reset()},this.getValue=this.toString=function(){return this.doc.getValue()},this.getSelection=function(){return this.selection},this.getState=function(e){return this.bgTokenizer.getState(e)},this.getTokens=function(e){return this.bgTokenizer.getTokens(e)},this.getTokenAt=function(e,t){var n=this.bgTokenizer.getTokens(e),r,i=0;if(t==null)s=n.length-1,i=this.getLine(e).length;else for(var s=0;s<n.length;s++){i+=n[s].value.length;if(i>=t)break}return r=n[s],r?(r.index=s,r.start=i-r.value.length,r):null},this.setUndoManager=function(e){this.$undoManager=e,this.$deltas=[],this.$deltasDoc=[],this.$deltasFold=[],this.$informUndoManager&&this.$informUndoManager.cancel();if(e){var t=this;this.$syncInformUndoManager=function(){t.$informUndoManager.cancel(),t.$deltasFold.length&&(t.$deltas.push({group:"fold",deltas:t.$deltasFold}),t.$deltasFold=[]),t.$deltasDoc.length&&(t.$deltas.push({group:"doc",deltas:t.$deltasDoc}),t.$deltasDoc=[]),t.$deltas.length>0&&e.execute({action:"aceupdate",args:[t.$deltas,t],merge:t.mergeUndoDeltas}),t.mergeUndoDeltas=!1,t.$deltas=[]},this.$informUndoManager=i.delayedCall(this.$syncInformUndoManager)}},this.markUndoGroup=function(){this.$syncInformUndoManager&&this.$syncInformUndoManager()},this.$defaultUndoManager={undo:function(){},redo:function(){},reset:function(){}},this.getUndoManager=function(){return this.$undoManager||this.$defaultUndoManager},this.getTabString=function(){return this.getUseSoftTabs()?i.stringRepeat(" ",this.getTabSize()):"	"},this.setUseSoftTabs=function(e){this.setOption("useSoftTabs",e)},this.getUseSoftTabs=function(){return this.$useSoftTabs&&!this.$mode.$indentWithTabs},this.setTabSize=function(e){this.setOption("tabSize",e)},this.getTabSize=function(){return this.$tabSize},this.isTabStop=function(e){return this.$useSoftTabs&&e.column%this.$tabSize===0},this.setNavigateWithinSoftTabs=function(e){this.setOption("navigateWithinSoftTabs",e)},this.getNavigateWithinSoftTabs=function(){return this.$navigateWithinSoftTabs},this.$overwrite=!1,this.setOverwrite=function(e){this.setOption("overwrite",e)},this.getOverwrite=function(){return this.$overwrite},this.toggleOverwrite=function(){this.setOverwrite(!this.$overwrite)},this.addGutterDecoration=function(e,t){this.$decorations[e]||(this.$decorations[e]=""),this.$decorations[e]+=" "+t,this._signal("changeBreakpoint",{})},this.removeGutterDecoration=function(e,t){this.$decorations[e]=(this.$decorations[e]||"").replace(" "+t,""),this._signal("changeBreakpoint",{})},this.getBreakpoints=function(){return this.$breakpoints},this.setBreakpoints=function(e){this.$breakpoints=[];for(var t=0;t<e.length;t++)this.$breakpoints[e[t]]="ace_breakpoint";this._signal("changeBreakpoint",{})},this.clearBreakpoints=function(){this.$breakpoints=[],this._signal("changeBreakpoint",{})},this.setBreakpoint=function(e,t){t===undefined&&(t="ace_breakpoint"),t?this.$breakpoints[e]=t:delete this.$breakpoints[e],this._signal("changeBreakpoint",{})},this.clearBreakpoint=function(e){delete this.$breakpoints[e],this._signal("changeBreakpoint",{})},this.addMarker=function(e,t,n,r){var i=this.$markerId++,s={range:e,type:n||"line",renderer:typeof n=="function"?n:null,clazz:t,inFront:!!r,id:i};return r?(this.$frontMarkers[i]=s,this._signal("changeFrontMarker")):(this.$backMarkers[i]=s,this._signal("changeBackMarker")),i},this.addDynamicMarker=function(e,t){if(!e.update)return;var n=this.$markerId++;return e.id=n,e.inFront=!!t,t?(this.$frontMarkers[n]=e,this._signal("changeFrontMarker")):(this.$backMarkers[n]=e,this._signal("changeBackMarker")),e},this.removeMarker=function(e){var t=this.$frontMarkers[e]||this.$backMarkers[e];if(!t)return;var n=t.inFront?this.$frontMarkers:this.$backMarkers;t&&(delete n[e],this._signal(t.inFront?"changeFrontMarker":"changeBackMarker"))},this.getMarkers=function(e){return e?this.$frontMarkers:this.$backMarkers},this.highlight=function(e){if(!this.$searchHighlight){var t=new h(null,"ace_selected-word","text");this.$searchHighlight=this.addDynamicMarker(t)}this.$searchHighlight.setRegexp(e)},this.highlightLines=function(e,t,n,r){typeof t!="number"&&(n=t,t=e),n||(n="ace_step");var i=new f(e,0,t,Infinity);return i.id=this.addMarker(i,n,"fullLine",r),i},this.setAnnotations=function(e){this.$annotations=e,this._signal("changeAnnotation",{})},this.getAnnotations=function(){return this.$annotations||[]},this.clearAnnotations=function(){this.setAnnotations([])},this.$detectNewLine=function(e){var t=e.match(/^.*?(\r?\n)/m);t?this.$autoNewLine=t[1]:this.$autoNewLine="\n"},this.getWordRange=function(e,t){var n=this.getLine(e),r=!1;t>0&&(r=!!n.charAt(t-1).match(this.tokenRe)),r||(r=!!n.charAt(t).match(this.tokenRe));if(r)var i=this.tokenRe;else if(/^\s+$/.test(n.slice(t-1,t+1)))var i=/\s/;else var i=this.nonTokenRe;var s=t;if(s>0){do s--;while(s>=0&&n.charAt(s).match(i));s++}var o=t;while(o<n.length&&n.charAt(o).match(i))o++;return new f(e,s,e,o)},this.getAWordRange=function(e,t){var n=this.getWordRange(e,t),r=this.getLine(n.end.row);while(r.charAt(n.end.column).match(/[ \t]/))n.end.column+=1;return n},this.setNewLineMode=function(e){this.doc.setNewLineMode(e)},this.getNewLineMode=function(){return this.doc.getNewLineMode()},this.setUseWorker=function(e){this.setOption("useWorker",e)},this.getUseWorker=function(){return this.$useWorker},this.onReloadTokenizer=function(e){var t=e.data;this.bgTokenizer.start(t.first),this._signal("tokenizerUpdate",e)},this.$modes={},this.$mode=null,this.$modeId=null,this.setMode=function(e,t){if(e&&typeof e=="object"){if(e.getTokenizer)return this.$onChangeMode(e);var n=e,r=n.path}else r=e||"ace/mode/text";this.$modes["ace/mode/text"]||(this.$modes["ace/mode/text"]=new a);if(this.$modes[r]&&!n){this.$onChangeMode(this.$modes[r]),t&&t();return}this.$modeId=r,s.loadModule(["mode",r],function(e){if(this.$modeId!==r)return t&&t();this.$modes[r]&&!n?this.$onChangeMode(this.$modes[r]):e&&e.Mode&&(e=new e.Mode(n),n||(this.$modes[r]=e,e.$id=r),this.$onChangeMode(e)),t&&t()}.bind(this)),this.$mode||this.$onChangeMode(this.$modes["ace/mode/text"],!0)},this.$onChangeMode=function(e,t){t||(this.$modeId=e.$id);if(this.$mode===e)return;this.$mode=e,this.$stopWorker(),this.$useWorker&&this.$startWorker();var n=e.getTokenizer();if(n.addEventListener!==undefined){var r=this.onReloadTokenizer.bind(this);n.addEventListener("update",r)}if(!this.bgTokenizer){this.bgTokenizer=new c(n);var i=this;this.bgTokenizer.addEventListener("update",function(e){i._signal("tokenizerUpdate",e)})}else this.bgTokenizer.setTokenizer(n);this.bgTokenizer.setDocument(this.getDocument()),this.tokenRe=e.tokenRe,this.nonTokenRe=e.nonTokenRe,t||(e.attachToSession&&e.attachToSession(this),this.$options.wrapMethod.set.call(this,this.$wrapMethod),this.$setFolding(e.foldingRules),this.bgTokenizer.start(0),this._emit("changeMode"))},this.$stopWorker=function(){this.$worker&&(this.$worker.terminate(),this.$worker=null)},this.$startWorker=function(){try{this.$worker=this.$mode.createWorker(this)}catch(e){s.warn("Could not load worker",e),this.$worker=null}},this.getMode=function(){return this.$mode},this.$scrollTop=0,this.setScrollTop=function(e){if(this.$scrollTop===e||isNaN(e))return;this.$scrollTop=e,this._signal("changeScrollTop",e)},this.getScrollTop=function(){return this.$scrollTop},this.$scrollLeft=0,this.setScrollLeft=function(e){if(this.$scrollLeft===e||isNaN(e))return;this.$scrollLeft=e,this._signal("changeScrollLeft",e)},this.getScrollLeft=function(){return this.$scrollLeft},this.getScreenWidth=function(){return this.$computeWidth(),this.lineWidgets?Math.max(this.getLineWidgetMaxWidth(),this.screenWidth):this.screenWidth},this.getLineWidgetMaxWidth=function(){if(this.lineWidgetsWidth!=null)return this.lineWidgetsWidth;var e=0;return this.lineWidgets.forEach(function(t){t&&t.screenWidth>e&&(e=t.screenWidth)}),this.lineWidgetWidth=e},this.$computeWidth=function(e){if(this.$modified||e){this.$modified=!1;if(this.$useWrapMode)return this.screenWidth=this.$wrapLimit;var t=this.doc.getAllLines(),n=this.$rowLengthCache,r=0,i=0,s=this.$foldData[i],o=s?s.start.row:Infinity,u=t.length;for(var a=0;a<u;a++){if(a>o){a=s.end.row+1;if(a>=u)break;s=this.$foldData[i++],o=s?s.start.row:Infinity}n[a]==null&&(n[a]=this.$getStringScreenWidth(t[a])[0]),n[a]>r&&(r=n[a])}this.screenWidth=r}},this.getLine=function(e){return this.doc.getLine(e)},this.getLines=function(e,t){return this.doc.getLines(e,t)},this.getLength=function(){return this.doc.getLength()},this.getTextRange=function(e){return this.doc.getTextRange(e||this.selection.getRange())},this.insert=function(e,t){return this.doc.insert(e,t)},this.remove=function(e){return this.doc.remove(e)},this.removeFullLines=function(e,t){return this.doc.removeFullLines(e,t)},this.undoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;var n=null;for(var r=e.length-1;r!=-1;r--){var i=e[r];i.group=="doc"?(this.doc.revertDeltas(i.deltas),n=this.$getUndoSelection(i.deltas,!0,n)):i.deltas.forEach(function(e){this.addFolds(e.folds)},this)}return this.$fromUndo=!1,n&&this.$undoSelect&&!t&&this.selection.setSelectionRange(n),n},this.redoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;var n=null;for(var r=0;r<e.length;r++){var i=e[r];i.group=="doc"&&(this.doc.applyDeltas(i.deltas),n=this.$getUndoSelection(i.deltas,!1,n))}return this.$fromUndo=!1,n&&this.$undoSelect&&!t&&this.selection.setSelectionRange(n),n},this.setUndoSelect=function(e){this.$undoSelect=e},this.$getUndoSelection=function(e,t,n){function r(e){return t?e.action!=="insert":e.action==="insert"}var i=e[0],s,o,u=!1;r(i)?(s=f.fromPoints(i.start,i.end),u=!0):(s=f.fromPoints(i.start,i.start),u=!1);for(var a=1;a<e.length;a++)i=e[a],r(i)?(o=i.start,s.compare(o.row,o.column)==-1&&s.setStart(o),o=i.end,s.compare(o.row,o.column)==1&&s.setEnd(o),u=!0):(o=i.start,s.compare(o.row,o.column)==-1&&(s=f.fromPoints(i.start,i.start)),u=!1);if(n!=null){f.comparePoints(n.start,s.start)===0&&(n.start.column+=s.end.column-s.start.column,n.end.column+=s.end.column-s.start.column);var l=n.compareRange(s);l==1?s.setStart(n.start):l==-1&&s.setEnd(n.end)}return s},this.replace=function(e,t){return this.doc.replace(e,t)},this.moveText=function(e,t,n){var r=this.getTextRange(e),i=this.getFoldsInRange(e),s=f.fromPoints(t,t);if(!n){this.remove(e);var o=e.start.row-e.end.row,u=o?-e.end.column:e.start.column-e.end.column;u&&(s.start.row==e.end.row&&s.start.column>e.end.column&&(s.start.column+=u),s.end.row==e.end.row&&s.end.column>e.end.column&&(s.end.column+=u)),o&&s.start.row>=e.end.row&&(s.start.row+=o,s.end.row+=o)}s.end=this.insert(s.start,r);if(i.length){var a=e.start,l=s.start,o=l.row-a.row,u=l.column-a.column;this.addFolds(i.map(function(e){return e=e.clone(),e.start.row==a.row&&(e.start.column+=u),e.end.row==a.row&&(e.end.column+=u),e.start.row+=o,e.end.row+=o,e}))}return s},this.indentRows=function(e,t,n){n=n.replace(/\t/g,this.getTabString());for(var r=e;r<=t;r++)this.doc.insertInLine({row:r,column:0},n)},this.outdentRows=function(e){var t=e.collapseRows(),n=new f(0,0,0,0),r=this.getTabSize();for(var i=t.start.row;i<=t.end.row;++i){var s=this.getLine(i);n.start.row=i,n.end.row=i;for(var o=0;o<r;++o)if(s.charAt(o)!=" ")break;o<r&&s.charAt(o)=="	"?(n.start.column=o,n.end.column=o+1):(n.start.column=0,n.end.column=o),this.remove(n)}},this.$moveLines=function(e,t,n){e=this.getRowFoldStart(e),t=this.getRowFoldEnd(t);if(n<0){var r=this.getRowFoldStart(e+n);if(r<0)return 0;var i=r-e}else if(n>0){var r=this.getRowFoldEnd(t+n);if(r>this.doc.getLength()-1)return 0;var i=r-t}else{e=this.$clipRowToDocument(e),t=this.$clipRowToDocument(t);var i=t-e+1}var s=new f(e,0,t,Number.MAX_VALUE),o=this.getFoldsInRange(s).map(function(e){return e=e.clone(),e.start.row+=i,e.end.row+=i,e}),u=n==0?this.doc.getLines(e,t):this.doc.removeFullLines(e,t);return this.doc.insertFullLines(e+i,u),o.length&&this.addFolds(o),i},this.moveLinesUp=function(e,t){return this.$moveLines(e,t,-1)},this.moveLinesDown=function(e,t){return this.$moveLines(e,t,1)},this.duplicateLines=function(e,t){return this.$moveLines(e,t,0)},this.$clipRowToDocument=function(e){return Math.max(0,Math.min(e,this.doc.getLength()-1))},this.$clipColumnToRow=function(e,t){return t<0?0:Math.min(this.doc.getLine(e).length,t)},this.$clipPositionToDocument=function(e,t){t=Math.max(0,t);if(e<0)e=0,t=0;else{var n=this.doc.getLength();e>=n?(e=n-1,t=this.doc.getLine(n-1).length):t=Math.min(this.doc.getLine(e).length,t)}return{row:e,column:t}},this.$clipRangeToDocument=function(e){e.start.row<0?(e.start.row=0,e.start.column=0):e.start.column=this.$clipColumnToRow(e.start.row,e.start.column);var t=this.doc.getLength()-1;return e.end.row>t?(e.end.row=t,e.end.column=this.doc.getLine(t).length):e.end.column=this.$clipColumnToRow(e.end.row,e.end.column),e},this.$wrapLimit=80,this.$useWrapMode=!1,this.$wrapLimitRange={min:null,max:null},this.setUseWrapMode=function(e){if(e!=this.$useWrapMode){this.$useWrapMode=e,this.$modified=!0,this.$resetRowCache(0);if(e){var t=this.getLength();this.$wrapData=Array(t),this.$updateWrapData(0,t-1)}this._signal("changeWrapMode")}},this.getUseWrapMode=function(){return this.$useWrapMode},this.setWrapLimitRange=function(e,t){if(this.$wrapLimitRange.min!==e||this.$wrapLimitRange.max!==t)this.$wrapLimitRange={min:e,max:t},this.$modified=!0,this.$useWrapMode&&this._signal("changeWrapMode")},this.adjustWrapLimit=function(e,t){var n=this.$wrapLimitRange;n.max<0&&(n={min:t,max:t});var r=this.$constrainWrapLimit(e,n.min,n.max);return r!=this.$wrapLimit&&r>1?(this.$wrapLimit=r,this.$modified=!0,this.$useWrapMode&&(this.$updateWrapData(0,this.getLength()-1),this.$resetRowCache(0),this._signal("changeWrapLimit")),!0):!1},this.$constrainWrapLimit=function(e,t,n){return t&&(e=Math.max(t,e)),n&&(e=Math.min(n,e)),e},this.getWrapLimit=function(){return this.$wrapLimit},this.setWrapLimit=function(e){this.setWrapLimitRange(e,e)},this.getWrapLimitRange=function(){return{min:this.$wrapLimitRange.min,max:this.$wrapLimitRange.max}},this.$updateInternalDataOnChange=function(e){var t=this.$useWrapMode,n=e.action,r=e.start,i=e.end,s=r.row,o=i.row,u=o-s,a=null;this.$updating=!0;if(u!=0)if(n==="remove"){this[t?"$wrapData":"$rowLengthCache"].splice(s,u);var f=this.$foldData;a=this.getFoldsInRange(e),this.removeFolds(a);var l=this.getFoldLine(i.row),c=0;if(l){l.addRemoveChars(i.row,i.column,r.column-i.column),l.shiftRow(-u);var h=this.getFoldLine(s);h&&h!==l&&(h.merge(l),l=h),c=f.indexOf(l)+1}for(c;c<f.length;c++){var l=f[c];l.start.row>=i.row&&l.shiftRow(-u)}o=s}else{var p=Array(u);p.unshift(s,0);var d=t?this.$wrapData:this.$rowLengthCache;d.splice.apply(d,p);var f=this.$foldData,l=this.getFoldLine(s),c=0;if(l){var v=l.range.compareInside(r.row,r.column);v==0?(l=l.split(r.row,r.column),l&&(l.shiftRow(u),l.addRemoveChars(o,0,i.column-r.column))):v==-1&&(l.addRemoveChars(s,0,i.column-r.column),l.shiftRow(u)),c=f.indexOf(l)+1}for(c;c<f.length;c++){var l=f[c];l.start.row>=s&&l.shiftRow(u)}}else{u=Math.abs(e.start.column-e.end.column),n==="remove"&&(a=this.getFoldsInRange(e),this.removeFolds(a),u=-u);var l=this.getFoldLine(s);l&&l.addRemoveChars(s,r.column,u)}return t&&this.$wrapData.length!=this.doc.getLength()&&console.error("doc.getLength() and $wrapData.length have to be the same!"),this.$updating=!1,t?this.$updateWrapData(s,o):this.$updateRowLengthCache(s,o),a},this.$updateRowLengthCache=function(e,t,n){this.$rowLengthCache[e]=null,this.$rowLengthCache[t]=null},this.$updateWrapData=function(e,t){var r=this.doc.getAllLines(),i=this.getTabSize(),s=this.$wrapData,o=this.$wrapLimit,a,f,l=e;t=Math.min(t,r.length-1);while(l<=t)f=this.getFoldLine(l,f),f?(a=[],f.walk(function(e,t,i,s){var o;if(e!=null){o=this.$getDisplayTokens(e,a.length),o[0]=n;for(var f=1;f<o.length;f++)o[f]=u}else o=this.$getDisplayTokens(r[t].substring(s,i),a.length);a=a.concat(o)}.bind(this),f.end.row,r[f.end.row].length+1),s[f.start.row]=this.$computeWrapSplits(a,o,i),l=f.end.row+1):(a=this.$getDisplayTokens(r[l]),s[l]=this.$computeWrapSplits(a,o,i),l++)};var e=1,t=2,n=3,u=4,l=9,p=10,d=11,v=12;this.$computeWrapSplits=function(e,r,i){function g(){var t=0;if(m===0)return t;if(h)for(var n=0;n<e.length;n++){var r=e[n];if(r==p)t+=1;else{if(r!=d){if(r==v)continue;break}t+=i}}return c&&h!==!1&&(t+=i),Math.min(t,m)}function y(t){var n=e.slice(a,t),r=n.length;n.join("").replace(/12/g,function(){r-=1}).replace(/2/g,function(){r-=1}),s.length||(b=g(),s.indent=b),f+=r,s.push(f),a=t}if(e.length==0)return[];var s=[],o=e.length,a=0,f=0,c=this.$wrapAsCode,h=this.$indentedSoftWrap,m=r<=Math.max(2*i,8)||h===!1?0:Math.floor(r/2),b=0;while(o-a>r-b){var w=a+r-b;if(e[w-1]>=p&&e[w]>=p){y(w);continue}if(e[w]==n||e[w]==u){for(w;w!=a-1;w--)if(e[w]==n)break;if(w>a){y(w);continue}w=a+r;for(w;w<e.length;w++)if(e[w]!=u)break;if(w==e.length)break;y(w);continue}var E=Math.max(w-(r-(r>>2)),a-1);while(w>E&&e[w]<n)w--;if(c){while(w>E&&e[w]<n)w--;while(w>E&&e[w]==l)w--}else while(w>E&&e[w]<p)w--;if(w>E){y(++w);continue}w=a+r,e[w]==t&&w--,y(w-b)}return s},this.$getDisplayTokens=function(n,r){var i=[],s;r=r||0;for(var o=0;o<n.length;o++){var u=n.charCodeAt(o);if(u==9){s=this.getScreenTabSize(i.length+r),i.push(d);for(var a=1;a<s;a++)i.push(v)}else u==32?i.push(p):u>39&&u<48||u>57&&u<64?i.push(l):u>=4352&&m(u)?i.push(e,t):i.push(e)}return i},this.$getStringScreenWidth=function(e,t,n){if(t==0)return[0,0];t==null&&(t=Infinity),n=n||0;var r,i;for(i=0;i<e.length;i++){r=e.charCodeAt(i),r==9?n+=this.getScreenTabSize(n):r>=4352&&m(r)?n+=2:n+=1;if(n>t)break}return[n,i]},this.lineWidgets=null,this.getRowLength=function(e){if(this.lineWidgets)var t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0;else t=0;return!this.$useWrapMode||!this.$wrapData[e]?1+t:this.$wrapData[e].length+1+t},this.getRowLineCount=function(e){return!this.$useWrapMode||!this.$wrapData[e]?1:this.$wrapData[e].length+1},this.getRowWrapIndent=function(e){if(this.$useWrapMode){var t=this.screenToDocumentPosition(e,Number.MAX_VALUE),n=this.$wrapData[t.row];return n.length&&n[0]<t.column?n.indent:0}return 0},this.getScreenLastRowColumn=function(e){var t=this.screenToDocumentPosition(e,Number.MAX_VALUE);return this.documentToScreenColumn(t.row,t.column)},this.getDocumentLastRowColumn=function(e,t){var n=this.documentToScreenRow(e,t);return this.getScreenLastRowColumn(n)},this.getDocumentLastRowColumnPosition=function(e,t){var n=this.documentToScreenRow(e,t);return this.screenToDocumentPosition(n,Number.MAX_VALUE/10)},this.getRowSplitData=function(e){return this.$useWrapMode?this.$wrapData[e]:undefined},this.getScreenTabSize=function(e){return this.$tabSize-e%this.$tabSize},this.screenToDocumentRow=function(e,t){return this.screenToDocumentPosition(e,t).row},this.screenToDocumentColumn=function(e,t){return this.screenToDocumentPosition(e,t).column},this.screenToDocumentPosition=function(e,t){if(e<0)return{row:0,column:0};var n,r=0,i=0,s,o=0,u=0,a=this.$screenRowCache,f=this.$getRowCacheIndex(a,e),l=a.length;if(l&&f>=0)var o=a[f],r=this.$docRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getLength()-1,p=this.getNextFoldLine(r),d=p?p.start.row:Infinity;while(o<=e){u=this.getRowLength(r);if(o+u>e||r>=h)break;o+=u,r++,r>d&&(r=p.end.row+1,p=this.getNextFoldLine(r,p),d=p?p.start.row:Infinity),c&&(this.$docRowCache.push(r),this.$screenRowCache.push(o))}if(p&&p.start.row<=r)n=this.getFoldDisplayLine(p),r=p.start.row;else{if(o+u<=e||r>h)return{row:h,column:this.getLine(h).length};n=this.getLine(r),p=null}var v=0;if(this.$useWrapMode){var m=this.$wrapData[r];if(m){var g=Math.floor(e-o);s=m[g],g>0&&m.length&&(v=m.indent,i=m[g-1]||m[m.length-1],n=n.substring(i))}}return i+=this.$getStringScreenWidth(n,t-v)[1],this.$useWrapMode&&i>=s&&(i=s-1),p?p.idxToPosition(i):{row:r,column:i}},this.documentToScreenPosition=function(e,t){if(typeof t=="undefined")var n=this.$clipPositionToDocument(e.row,e.column);else n=this.$clipPositionToDocument(e,t);e=n.row,t=n.column;var r=0,i=null,s=null;s=this.getFoldAt(e,t,1),s&&(e=s.start.row,t=s.start.column);var o,u=0,a=this.$docRowCache,f=this.$getRowCacheIndex(a,e),l=a.length;if(l&&f>=0)var u=a[f],r=this.$screenRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getNextFoldLine(u),p=h?h.start.row:Infinity;while(u<e){if(u>=p){o=h.end.row+1;if(o>e)break;h=this.getNextFoldLine(o,h),p=h?h.start.row:Infinity}else o=u+1;r+=this.getRowLength(u),u=o,c&&(this.$docRowCache.push(u),this.$screenRowCache.push(r))}var d="";h&&u>=p?(d=this.getFoldDisplayLine(h,e,t),i=h.start.row):(d=this.getLine(e).substring(0,t),i=e);var v=0;if(this.$useWrapMode){var m=this.$wrapData[i];if(m){var g=0;while(d.length>=m[g])r++,g++;d=d.substring(m[g-1]||0,d.length),v=g>0?m.indent:0}}return{row:r,column:v+this.$getStringScreenWidth(d)[0]}},this.documentToScreenColumn=function(e,t){return this.documentToScreenPosition(e,t).column},this.documentToScreenRow=function(e,t){return this.documentToScreenPosition(e,t).row},this.getScreenLength=function(){var e=0,t=null;if(!this.$useWrapMode){e=this.getLength();var n=this.$foldData;for(var r=0;r<n.length;r++)t=n[r],e-=t.end.row-t.start.row}else{var i=this.$wrapData.length,s=0,r=0,t=this.$foldData[r++],o=t?t.start.row:Infinity;while(s<i){var u=this.$wrapData[s];e+=u?u.length+1:1,s++,s>o&&(s=t.end.row+1,t=this.$foldData[r++],o=t?t.start.row:Infinity)}}return this.lineWidgets&&(e+=this.$getWidgetScreenLength()),e},this.$setFontMetrics=function(e){if(!this.$enableVarChar)return;this.$getStringScreenWidth=function(t,n,r){if(n===0)return[0,0];n||(n=Infinity),r=r||0;var i,s;for(s=0;s<t.length;s++){i=t.charAt(s),i==="	"?r+=this.getScreenTabSize(r):r+=e.getCharacterWidth(i);if(r>n)break}return[r,s]}},this.destroy=function(){this.bgTokenizer&&(this.bgTokenizer.setDocument(null),this.bgTokenizer=null),this.$stopWorker()}}.call(p.prototype),e("./edit_session/folding").Folding.call(p.prototype),e("./edit_session/bracket_match").BracketMatch.call(p.prototype),s.defineOptions(p.prototype,"session",{wrap:{set:function(e){!e||e=="off"?e=!1:e=="free"?e=!0:e=="printMargin"?e=-1:typeof e=="string"&&(e=parseInt(e,10)||!1);if(this.$wrap==e)return;this.$wrap=e;if(!e)this.setUseWrapMode(!1);else{var t=typeof e=="number"?e:null;this.setWrapLimitRange(t,t),this.setUseWrapMode(!0)}},get:function(){return this.getUseWrapMode()?this.$wrap==-1?"printMargin":this.getWrapLimitRange().min?this.$wrap:"free":"off"},handlesSet:!0},wrapMethod:{set:function(e){e=e=="auto"?this.$mode.type!="text":e!="text",e!=this.$wrapAsCode&&(this.$wrapAsCode=e,this.$useWrapMode&&(this.$modified=!0,this.$resetRowCache(0),this.$updateWrapData(0,this.getLength()-1)))},initialValue:"auto"},indentedSoftWrap:{initialValue:!0},firstLineNumber:{set:function(){this._signal("changeBreakpoint")},initialValue:1},useWorker:{set:function(e){this.$useWorker=e,this.$stopWorker(),e&&this.$startWorker()},initialValue:!0},useSoftTabs:{initialValue:!0},tabSize:{set:function(e){if(isNaN(e)||this.$tabSize===e)return;this.$modified=!0,this.$rowLengthCache=[],this.$tabSize=e,this._signal("changeTabSize")},initialValue:4,handlesSet:!0},navigateWithinSoftTabs:{initialValue:!1},overwrite:{set:function(e){this._signal("changeOverwrite")},initialValue:!1},newLineMode:{set:function(e){this.doc.setNewLineMode(e)},get:function(){return this.doc.getNewLineMode()},handlesSet:!0},mode:{set:function(e){this.setMode(e)},get:function(){return this.$modeId}}}),t.EditSession=p}),define("ace/search",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";function u(e,t){function n(e){return/\w/.test(e)||t.regExp?"\\b":""}return n(e[0])+e+n(e[e.length-1])}var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(){this.$options={}};(function(){this.set=function(e){return i.mixin(this.$options,e),this},this.getOptions=function(){return r.copyObject(this.$options)},this.setOptions=function(e){this.$options=e},this.find=function(e){var t=this.$options,n=this.$matchIterator(e,t);if(!n)return!1;var r=null;return n.forEach(function(e,n,i,o){return r=new s(e,n,i,o),n==o&&t.start&&t.start.start&&t.skipCurrent!=0&&r.isEqual(t.start)?(r=null,!1):!0}),r},this.findAll=function(e){var t=this.$options;if(!t.needle)return[];this.$assembleRegExp(t);var n=t.range,i=n?e.getLines(n.start.row,n.end.row):e.doc.getAllLines(),o=[],u=t.re;if(t.$isMultiLine){var a=u.length,f=i.length-a,l;e:for(var c=u.offset||0;c<=f;c++){for(var h=0;h<a;h++)if(i[c+h].search(u[h])==-1)continue e;var p=i[c],d=i[c+a-1],v=p.length-p.match(u[0])[0].length,m=d.match(u[a-1])[0].length;if(l&&l.end.row===c&&l.end.column>v)continue;o.push(l=new s(c,v,c+a-1,m)),a>2&&(c=c+a-2)}}else for(var g=0;g<i.length;g++){var y=r.getMatchOffsets(i[g],u);for(var h=0;h<y.length;h++){var b=y[h];o.push(new s(g,b.offset,g,b.offset+b.length))}}if(n){var w=n.start.column,E=n.start.column,g=0,h=o.length-1;while(g<h&&o[g].start.column<w&&o[g].start.row==n.start.row)g++;while(g<h&&o[h].end.column>E&&o[h].end.row==n.end.row)h--;o=o.slice(g,h+1);for(g=0,h=o.length;g<h;g++)o[g].start.row+=n.start.row,o[g].end.row+=n.start.row}return o},this.replace=function(e,t){var n=this.$options,r=this.$assembleRegExp(n);if(n.$isMultiLine)return t;if(!r)return;var i=r.exec(e);if(!i||i[0].length!=e.length)return null;t=e.replace(r,t);if(n.preserveCase){t=t.split("");for(var s=Math.min(e.length,e.length);s--;){var o=e[s];o&&o.toLowerCase()!=o?t[s]=t[s].toUpperCase():t[s]=t[s].toLowerCase()}t=t.join("")}return t},this.$assembleRegExp=function(e,t){if(e.needle instanceof RegExp)return e.re=e.needle;var n=e.needle;if(!e.needle)return e.re=!1;e.regExp||(n=r.escapeRegExp(n)),e.wholeWord&&(n=u(n,e));var i=e.caseSensitive?"gm":"gmi";e.$isMultiLine=!t&&/[\n\r]/.test(n);if(e.$isMultiLine)return e.re=this.$assembleMultilineRegExp(n,i);try{var s=new RegExp(n,i)}catch(o){s=!1}return e.re=s},this.$assembleMultilineRegExp=function(e,t){var n=e.replace(/\r\n|\r|\n/g,"$\n^").split("\n"),r=[];for(var i=0;i<n.length;i++)try{r.push(new RegExp(n[i],t))}catch(s){return!1}return r},this.$matchIterator=function(e,t){var n=this.$assembleRegExp(t);if(!n)return!1;var r=t.backwards==1,i=t.skipCurrent!=0,s=t.range,o=t.start;o||(o=s?s[r?"end":"start"]:e.selection.getRange()),o.start&&(o=o[i!=r?"end":"start"]);var u=s?s.start.row:0,a=s?s.end.row:e.getLength()-1;if(r)var f=function(e){var n=o.row;if(c(n,o.column,e))return;for(n--;n>=u;n--)if(c(n,Number.MAX_VALUE,e))return;if(t.wrap==0)return;for(n=a,u=o.row;n>=u;n--)if(c(n,Number.MAX_VALUE,e))return};else var f=function(e){var n=o.row;if(c(n,o.column,e))return;for(n+=1;n<=a;n++)if(c(n,0,e))return;if(t.wrap==0)return;for(n=u,a=o.row;n<=a;n++)if(c(n,0,e))return};if(t.$isMultiLine)var l=n.length,c=function(t,i,s){var o=r?t-l+1:t;if(o<0)return;var u=e.getLine(o),a=u.search(n[0]);if(!r&&a<i||a===-1)return;for(var f=1;f<l;f++){u=e.getLine(o+f);if(u.search(n[f])==-1)return}var c=u.match(n[l-1])[0].length;if(r&&c>i)return;if(s(o,a,o+l-1,c))return!0};else if(r)var c=function(t,r,i){var s=e.getLine(t),o=[],u,a=0;n.lastIndex=0;while(u=n.exec(s)){var f=u[0].length;a=u.index;if(!f){if(a>=s.length)break;n.lastIndex=a+=1}if(u.index+f>r)break;o.push(u.index,f)}for(var l=o.length-1;l>=0;l-=2){var c=o[l-1],f=o[l];if(i(t,c,t,c+f))return!0}};else var c=function(t,r,i){var s=e.getLine(t),o,u=r;n.lastIndex=r;while(o=n.exec(s)){var a=o[0].length;u=o.index;if(i(t,u,t,u+a))return!0;if(!a){n.lastIndex=u+=1;if(u>=s.length)return!1}}};return{forEach:f}}}).call(o.prototype),t.Search=o}),define("ace/keyboard/hash_handler",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function o(e,t){this.platform=t||(i.isMac?"mac":"win"),this.commands={},this.commandKeyBinding={},this.addCommands(e),this.$singleCommand=!0}function u(e,t){o.call(this,e,t),this.$singleCommand=!1}var r=e("../lib/keys"),i=e("../lib/useragent"),s=r.KEY_MODS;u.prototype=o.prototype,function(){function e(e){return typeof e=="object"&&e.bindKey&&e.bindKey.position||(e.isDefault?-100:0)}this.addCommand=function(e){this.commands[e.name]&&this.removeCommand(e),this.commands[e.name]=e,e.bindKey&&this._buildKeyHash(e)},this.removeCommand=function(e,t){var n=e&&(typeof e=="string"?e:e.name);e=this.commands[n],t||delete this.commands[n];var r=this.commandKeyBinding;for(var i in r){var s=r[i];if(s==e)delete r[i];else if(Array.isArray(s)){var o=s.indexOf(e);o!=-1&&(s.splice(o,1),s.length==1&&(r[i]=s[0]))}}},this.bindKey=function(e,t,n){typeof e=="object"&&e&&(n==undefined&&(n=e.position),e=e[this.platform]);if(!e)return;if(typeof t=="function")return this.addCommand({exec:t,bindKey:e,name:t.name||e});e.split("|").forEach(function(e){var r="";if(e.indexOf(" ")!=-1){var i=e.split(/\s+/);e=i.pop(),i.forEach(function(e){var t=this.parseKeys(e),n=s[t.hashId]+t.key;r+=(r?" ":"")+n,this._addCommandToBinding(r,"chainKeys")},this),r+=" "}var o=this.parseKeys(e),u=s[o.hashId]+o.key;this._addCommandToBinding(r+u,t,n)},this)},this._addCommandToBinding=function(t,n,r){var i=this.commandKeyBinding,s;if(!n)delete i[t];else if(!i[t]||this.$singleCommand)i[t]=n;else{Array.isArray(i[t])?(s=i[t].indexOf(n))!=-1&&i[t].splice(s,1):i[t]=[i[t]],typeof r!="number"&&(r=e(n));var o=i[t];for(s=0;s<o.length;s++){var u=o[s],a=e(u);if(a>r)break}o.splice(s,0,n)}},this.addCommands=function(e){e&&Object.keys(e).forEach(function(t){var n=e[t];if(!n)return;if(typeof n=="string")return this.bindKey(n,t);typeof n=="function"&&(n={exec:n});if(typeof n!="object")return;n.name||(n.name=t),this.addCommand(n)},this)},this.removeCommands=function(e){Object.keys(e).forEach(function(t){this.removeCommand(e[t])},this)},this.bindKeys=function(e){Object.keys(e).forEach(function(t){this.bindKey(t,e[t])},this)},this._buildKeyHash=function(e){this.bindKey(e.bindKey,e)},this.parseKeys=function(e){var t=e.toLowerCase().split(/[\-\+]([\-\+])?/).filter(function(e){return e}),n=t.pop(),i=r[n];if(r.FUNCTION_KEYS[i])n=r.FUNCTION_KEYS[i].toLowerCase();else{if(!t.length)return{key:n,hashId:-1};if(t.length==1&&t[0]=="shift")return{key:n.toUpperCase(),hashId:-1}}var s=0;for(var o=t.length;o--;){var u=r.KEY_MODS[t[o]];if(u==null)return typeof console!="undefined"&&console.error("invalid modifier "+t[o]+" in "+e),!1;s|=u}return{key:n,hashId:s}},this.findKeyCommand=function(t,n){var r=s[t]+n;return this.commandKeyBinding[r]},this.handleKeyboard=function(e,t,n,r){if(r<0)return;var i=s[t]+n,o=this.commandKeyBinding[i];e.$keyChain&&(e.$keyChain+=" "+i,o=this.commandKeyBinding[e.$keyChain]||o);if(o)if(o=="chainKeys"||o[o.length-1]=="chainKeys")return e.$keyChain=e.$keyChain||i,{command:"null"};if(e.$keyChain)if(!!t&&t!=4||n.length!=1){if(t==-1||r>0)e.$keyChain=""}else e.$keyChain=e.$keyChain.slice(0,-i.length-1);return{command:o}},this.getStatusText=function(e,t){return t.$keyChain||""}}.call(o.prototype),t.HashHandler=o,t.MultiHashHandler=u}),define("ace/commands/command_manager",["require","exports","module","ace/lib/oop","ace/keyboard/hash_handler","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../keyboard/hash_handler").MultiHashHandler,s=e("../lib/event_emitter").EventEmitter,o=function(e,t){i.call(this,t,e),this.byName=this.commands,this.setDefaultHandler("exec",function(e){return e.command.exec(e.editor,e.args||{})})};r.inherits(o,i),function(){r.implement(this,s),this.exec=function(e,t,n){if(Array.isArray(e)){for(var r=e.length;r--;)if(this.exec(e[r],t,n))return!0;return!1}typeof e=="string"&&(e=this.commands[e]);if(!e)return!1;if(t&&t.$readOnly&&!e.readOnly)return!1;if(e.isAvailable&&!e.isAvailable(t))return!1;var i={editor:t,command:e,args:n};return i.returnValue=this._emit("exec",i),this._signal("afterExec",i),i.returnValue===!1?!1:!0},this.toggleRecording=function(e){if(this.$inReplay)return;return e&&e._emit("changeStatus"),this.recording?(this.macro.pop(),this.removeEventListener("exec",this.$addCommandToMacro),this.macro.length||(this.macro=this.oldMacro),this.recording=!1):(this.$addCommandToMacro||(this.$addCommandToMacro=function(e){this.macro.push([e.command,e.args])}.bind(this)),this.oldMacro=this.macro,this.macro=[],this.on("exec",this.$addCommandToMacro),this.recording=!0)},this.replay=function(e){if(this.$inReplay||!this.macro)return;if(this.recording)return this.toggleRecording(e);try{this.$inReplay=!0,this.macro.forEach(function(t){typeof t=="string"?this.exec(t,e):this.exec(t[0],e,t[1])},this)}finally{this.$inReplay=!1}},this.trimMacro=function(e){return e.map(function(e){return typeof e[0]!="string"&&(e[0]=e[0].name),e[1]||(e=e[0]),e})}}.call(o.prototype),t.CommandManager=o}),define("ace/commands/default_commands",["require","exports","module","ace/lib/lang","ace/config","ace/range"],function(e,t,n){"use strict";function o(e,t){return{win:e,mac:t}}var r=e("../lib/lang"),i=e("../config"),s=e("../range").Range;t.commands=[{name:"showSettingsMenu",bindKey:o("Ctrl-,","Command-,"),exec:function(e){i.loadModule("ace/ext/settings_menu",function(t){t.init(e),e.showSettingsMenu()})},readOnly:!0},{name:"goToNextError",bindKey:o("Alt-E","F4"),exec:function(e){i.loadModule("ace/ext/error_marker",function(t){t.showErrorMarker(e,1)})},scrollIntoView:"animate",readOnly:!0},{name:"goToPreviousError",bindKey:o("Alt-Shift-E","Shift-F4"),exec:function(e){i.loadModule("ace/ext/error_marker",function(t){t.showErrorMarker(e,-1)})},scrollIntoView:"animate",readOnly:!0},{name:"selectall",bindKey:o("Ctrl-A","Command-A"),exec:function(e){e.selectAll()},readOnly:!0},{name:"centerselection",bindKey:o(null,"Ctrl-L"),exec:function(e){e.centerSelection()},readOnly:!0},{name:"gotoline",bindKey:o("Ctrl-L","Command-L"),exec:function(e){var t=parseInt(prompt("Enter line number:"),10);isNaN(t)||e.gotoLine(t)},readOnly:!0},{name:"fold",bindKey:o("Alt-L|Ctrl-F1","Command-Alt-L|Command-F1"),exec:function(e){e.session.toggleFold(!1)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"unfold",bindKey:o("Alt-Shift-L|Ctrl-Shift-F1","Command-Alt-Shift-L|Command-Shift-F1"),exec:function(e){e.session.toggleFold(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleFoldWidget",bindKey:o("F2","F2"),exec:function(e){e.session.toggleFoldWidget()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleParentFoldWidget",bindKey:o("Alt-F2","Alt-F2"),exec:function(e){e.session.toggleFoldWidget(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"foldall",bindKey:o(null,"Ctrl-Command-Option-0"),exec:function(e){e.session.foldAll()},scrollIntoView:"center",readOnly:!0},{name:"foldOther",bindKey:o("Alt-0","Command-Option-0"),exec:function(e){e.session.foldAll(),e.session.unfold(e.selection.getAllRanges())},scrollIntoView:"center",readOnly:!0},{name:"unfoldall",bindKey:o("Alt-Shift-0","Command-Option-Shift-0"),exec:function(e){e.session.unfold()},scrollIntoView:"center",readOnly:!0},{name:"findnext",bindKey:o("Ctrl-K","Command-G"),exec:function(e){e.findNext()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"findprevious",bindKey:o("Ctrl-Shift-K","Command-Shift-G"),exec:function(e){e.findPrevious()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"selectOrFindNext",bindKey:o("Alt-K","Ctrl-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findNext()},readOnly:!0},{name:"selectOrFindPrevious",bindKey:o("Alt-Shift-K","Ctrl-Shift-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findPrevious()},readOnly:!0},{name:"find",bindKey:o("Ctrl-F","Command-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e)})},readOnly:!0},{name:"overwrite",bindKey:"Insert",exec:function(e){e.toggleOverwrite()},readOnly:!0},{name:"selecttostart",bindKey:o("Ctrl-Shift-Home","Command-Shift-Home|Command-Shift-Up"),exec:function(e){e.getSelection().selectFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotostart",bindKey:o("Ctrl-Home","Command-Home|Command-Up"),exec:function(e){e.navigateFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectup",bindKey:o("Shift-Up","Shift-Up|Ctrl-Shift-P"),exec:function(e){e.getSelection().selectUp()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golineup",bindKey:o("Up","Up|Ctrl-P"),exec:function(e,t){e.navigateUp(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttoend",bindKey:o("Ctrl-Shift-End","Command-Shift-End|Command-Shift-Down"),exec:function(e){e.getSelection().selectFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotoend",bindKey:o("Ctrl-End","Command-End|Command-Down"),exec:function(e){e.navigateFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectdown",bindKey:o("Shift-Down","Shift-Down|Ctrl-Shift-N"),exec:function(e){e.getSelection().selectDown()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golinedown",bindKey:o("Down","Down|Ctrl-N"),exec:function(e,t){e.navigateDown(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordleft",bindKey:o("Ctrl-Shift-Left","Option-Shift-Left"),exec:function(e){e.getSelection().selectWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordleft",bindKey:o("Ctrl-Left","Option-Left"),exec:function(e){e.navigateWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolinestart",bindKey:o("Alt-Shift-Left","Command-Shift-Left|Ctrl-Shift-A"),exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolinestart",bindKey:o("Alt-Left|Home","Command-Left|Home|Ctrl-A"),exec:function(e){e.navigateLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectleft",bindKey:o("Shift-Left","Shift-Left|Ctrl-Shift-B"),exec:function(e){e.getSelection().selectLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoleft",bindKey:o("Left","Left|Ctrl-B"),exec:function(e,t){e.navigateLeft(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordright",bindKey:o("Ctrl-Shift-Right","Option-Shift-Right"),exec:function(e){e.getSelection().selectWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordright",bindKey:o("Ctrl-Right","Option-Right"),exec:function(e){e.navigateWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolineend",bindKey:o("Alt-Shift-Right","Command-Shift-Right|Shift-End|Ctrl-Shift-E"),exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolineend",bindKey:o("Alt-Right|End","Command-Right|End|Ctrl-E"),exec:function(e){e.navigateLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectright",bindKey:o("Shift-Right","Shift-Right"),exec:function(e){e.getSelection().selectRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoright",bindKey:o("Right","Right|Ctrl-F"),exec:function(e,t){e.navigateRight(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectpagedown",bindKey:"Shift-PageDown",exec:function(e){e.selectPageDown()},readOnly:!0},{name:"pagedown",bindKey:o(null,"Option-PageDown"),exec:function(e){e.scrollPageDown()},readOnly:!0},{name:"gotopagedown",bindKey:o("PageDown","PageDown|Ctrl-V"),exec:function(e){e.gotoPageDown()},readOnly:!0},{name:"selectpageup",bindKey:"Shift-PageUp",exec:function(e){e.selectPageUp()},readOnly:!0},{name:"pageup",bindKey:o(null,"Option-PageUp"),exec:function(e){e.scrollPageUp()},readOnly:!0},{name:"gotopageup",bindKey:"PageUp",exec:function(e){e.gotoPageUp()},readOnly:!0},{name:"scrollup",bindKey:o("Ctrl-Up",null),exec:function(e){e.renderer.scrollBy(0,-2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"scrolldown",bindKey:o("Ctrl-Down",null),exec:function(e){e.renderer.scrollBy(0,2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"selectlinestart",bindKey:"Shift-Home",exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectlineend",bindKey:"Shift-End",exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"togglerecording",bindKey:o("Ctrl-Alt-E","Command-Option-E"),exec:function(e){e.commands.toggleRecording(e)},readOnly:!0},{name:"replaymacro",bindKey:o("Ctrl-Shift-E","Command-Shift-E"),exec:function(e){e.commands.replay(e)},readOnly:!0},{name:"jumptomatching",bindKey:o("Ctrl-P","Ctrl-P"),exec:function(e){e.jumpToMatching()},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"selecttomatching",bindKey:o("Ctrl-Shift-P","Ctrl-Shift-P"),exec:function(e){e.jumpToMatching(!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"expandToMatching",bindKey:o("Ctrl-Shift-M","Ctrl-Shift-M"),exec:function(e){e.jumpToMatching(!0,!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"passKeysToBrowser",bindKey:o(null,null),exec:function(){},passEvent:!0,readOnly:!0},{name:"copy",exec:function(e){},readOnly:!0},{name:"cut",exec:function(e){var t=e.getSelectionRange();e._emit("cut",t),e.selection.isEmpty()||(e.session.remove(t),e.clearSelection())},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"paste",exec:function(e,t){e.$handlePaste(t)},scrollIntoView:"cursor"},{name:"removeline",bindKey:o("Ctrl-D","Command-D"),exec:function(e){e.removeLines()},scrollIntoView:"cursor",multiSelectAction:"forEachLine"},{name:"duplicateSelection",bindKey:o("Ctrl-Shift-D","Command-Shift-D"),exec:function(e){e.duplicateSelection()},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"sortlines",bindKey:o("Ctrl-Alt-S","Command-Alt-S"),exec:function(e){e.sortLines()},scrollIntoView:"selection",multiSelectAction:"forEachLine"},{name:"togglecomment",bindKey:o("Ctrl-/","Command-/"),exec:function(e){e.toggleCommentLines()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"toggleBlockComment",bindKey:o("Ctrl-Shift-/","Command-Shift-/"),exec:function(e){e.toggleBlockComment()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"modifyNumberUp",bindKey:o("Ctrl-Shift-Up","Alt-Shift-Up"),exec:function(e){e.modifyNumber(1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"modifyNumberDown",bindKey:o("Ctrl-Shift-Down","Alt-Shift-Down"),exec:function(e){e.modifyNumber(-1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"replace",bindKey:o("Ctrl-H","Command-Option-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e,!0)})}},{name:"undo",bindKey:o("Ctrl-Z","Command-Z"),exec:function(e){e.undo()}},{name:"redo",bindKey:o("Ctrl-Shift-Z|Ctrl-Y","Command-Shift-Z|Command-Y"),exec:function(e){e.redo()}},{name:"copylinesup",bindKey:o("Alt-Shift-Up","Command-Option-Up"),exec:function(e){e.copyLinesUp()},scrollIntoView:"cursor"},{name:"movelinesup",bindKey:o("Alt-Up","Option-Up"),exec:function(e){e.moveLinesUp()},scrollIntoView:"cursor"},{name:"copylinesdown",bindKey:o("Alt-Shift-Down","Command-Option-Down"),exec:function(e){e.copyLinesDown()},scrollIntoView:"cursor"},{name:"movelinesdown",bindKey:o("Alt-Down","Option-Down"),exec:function(e){e.moveLinesDown()},scrollIntoView:"cursor"},{name:"del",bindKey:o("Delete","Delete|Ctrl-D|Shift-Delete"),exec:function(e){e.remove("right")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"backspace",bindKey:o("Shift-Backspace|Backspace","Ctrl-Backspace|Shift-Backspace|Backspace|Ctrl-H"),exec:function(e){e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"cut_or_delete",bindKey:o("Shift-Delete",null),exec:function(e){if(!e.selection.isEmpty())return!1;e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolinestart",bindKey:o("Alt-Backspace","Command-Backspace"),exec:function(e){e.removeToLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolineend",bindKey:o("Alt-Delete","Ctrl-K"),exec:function(e){e.removeToLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordleft",bindKey:o("Ctrl-Backspace","Alt-Backspace|Ctrl-Alt-Backspace"),exec:function(e){e.removeWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordright",bindKey:o("Ctrl-Delete","Alt-Delete"),exec:function(e){e.removeWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"outdent",bindKey:o("Shift-Tab","Shift-Tab"),exec:function(e){e.blockOutdent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"indent",bindKey:o("Tab","Tab"),exec:function(e){e.indent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"blockoutdent",bindKey:o("Ctrl-[","Ctrl-["),exec:function(e){e.blockOutdent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"blockindent",bindKey:o("Ctrl-]","Ctrl-]"),exec:function(e){e.blockIndent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"insertstring",exec:function(e,t){e.insert(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"inserttext",exec:function(e,t){e.insert(r.stringRepeat(t.text||"",t.times||1))},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"splitline",bindKey:o(null,"Ctrl-O"),exec:function(e){e.splitLine()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"transposeletters",bindKey:o("Alt-Shift-X","Ctrl-T"),exec:function(e){e.transposeLetters()},multiSelectAction:function(e){e.transposeSelections(1)},scrollIntoView:"cursor"},{name:"touppercase",bindKey:o("Ctrl-U","Ctrl-U"),exec:function(e){e.toUpperCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"tolowercase",bindKey:o("Ctrl-Shift-U","Ctrl-Shift-U"),exec:function(e){e.toLowerCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"expandtoline",bindKey:o("Ctrl-Shift-L","Command-Shift-L"),exec:function(e){var t=e.selection.getRange();t.start.column=t.end.column=0,t.end.row++,e.selection.setRange(t,!1)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"joinlines",bindKey:o(null,null),exec:function(e){var t=e.selection.isBackwards(),n=t?e.selection.getSelectionLead():e.selection.getSelectionAnchor(),i=t?e.selection.getSelectionAnchor():e.selection.getSelectionLead(),o=e.session.doc.getLine(n.row).length,u=e.session.doc.getTextRange(e.selection.getRange()),a=u.replace(/\n\s*/," ").length,f=e.session.doc.getLine(n.row);for(var l=n.row+1;l<=i.row+1;l++){var c=r.stringTrimLeft(r.stringTrimRight(e.session.doc.getLine(l)));c.length!==0&&(c=" "+c),f+=c}i.row+1<e.session.doc.getLength()-1&&(f+=e.session.doc.getNewLineCharacter()),e.clearSelection(),e.session.doc.replace(new s(n.row,0,i.row+2,0),f),a>0?(e.selection.moveCursorTo(n.row,n.column),e.selection.selectTo(n.row,n.column+a)):(o=e.session.doc.getLine(n.row).length>o?o+1:o,e.selection.moveCursorTo(n.row,o))},multiSelectAction:"forEach",readOnly:!0},{name:"invertSelection",bindKey:o(null,null),exec:function(e){var t=e.session.doc.getLength()-1,n=e.session.doc.getLine(t).length,r=e.selection.rangeList.ranges,i=[];r.length<1&&(r=[e.selection.getRange()]);for(var o=0;o<r.length;o++)o==r.length-1&&(r[o].end.row!==t||r[o].end.column!==n)&&i.push(new s(r[o].end.row,r[o].end.column,t,n)),o===0?(r[o].start.row!==0||r[o].start.column!==0)&&i.push(new s(0,0,r[o].start.row,r[o].start.column)):i.push(new s(r[o-1].end.row,r[o-1].end.column,r[o].start.row,r[o].start.column));e.exitMultiSelectMode(),e.clearSelection();for(var o=0;o<i.length;o++)e.selection.addRange(i[o],!1)},readOnly:!0,scrollIntoView:"none"}]}),define("ace/editor",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/keyboard/textinput","ace/mouse/mouse_handler","ace/mouse/fold_handler","ace/keyboard/keybinding","ace/edit_session","ace/search","ace/range","ace/lib/event_emitter","ace/commands/command_manager","ace/commands/default_commands","ace/config","ace/token_iterator"],function(e,t,n){"use strict";e("./lib/fixoldbrowsers");var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./lib/lang"),o=e("./lib/useragent"),u=e("./keyboard/textinput").TextInput,a=e("./mouse/mouse_handler").MouseHandler,f=e("./mouse/fold_handler").FoldHandler,l=e("./keyboard/keybinding").KeyBinding,c=e("./edit_session").EditSession,h=e("./search").Search,p=e("./range").Range,d=e("./lib/event_emitter").EventEmitter,v=e("./commands/command_manager").CommandManager,m=e("./commands/default_commands").commands,g=e("./config"),y=e("./token_iterator").TokenIterator,b=function(e,t){var n=e.getContainerElement();this.container=n,this.renderer=e,this.id="editor"+ ++b.$uid,this.commands=new v(o.isMac?"mac":"win",m),typeof document=="object"&&(this.textInput=new u(e.getTextAreaContainer(),this),this.renderer.textarea=this.textInput.getElement(),this.$mouseHandler=new a(this),new f(this)),this.keyBinding=new l(this),this.$blockScrolling=0,this.$search=(new h).set({wrap:!0}),this.$historyTracker=this.$historyTracker.bind(this),this.commands.on("exec",this.$historyTracker),this.$initOperationListeners(),this._$emitInputEvent=s.delayedCall(function(){this._signal("input",{}),this.session&&this.session.bgTokenizer&&this.session.bgTokenizer.scheduleStart()}.bind(this)),this.on("change",function(e,t){t._$emitInputEvent.schedule(31)}),this.setSession(t||new c("")),g.resetOptions(this),g._signal("editor",this)};b.$uid=0,function(){r.implement(this,d),this.$initOperationListeners=function(){function e(e){return e[e.length-1]}this.selections=[],this.commands.on("exec",this.startOperation.bind(this),!0),this.commands.on("afterExec",this.endOperation.bind(this),!0),this.$opResetTimer=s.delayedCall(this.endOperation.bind(this)),this.on("change",function(){this.curOp||this.startOperation(),this.curOp.docChanged=!0}.bind(this),!0),this.on("changeSelection",function(){this.curOp||this.startOperation(),this.curOp.selectionChanged=!0}.bind(this),!0)},this.curOp=null,this.prevOp={},this.startOperation=function(e){if(this.curOp){if(!e||this.curOp.command)return;this.prevOp=this.curOp}e||(this.previousCommand=null,e={}),this.$opResetTimer.schedule(),this.curOp={command:e.command||{},args:e.args,scrollTop:this.renderer.scrollTop},this.curOp.command.name&&this.curOp.command.scrollIntoView!==undefined&&this.$blockScrolling++},this.endOperation=function(e){if(this.curOp){if(e&&e.returnValue===!1)return this.curOp=null;this._signal("beforeEndOperation");var t=this.curOp.command;t.name&&this.$blockScrolling>0&&this.$blockScrolling--;var n=t&&t.scrollIntoView;if(n){switch(n){case"center-animate":n="animate";case"center":this.renderer.scrollCursorIntoView(null,.5);break;case"animate":case"cursor":this.renderer.scrollCursorIntoView();break;case"selectionPart":var r=this.selection.getRange(),i=this.renderer.layerConfig;(r.start.row>=i.lastRow||r.end.row<=i.firstRow)&&this.renderer.scrollSelectionIntoView(this.selection.anchor,this.selection.lead);break;default:}n=="animate"&&this.renderer.animateScrolling(this.curOp.scrollTop)}this.prevOp=this.curOp,this.curOp=null}},this.$mergeableCommands=["backspace","del","insertstring"],this.$historyTracker=function(e){if(!this.$mergeUndoDeltas)return;var t=this.prevOp,n=this.$mergeableCommands,r=t.command&&e.command.name==t.command.name;if(e.command.name=="insertstring"){var i=e.args;this.mergeNextCommand===undefined&&(this.mergeNextCommand=!0),r=r&&this.mergeNextCommand&&(!/\s/.test(i)||/\s/.test(t.args)),this.mergeNextCommand=!0}else r=r&&n.indexOf(e.command.name)!==-1;this.$mergeUndoDeltas!="always"&&Date.now()-this.sequenceStartTime>2e3&&(r=!1),r?this.session.mergeUndoDeltas=!0:n.indexOf(e.command.name)!==-1&&(this.sequenceStartTime=Date.now())},this.setKeyboardHandler=function(e,t){if(e&&typeof e=="string"){this.$keybindingId=e;var n=this;g.loadModule(["keybinding",e],function(r){n.$keybindingId==e&&n.keyBinding.setKeyboardHandler(r&&r.handler),t&&t()})}else this.$keybindingId=null,this.keyBinding.setKeyboardHandler(e),t&&t()},this.getKeyboardHandler=function(){return this.keyBinding.getKeyboardHandler()},this.setSession=function(e){if(this.session==e)return;this.curOp&&this.endOperation(),this.curOp={};var t=this.session;if(t){this.session.off("change",this.$onDocumentChange),this.session.off("changeMode",this.$onChangeMode),this.session.off("tokenizerUpdate",this.$onTokenizerUpdate),this.session.off("changeTabSize",this.$onChangeTabSize),this.session.off("changeWrapLimit",this.$onChangeWrapLimit),this.session.off("changeWrapMode",this.$onChangeWrapMode),this.session.off("changeFold",this.$onChangeFold),this.session.off("changeFrontMarker",this.$onChangeFrontMarker),this.session.off("changeBackMarker",this.$onChangeBackMarker),this.session.off("changeBreakpoint",this.$onChangeBreakpoint),this.session.off("changeAnnotation",this.$onChangeAnnotation),this.session.off("changeOverwrite",this.$onCursorChange),this.session.off("changeScrollTop",this.$onScrollTopChange),this.session.off("changeScrollLeft",this.$onScrollLeftChange);var n=this.session.getSelection();n.off("changeCursor",this.$onCursorChange),n.off("changeSelection",this.$onSelectionChange)}this.session=e,e?(this.$onDocumentChange=this.onDocumentChange.bind(this),e.on("change",this.$onDocumentChange),this.renderer.setSession(e),this.$onChangeMode=this.onChangeMode.bind(this),e.on("changeMode",this.$onChangeMode),this.$onTokenizerUpdate=this.onTokenizerUpdate.bind(this),e.on("tokenizerUpdate",this.$onTokenizerUpdate),this.$onChangeTabSize=this.renderer.onChangeTabSize.bind(this.renderer),e.on("changeTabSize",this.$onChangeTabSize),this.$onChangeWrapLimit=this.onChangeWrapLimit.bind(this),e.on("changeWrapLimit",this.$onChangeWrapLimit),this.$onChangeWrapMode=this.onChangeWrapMode.bind(this),e.on("changeWrapMode",this.$onChangeWrapMode),this.$onChangeFold=this.onChangeFold.bind(this),e.on("changeFold",this.$onChangeFold),this.$onChangeFrontMarker=this.onChangeFrontMarker.bind(this),this.session.on("changeFrontMarker",this.$onChangeFrontMarker),this.$onChangeBackMarker=this.onChangeBackMarker.bind(this),this.session.on("changeBackMarker",this.$onChangeBackMarker),this.$onChangeBreakpoint=this.onChangeBreakpoint.bind(this),this.session.on("changeBreakpoint",this.$onChangeBreakpoint),this.$onChangeAnnotation=this.onChangeAnnotation.bind(this),this.session.on("changeAnnotation",this.$onChangeAnnotation),this.$onCursorChange=this.onCursorChange.bind(this),this.session.on("changeOverwrite",this.$onCursorChange),this.$onScrollTopChange=this.onScrollTopChange.bind(this),this.session.on("changeScrollTop",this.$onScrollTopChange),this.$onScrollLeftChange=this.onScrollLeftChange.bind(this),this.session.on("changeScrollLeft",this.$onScrollLeftChange),this.selection=e.getSelection(),this.selection.on("changeCursor",this.$onCursorChange),this.$onSelectionChange=this.onSelectionChange.bind(this),this.selection.on("changeSelection",this.$onSelectionChange),this.onChangeMode(),this.$blockScrolling+=1,this.onCursorChange(),this.$blockScrolling-=1,this.onScrollTopChange(),this.onScrollLeftChange(),this.onSelectionChange(),this.onChangeFrontMarker(),this.onChangeBackMarker(),this.onChangeBreakpoint(),this.onChangeAnnotation(),this.session.getUseWrapMode()&&this.renderer.adjustWrapLimit(),this.renderer.updateFull()):(this.selection=null,this.renderer.setSession(e)),this._signal("changeSession",{session:e,oldSession:t}),this.curOp=null,t&&t._signal("changeEditor",{oldEditor:this}),e&&e._signal("changeEditor",{editor:this}),e&&e.bgTokenizer&&e.bgTokenizer.scheduleStart()},this.getSession=function(){return this.session},this.setValue=function(e,t){return this.session.doc.setValue(e),t?t==1?this.navigateFileEnd():t==-1&&this.navigateFileStart():this.selectAll(),e},this.getValue=function(){return this.session.getValue()},this.getSelection=function(){return this.selection},this.resize=function(e){this.renderer.onResize(e)},this.setTheme=function(e,t){this.renderer.setTheme(e,t)},this.getTheme=function(){return this.renderer.getTheme()},this.setStyle=function(e){this.renderer.setStyle(e)},this.unsetStyle=function(e){this.renderer.unsetStyle(e)},this.getFontSize=function(){return this.getOption("fontSize")||i.computedStyle(this.container,"fontSize")},this.setFontSize=function(e){this.setOption("fontSize",e)},this.$highlightBrackets=function(){this.session.$bracketHighlight&&(this.session.removeMarker(this.session.$bracketHighlight),this.session.$bracketHighlight=null);if(this.$highlightPending)return;var e=this;this.$highlightPending=!0,setTimeout(function(){e.$highlightPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;var n=t.findMatchingBracket(e.getCursorPosition());if(n)var r=new p(n.row,n.column,n.row,n.column+1);else if(t.$mode.getMatching)var r=t.$mode.getMatching(e.session);r&&(t.$bracketHighlight=t.addMarker(r,"ace_bracket","text"))},50)},this.$highlightTags=function(){if(this.$highlightTagPending)return;var e=this;this.$highlightTagPending=!0,setTimeout(function(){e.$highlightTagPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;var n=e.getCursorPosition(),r=new y(e.session,n.row,n.column),i=r.getCurrentToken();if(!i||!/\b(?:tag-open|tag-name)/.test(i.type)){t.removeMarker(t.$tagHighlight),t.$tagHighlight=null;return}if(i.type.indexOf("tag-open")!=-1){i=r.stepForward();if(!i)return}var s=i.value,o=0,u=r.stepBackward();if(u.value=="<"){do u=i,i=r.stepForward(),i&&i.value===s&&i.type.indexOf("tag-name")!==-1&&(u.value==="<"?o++:u.value==="</"&&o--);while(i&&o>=0)}else{do i=u,u=r.stepBackward(),i&&i.value===s&&i.type.indexOf("tag-name")!==-1&&(u.value==="<"?o++:u.value==="</"&&o--);while(u&&o<=0);r.stepForward()}if(!i){t.removeMarker(t.$tagHighlight),t.$tagHighlight=null;return}var a=r.getCurrentTokenRow(),f=r.getCurrentTokenColumn(),l=new p(a,f,a,f+i.value.length),c=t.$backMarkers[t.$tagHighlight];t.$tagHighlight&&c!=undefined&&l.compareRange(c.range)!==0&&(t.removeMarker(t.$tagHighlight),t.$tagHighlight=null),l&&!t.$tagHighlight&&(t.$tagHighlight=t.addMarker(l,"ace_bracket","text"))},50)},this.focus=function(){var e=this;setTimeout(function(){e.textInput.focus()}),this.textInput.focus()},this.isFocused=function(){return this.textInput.isFocused()},this.blur=function(){this.textInput.blur()},this.onFocus=function(e){if(this.$isFocused)return;this.$isFocused=!0,this.renderer.showCursor(),this.renderer.visualizeFocus(),this._emit("focus",e)},this.onBlur=function(e){if(!this.$isFocused)return;this.$isFocused=!1,this.renderer.hideCursor(),this.renderer.visualizeBlur(),this._emit("blur",e)},this.$cursorChange=function(){this.renderer.updateCursor()},this.onDocumentChange=function(e){var t=this.session.$useWrapMode,n=e.start.row==e.end.row?e.end.row:Infinity;this.renderer.updateLines(e.start.row,n,t),this._signal("change",e),this.$cursorChange(),this.$updateHighlightActiveLine()},this.onTokenizerUpdate=function(e){var t=e.data;this.renderer.updateLines(t.first,t.last)},this.onScrollTopChange=function(){this.renderer.scrollToY(this.session.getScrollTop())},this.onScrollLeftChange=function(){this.renderer.scrollToX(this.session.getScrollLeft())},this.onCursorChange=function(){this.$cursorChange(),this.$blockScrolling||(g.warn("Automatically scrolling cursor into view after selection change","this will be disabled in the next version","set editor.$blockScrolling = Infinity to disable this message"),this.renderer.scrollCursorIntoView()),this.$highlightBrackets(),this.$highlightTags(),this.$updateHighlightActiveLine(),this._signal("changeSelection")},this.$updateHighlightActiveLine=function(){var e=this.getSession(),t;if(this.$highlightActiveLine){if(this.$selectionStyle!="line"||!this.selection.isMultiLine())t=this.getCursorPosition();this.renderer.$maxLines&&this.session.getLength()===1&&!(this.renderer.$minLines>1)&&(t=!1)}if(e.$highlightLineMarker&&!t)e.removeMarker(e.$highlightLineMarker.id),e.$highlightLineMarker=null;else if(!e.$highlightLineMarker&&t){var n=new p(t.row,t.column,t.row,Infinity);n.id=e.addMarker(n,"ace_active-line","screenLine"),e.$highlightLineMarker=n}else t&&(e.$highlightLineMarker.start.row=t.row,e.$highlightLineMarker.end.row=t.row,e.$highlightLineMarker.start.column=t.column,e._signal("changeBackMarker"))},this.onSelectionChange=function(e){var t=this.session;t.$selectionMarker&&t.removeMarker(t.$selectionMarker),t.$selectionMarker=null;if(!this.selection.isEmpty()){var n=this.selection.getRange(),r=this.getSelectionStyle();t.$selectionMarker=t.addMarker(n,"ace_selection",r)}else this.$updateHighlightActiveLine();var i=this.$highlightSelectedWord&&this.$getSelectionHighLightRegexp();this.session.highlight(i),this._signal("changeSelection")},this.$getSelectionHighLightRegexp=function(){var e=this.session,t=this.getSelectionRange();if(t.isEmpty()||t.isMultiLine())return;var n=t.start.column-1,r=t.end.column+1,i=e.getLine(t.start.row),s=i.length,o=i.substring(Math.max(n,0),Math.min(r,s));if(n>=0&&/^[\w\d]/.test(o)||r<=s&&/[\w\d]$/.test(o))return;o=i.substring(t.start.column,t.end.column);if(!/^[\w\d]+$/.test(o))return;var u=this.$search.$assembleRegExp({wholeWord:!0,caseSensitive:!0,needle:o});return u},this.onChangeFrontMarker=function(){this.renderer.updateFrontMarkers()},this.onChangeBackMarker=function(){this.renderer.updateBackMarkers()},this.onChangeBreakpoint=function(){this.renderer.updateBreakpoints()},this.onChangeAnnotation=function(){this.renderer.setAnnotations(this.session.getAnnotations())},this.onChangeMode=function(e){this.renderer.updateText(),this._emit("changeMode",e)},this.onChangeWrapLimit=function(){this.renderer.updateFull()},this.onChangeWrapMode=function(){this.renderer.onResize(!0)},this.onChangeFold=function(){this.$updateHighlightActiveLine(),this.renderer.updateFull()},this.getSelectedText=function(){return this.session.getTextRange(this.getSelectionRange())},this.getCopyText=function(){var e=this.getSelectedText();return this._signal("copy",e),e},this.onCopy=function(){this.commands.exec("copy",this)},this.onCut=function(){this.commands.exec("cut",this)},this.onPaste=function(e,t){var n={text:e,event:t};this.commands.exec("paste",this,n)},this.$handlePaste=function(e){typeof e=="string"&&(e={text:e}),this._signal("paste",e);var t=e.text;if(!this.inMultiSelectMode||this.inVirtualSelectionMode)this.insert(t);else{var n=t.split(/\r\n|\r|\n/),r=this.selection.rangeList.ranges;if(n.length>r.length||n.length<2||!n[1])return this.commands.exec("insertstring",this,t);for(var i=r.length;i--;){var s=r[i];s.isEmpty()||this.session.remove(s),this.session.insert(s.start,n[i])}}},this.execCommand=function(e,t){return this.commands.exec(e,this,t)},this.insert=function(e,t){var n=this.session,r=n.getMode(),i=this.getCursorPosition();if(this.getBehavioursEnabled()&&!t){var s=r.transformAction(n.getState(i.row),"insertion",this,n,e);s&&(e!==s.text&&(this.session.mergeUndoDeltas=!1,this.$mergeNextCommand=!1),e=s.text)}e=="	"&&(e=this.session.getTabString());if(!this.selection.isEmpty()){var o=this.getSelectionRange();i=this.session.remove(o),this.clearSelection()}else if(this.session.getOverwrite()&&e.indexOf("\n")==-1){var o=new p.fromPoints(i,i);o.end.column+=e.length,this.session.remove(o)}if(e=="\n"||e=="\r\n"){var u=n.getLine(i.row);if(i.column>u.search(/\S|$/)){var a=u.substr(i.column).search(/\S|$/);n.doc.removeInLine(i.row,i.column,i.column+a)}}this.clearSelection();var f=i.column,l=n.getState(i.row),u=n.getLine(i.row),c=r.checkOutdent(l,u,e),h=n.insert(i,e);s&&s.selection&&(s.selection.length==2?this.selection.setSelectionRange(new p(i.row,f+s.selection[0],i.row,f+s.selection[1])):this.selection.setSelectionRange(new p(i.row+s.selection[0],s.selection[1],i.row+s.selection[2],s.selection[3])));if(n.getDocument().isNewLine(e)){var d=r.getNextLineIndent(l,u.slice(0,i.column),n.getTabString());n.insert({row:i.row+1,column:0},d)}c&&r.autoOutdent(l,n,i.row)},this.onTextInput=function(e){this.keyBinding.onTextInput(e)},this.onCommandKey=function(e,t,n){this.keyBinding.onCommandKey(e,t,n)},this.setOverwrite=function(e){this.session.setOverwrite(e)},this.getOverwrite=function(){return this.session.getOverwrite()},this.toggleOverwrite=function(){this.session.toggleOverwrite()},this.setScrollSpeed=function(e){this.setOption("scrollSpeed",e)},this.getScrollSpeed=function(){return this.getOption("scrollSpeed")},this.setDragDelay=function(e){this.setOption("dragDelay",e)},this.getDragDelay=function(){return this.getOption("dragDelay")},this.setSelectionStyle=function(e){this.setOption("selectionStyle",e)},this.getSelectionStyle=function(){return this.getOption("selectionStyle")},this.setHighlightActiveLine=function(e){this.setOption("highlightActiveLine",e)},this.getHighlightActiveLine=function(){return this.getOption("highlightActiveLine")},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.setHighlightSelectedWord=function(e){this.setOption("highlightSelectedWord",e)},this.getHighlightSelectedWord=function(){return this.$highlightSelectedWord},this.setAnimatedScroll=function(e){this.renderer.setAnimatedScroll(e)},this.getAnimatedScroll=function(){return this.renderer.getAnimatedScroll()},this.setShowInvisibles=function(e){this.renderer.setShowInvisibles(e)},this.getShowInvisibles=function(){return this.renderer.getShowInvisibles()},this.setDisplayIndentGuides=function(e){this.renderer.setDisplayIndentGuides(e)},this.getDisplayIndentGuides=function(){return this.renderer.getDisplayIndentGuides()},this.setShowPrintMargin=function(e){this.renderer.setShowPrintMargin(e)},this.getShowPrintMargin=function(){return this.renderer.getShowPrintMargin()},this.setPrintMarginColumn=function(e){this.renderer.setPrintMarginColumn(e)},this.getPrintMarginColumn=function(){return this.renderer.getPrintMarginColumn()},this.setReadOnly=function(e){this.setOption("readOnly",e)},this.getReadOnly=function(){return this.getOption("readOnly")},this.setBehavioursEnabled=function(e){this.setOption("behavioursEnabled",e)},this.getBehavioursEnabled=function(){return this.getOption("behavioursEnabled")},this.setWrapBehavioursEnabled=function(e){this.setOption("wrapBehavioursEnabled",e)},this.getWrapBehavioursEnabled=function(){return this.getOption("wrapBehavioursEnabled")},this.setShowFoldWidgets=function(e){this.setOption("showFoldWidgets",e)},this.getShowFoldWidgets=function(){return this.getOption("showFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.remove=function(e){this.selection.isEmpty()&&(e=="left"?this.selection.selectLeft():this.selection.selectRight());var t=this.getSelectionRange();if(this.getBehavioursEnabled()){var n=this.session,r=n.getState(t.start.row),i=n.getMode().transformAction(r,"deletion",this,n,t);if(t.end.column===0){var s=n.getTextRange(t);if(s[s.length-1]=="\n"){var o=n.getLine(t.end.row);/^\s+$/.test(o)&&(t.end.column=o.length)}}i&&(t=i)}this.session.remove(t),this.clearSelection()},this.removeWordRight=function(){this.selection.isEmpty()&&this.selection.selectWordRight(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeWordLeft=function(){this.selection.isEmpty()&&this.selection.selectWordLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineStart=function(){this.selection.isEmpty()&&this.selection.selectLineStart(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineEnd=function(){this.selection.isEmpty()&&this.selection.selectLineEnd();var e=this.getSelectionRange();e.start.column==e.end.column&&e.start.row==e.end.row&&(e.end.column=0,e.end.row++),this.session.remove(e),this.clearSelection()},this.splitLine=function(){this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection());var e=this.getCursorPosition();this.insert("\n"),this.moveCursorToPosition(e)},this.transposeLetters=function(){if(!this.selection.isEmpty())return;var e=this.getCursorPosition(),t=e.column;if(t===0)return;var n=this.session.getLine(e.row),r,i;t<n.length?(r=n.charAt(t)+n.charAt(t-1),i=new p(e.row,t-1,e.row,t+1)):(r=n.charAt(t-1)+n.charAt(t-2),i=new p(e.row,t-2,e.row,t)),this.session.replace(i,r),this.session.selection.moveToPosition(i.end)},this.toLowerCase=function(){var e=this.getSelectionRange();this.selection.isEmpty()&&this.selection.selectWord();var t=this.getSelectionRange(),n=this.session.getTextRange(t);this.session.replace(t,n.toLowerCase()),this.selection.setSelectionRange(e)},this.toUpperCase=function(){var e=this.getSelectionRange();this.selection.isEmpty()&&this.selection.selectWord();var t=this.getSelectionRange(),n=this.session.getTextRange(t);this.session.replace(t,n.toUpperCase()),this.selection.setSelectionRange(e)},this.indent=function(){var e=this.session,t=this.getSelectionRange();if(t.start.row<t.end.row){var n=this.$getSelectedRows();e.indentRows(n.first,n.last,"	");return}if(t.start.column<t.end.column){var r=e.getTextRange(t);if(!/^\s+$/.test(r)){var n=this.$getSelectedRows();e.indentRows(n.first,n.last,"	");return}}var i=e.getLine(t.start.row),o=t.start,u=e.getTabSize(),a=e.documentToScreenColumn(o.row,o.column);if(this.session.getUseSoftTabs())var f=u-a%u,l=s.stringRepeat(" ",f);else{var f=a%u;while(i[t.start.column-1]==" "&&f)t.start.column--,f--;this.selection.setSelectionRange(t),l="	"}return this.insert(l)},this.blockIndent=function(){var e=this.$getSelectedRows();this.session.indentRows(e.first,e.last,"	")},this.blockOutdent=function(){var e=this.session.getSelection();this.session.outdentRows(e.getRange())},this.sortLines=function(){var e=this.$getSelectedRows(),t=this.session,n=[];for(i=e.first;i<=e.last;i++)n.push(t.getLine(i));n.sort(function(e,t){return e.toLowerCase()<t.toLowerCase()?-1:e.toLowerCase()>t.toLowerCase()?1:0});var r=new p(0,0,0,0);for(var i=e.first;i<=e.last;i++){var s=t.getLine(i);r.start.row=i,r.end.row=i,r.end.column=s.length,t.replace(r,n[i-e.first])}},this.toggleCommentLines=function(){var e=this.session.getState(this.getCursorPosition().row),t=this.$getSelectedRows();this.session.getMode().toggleCommentLines(e,this.session,t.first,t.last)},this.toggleBlockComment=function(){var e=this.getCursorPosition(),t=this.session.getState(e.row),n=this.getSelectionRange();this.session.getMode().toggleBlockComment(t,this.session,n,e)},this.getNumberAt=function(e,t){var n=/[\-]?[0-9]+(?:\.[0-9]+)?/g;n.lastIndex=0;var r=this.session.getLine(e);while(n.lastIndex<t){var i=n.exec(r);if(i.index<=t&&i.index+i[0].length>=t){var s={value:i[0],start:i.index,end:i.index+i[0].length};return s}}return null},this.modifyNumber=function(e){var t=this.selection.getCursor().row,n=this.selection.getCursor().column,r=new p(t,n-1,t,n),i=this.session.getTextRange(r);if(!isNaN(parseFloat(i))&&isFinite(i)){var s=this.getNumberAt(t,n);if(s){var o=s.value.indexOf(".")>=0?s.start+s.value.indexOf(".")+1:s.end,u=s.start+s.value.length-o,a=parseFloat(s.value);a*=Math.pow(10,u),o!==s.end&&n<o?e*=Math.pow(10,s.end-n-1):e*=Math.pow(10,s.end-n),a+=e,a/=Math.pow(10,u);var f=a.toFixed(u),l=new p(t,s.start,t,s.end);this.session.replace(l,f),this.moveCursorTo(t,Math.max(s.start+1,n+f.length-s.value.length))}}},this.removeLines=function(){var e=this.$getSelectedRows();this.session.removeFullLines(e.first,e.last),this.clearSelection()},this.duplicateSelection=function(){var e=this.selection,t=this.session,n=e.getRange(),r=e.isBackwards();if(n.isEmpty()){var i=n.start.row;t.duplicateLines(i,i)}else{var s=r?n.start:n.end,o=t.insert(s,t.getTextRange(n),!1);n.start=s,n.end=o,e.setSelectionRange(n,r)}},this.moveLinesDown=function(){this.$moveLines(1,!1)},this.moveLinesUp=function(){this.$moveLines(-1,!1)},this.moveText=function(e,t,n){return this.session.moveText(e,t,n)},this.copyLinesUp=function(){this.$moveLines(-1,!0)},this.copyLinesDown=function(){this.$moveLines(1,!0)},this.$moveLines=function(e,t){var n,r,i=this.selection;if(!i.inMultiSelectMode||this.inVirtualSelectionMode){var s=i.toOrientedRange();n=this.$getSelectedRows(s),r=this.session.$moveLines(n.first,n.last,t?0:e),t&&e==-1&&(r=0),s.moveBy(r,0),i.fromOrientedRange(s)}else{var o=i.rangeList.ranges;i.rangeList.detach(this.session),this.inVirtualSelectionMode=!0;var u=0,a=0,f=o.length;for(var l=0;l<f;l++){var c=l;o[l].moveBy(u,0),n=this.$getSelectedRows(o[l]);var h=n.first,p=n.last;while(++l<f){a&&o[l].moveBy(a,0);var d=this.$getSelectedRows(o[l]);if(t&&d.first!=p)break;if(!t&&d.first>p+1)break;p=d.last}l--,u=this.session.$moveLines(h,p,t?0:e),t&&e==-1&&(c=l+1);while(c<=l)o[c].moveBy(u,0),c++;t||(u=0),a+=u}i.fromOrientedRange(i.ranges[0]),i.rangeList.attach(this.session),this.inVirtualSelectionMode=!1}},this.$getSelectedRows=function(e){return e=(e||this.getSelectionRange()).collapseRows(),{first:this.session.getRowFoldStart(e.start.row),last:this.session.getRowFoldEnd(e.end.row)}},this.onCompositionStart=function(e){this.renderer.showComposition(this.getCursorPosition())},this.onCompositionUpdate=function(e){this.renderer.setCompositionText(e)},this.onCompositionEnd=function(){this.renderer.hideComposition()},this.getFirstVisibleRow=function(){return this.renderer.getFirstVisibleRow()},this.getLastVisibleRow=function(){return this.renderer.getLastVisibleRow()},this.isRowVisible=function(e){return e>=this.getFirstVisibleRow()&&e<=this.getLastVisibleRow()},this.isRowFullyVisible=function(e){return e>=this.renderer.getFirstFullyVisibleRow()&&e<=this.renderer.getLastFullyVisibleRow()},this.$getVisibleRowCount=function(){return this.renderer.getScrollBottomRow()-this.renderer.getScrollTopRow()+1},this.$moveByPage=function(e,t){var n=this.renderer,r=this.renderer.layerConfig,i=e*Math.floor(r.height/r.lineHeight);this.$blockScrolling++,t===!0?this.selection.$moveSelection(function(){this.moveCursorBy(i,0)}):t===!1&&(this.selection.moveCursorBy(i,0),this.selection.clearSelection()),this.$blockScrolling--;var s=n.scrollTop;n.scrollBy(0,i*r.lineHeight),t!=null&&n.scrollCursorIntoView(null,.5),n.animateScrolling(s)},this.selectPageDown=function(){this.$moveByPage(1,!0)},this.selectPageUp=function(){this.$moveByPage(-1,!0)},this.gotoPageDown=function(){this.$moveByPage(1,!1)},this.gotoPageUp=function(){this.$moveByPage(-1,!1)},this.scrollPageDown=function(){this.$moveByPage(1)},this.scrollPageUp=function(){this.$moveByPage(-1)},this.scrollToRow=function(e){this.renderer.scrollToRow(e)},this.scrollToLine=function(e,t,n,r){this.renderer.scrollToLine(e,t,n,r)},this.centerSelection=function(){var e=this.getSelectionRange(),t={row:Math.floor(e.start.row+(e.end.row-e.start.row)/2),column:Math.floor(e.start.column+(e.end.column-e.start.column)/2)};this.renderer.alignCursor(t,.5)},this.getCursorPosition=function(){return this.selection.getCursor()},this.getCursorPositionScreen=function(){return this.session.documentToScreenPosition(this.getCursorPosition())},this.getSelectionRange=function(){return this.selection.getRange()},this.selectAll=function(){this.$blockScrolling+=1,this.selection.selectAll(),this.$blockScrolling-=1},this.clearSelection=function(){this.selection.clearSelection()},this.moveCursorTo=function(e,t){this.selection.moveCursorTo(e,t)},this.moveCursorToPosition=function(e){this.selection.moveCursorToPosition(e)},this.jumpToMatching=function(e,t){var n=this.getCursorPosition(),r=new y(this.session,n.row,n.column),i=r.getCurrentToken(),s=i||r.stepForward();if(!s)return;var o,u=!1,a={},f=n.column-s.start,l,c={")":"(","(":"(","]":"[","[":"[","{":"{","}":"{"};do{if(s.value.match(/[{}()\[\]]/g))for(;f<s.value.length&&!u;f++){if(!c[s.value[f]])continue;l=c[s.value[f]]+"."+s.type.replace("rparen","lparen"),isNaN(a[l])&&(a[l]=0);switch(s.value[f]){case"(":case"[":case"{":a[l]++;break;case")":case"]":case"}":a[l]--,a[l]===-1&&(o="bracket",u=!0)}}else s&&s.type.indexOf("tag-name")!==-1&&(isNaN(a[s.value])&&(a[s.value]=0),i.value==="<"?a[s.value]++:i.value==="</"&&a[s.value]--,a[s.value]===-1&&(o="tag",u=!0));u||(i=s,s=r.stepForward(),f=0)}while(s&&!u);if(!o)return;var h,d;if(o==="bracket"){h=this.session.getBracketRange(n);if(!h){h=new p(r.getCurrentTokenRow(),r.getCurrentTokenColumn()+f-1,r.getCurrentTokenRow(),r.getCurrentTokenColumn()+f-1),d=h.start;if(t||d.row===n.row&&Math.abs(d.column-n.column)<2)h=this.session.getBracketRange(d)}}else if(o==="tag"){if(!s||s.type.indexOf("tag-name")===-1)return;var v=s.value;h=new p(r.getCurrentTokenRow(),r.getCurrentTokenColumn()-2,r.getCurrentTokenRow(),r.getCurrentTokenColumn()-2);if(h.compare(n.row,n.column)===0){u=!1;do s=i,i=r.stepBackward(),i&&(i.type.indexOf("tag-close")!==-1&&h.setEnd(r.getCurrentTokenRow(),r.getCurrentTokenColumn()+1),s.value===v&&s.type.indexOf("tag-name")!==-1&&(i.value==="<"?a[v]++:i.value==="</"&&a[v]--,a[v]===0&&(u=!0)));while(i&&!u)}s&&s.type.indexOf("tag-name")&&(d=h.start,d.row==n.row&&Math.abs(d.column-n.column)<2&&(d=h.end))}d=h&&h.cursor||d,d&&(e?h&&t?this.selection.setRange(h):h&&h.isEqual(this.getSelectionRange())?this.clearSelection():this.selection.selectTo(d.row,d.column):this.selection.moveTo(d.row,d.column))},this.gotoLine=function(e,t,n){this.selection.clearSelection(),this.session.unfold({row:e-1,column:t||0}),this.$blockScrolling+=1,this.exitMultiSelectMode&&this.exitMultiSelectMode(),this.moveCursorTo(e-1,t||0),this.$blockScrolling-=1,this.isRowFullyVisible(e-1)||this.scrollToLine(e-1,!0,n)},this.navigateTo=function(e,t){this.selection.moveTo(e,t)},this.navigateUp=function(e){if(this.selection.isMultiLine()&&!this.selection.isBackwards()){var t=this.selection.anchor.getPosition();return this.moveCursorToPosition(t)}this.selection.clearSelection(),this.selection.moveCursorBy(-e||-1,0)},this.navigateDown=function(e){if(this.selection.isMultiLine()&&this.selection.isBackwards()){var t=this.selection.anchor.getPosition();return this.moveCursorToPosition(t)}this.selection.clearSelection(),this.selection.moveCursorBy(e||1,0)},this.navigateLeft=function(e){if(!this.selection.isEmpty()){var t=this.getSelectionRange().start;this.moveCursorToPosition(t)}else{e=e||1;while(e--)this.selection.moveCursorLeft()}this.clearSelection()},this.navigateRight=function(e){if(!this.selection.isEmpty()){var t=this.getSelectionRange().end;this.moveCursorToPosition(t)}else{e=e||1;while(e--)this.selection.moveCursorRight()}this.clearSelection()},this.navigateLineStart=function(){this.selection.moveCursorLineStart(),this.clearSelection()},this.navigateLineEnd=function(){this.selection.moveCursorLineEnd(),this.clearSelection()},this.navigateFileEnd=function(){this.selection.moveCursorFileEnd(),this.clearSelection()},this.navigateFileStart=function(){this.selection.moveCursorFileStart(),this.clearSelection()},this.navigateWordRight=function(){this.selection.moveCursorWordRight(),this.clearSelection()},this.navigateWordLeft=function(){this.selection.moveCursorWordLeft(),this.clearSelection()},this.replace=function(e,t){t&&this.$search.set(t);var n=this.$search.find(this.session),r=0;return n?(this.$tryReplace(n,e)&&(r=1),n!==null&&(this.selection.setSelectionRange(n),this.renderer.scrollSelectionIntoView(n.start,n.end)),r):r},this.replaceAll=function(e,t){t&&this.$search.set(t);var n=this.$search.findAll(this.session),r=0;if(!n.length)return r;this.$blockScrolling+=1;var i=this.getSelectionRange();this.selection.moveTo(0,0);for(var s=n.length-1;s>=0;--s)this.$tryReplace(n[s],e)&&r++;return this.selection.setSelectionRange(i),this.$blockScrolling-=1,r},this.$tryReplace=function(e,t){var n=this.session.getTextRange(e);return t=this.$search.replace(n,t),t!==null?(e.end=this.session.replace(e,t),e):null},this.getLastSearchOptions=function(){return this.$search.getOptions()},this.find=function(e,t,n){t||(t={}),typeof e=="string"||e instanceof RegExp?t.needle=e:typeof e=="object"&&r.mixin(t,e);var i=this.selection.getRange();t.needle==null&&(e=this.session.getTextRange(i)||this.$search.$options.needle,e||(i=this.session.getWordRange(i.start.row,i.start.column),e=this.session.getTextRange(i)),this.$search.set({needle:e})),this.$search.set(t),t.start||this.$search.set({start:i});var s=this.$search.find(this.session);if(t.preventScroll)return s;if(s)return this.revealRange(s,n),s;t.backwards?i.start=i.end:i.end=i.start,this.selection.setRange(i)},this.findNext=function(e,t){this.find({skipCurrent:!0,backwards:!1},e,t)},this.findPrevious=function(e,t){this.find(e,{skipCurrent:!0,backwards:!0},t)},this.revealRange=function(e,t){this.$blockScrolling+=1,this.session.unfold(e),this.selection.setSelectionRange(e),this.$blockScrolling-=1;var n=this.renderer.scrollTop;this.renderer.scrollSelectionIntoView(e.start,e.end,.5),t!==!1&&this.renderer.animateScrolling(n)},this.undo=function(){this.$blockScrolling++,this.session.getUndoManager().undo(),this.$blockScrolling--,this.renderer.scrollCursorIntoView(null,.5)},this.redo=function(){this.$blockScrolling++,this.session.getUndoManager().redo(),this.$blockScrolling--,this.renderer.scrollCursorIntoView(null,.5)},this.destroy=function(){this.renderer.destroy(),this._signal("destroy",this),this.session&&this.session.destroy()},this.setAutoScrollEditorIntoView=function(e){if(!e)return;var t,n=this,r=!1;this.$scrollAnchor||(this.$scrollAnchor=document.createElement("div"));var i=this.$scrollAnchor;i.style.cssText="position:absolute",this.container.insertBefore(i,this.container.firstChild);var s=this.on("changeSelection",function(){r=!0}),o=this.renderer.on("beforeRender",function(){r&&(t=n.renderer.container.getBoundingClientRect())}),u=this.renderer.on("afterRender",function(){if(r&&t&&(n.isFocused()||n.searchBox&&n.searchBox.isFocused())){var e=n.renderer,s=e.$cursorLayer.$pixelPos,o=e.layerConfig,u=s.top-o.offset;s.top>=0&&u+t.top<0?r=!0:s.top<o.height&&s.top+t.top+o.lineHeight>window.innerHeight?r=!1:r=null,r!=null&&(i.style.top=u+"px",i.style.left=s.left+"px",i.style.height=o.lineHeight+"px",i.scrollIntoView(r)),r=t=null}});this.setAutoScrollEditorIntoView=function(e){if(e)return;delete this.setAutoScrollEditorIntoView,this.off("changeSelection",s),this.renderer.off("afterRender",u),this.renderer.off("beforeRender",o)}},this.$resetCursorStyle=function(){var e=this.$cursorStyle||"ace",t=this.renderer.$cursorLayer;if(!t)return;t.setSmoothBlinking(/smooth/.test(e)),t.isBlinking=!this.$readOnly&&e!="wide",i.setCssClass(t.element,"ace_slim-cursors",/slim/.test(e))}}.call(b.prototype),g.defineOptions(b.prototype,"editor",{selectionStyle:{set:function(e){this.onSelectionChange(),this._signal("changeSelectionStyle",{data:e})},initialValue:"line"},highlightActiveLine:{set:function(){this.$updateHighlightActiveLine()},initialValue:!0},highlightSelectedWord:{set:function(e){this.$onSelectionChange()},initialValue:!0},readOnly:{set:function(e){this.$resetCursorStyle()},initialValue:!1},cursorStyle:{set:function(e){this.$resetCursorStyle()},values:["ace","slim","smooth","wide"],initialValue:"ace"},mergeUndoDeltas:{values:[!1,!0,"always"],initialValue:!0},behavioursEnabled:{initialValue:!0},wrapBehavioursEnabled:{initialValue:!0},autoScrollEditorIntoView:{set:function(e){this.setAutoScrollEditorIntoView(e)}},keyboardHandler:{set:function(e){this.setKeyboardHandler(e)},get:function(){return this.keybindingId},handlesSet:!0},hScrollBarAlwaysVisible:"renderer",vScrollBarAlwaysVisible:"renderer",highlightGutterLine:"renderer",animatedScroll:"renderer",showInvisibles:"renderer",showPrintMargin:"renderer",printMarginColumn:"renderer",printMargin:"renderer",fadeFoldWidgets:"renderer",showFoldWidgets:"renderer",showLineNumbers:"renderer",showGutter:"renderer",displayIndentGuides:"renderer",fontSize:"renderer",fontFamily:"renderer",maxLines:"renderer",minLines:"renderer",scrollPastEnd:"renderer",fixedWidthGutter:"renderer",theme:"renderer",scrollSpeed:"$mouseHandler",dragDelay:"$mouseHandler",dragEnabled:"$mouseHandler",focusTimout:"$mouseHandler",tooltipFollowsMouse:"$mouseHandler",firstLineNumber:"session",overwrite:"session",newLineMode:"session",useWorker:"session",useSoftTabs:"session",tabSize:"session",wrap:"session",indentedSoftWrap:"session",foldStyle:"session",mode:"session"}),t.Editor=b}),define("ace/undomanager",["require","exports","module"],function(e,t,n){"use strict";var r=function(){this.reset()};(function(){function e(e){return{action:e.action,start:e.start,end:e.end,lines:e.lines.length==1?null:e.lines,text:e.lines.length==1?e.lines[0]:null}}function t(e){return{action:e.action,start:e.start,end:e.end,lines:e.lines||[e.text]}}function n(e,t){var n=new Array(e.length);for(var r=0;r<e.length;r++){var i=e[r],s={group:i.group,deltas:new Array(i.length)};for(var o=0;o<i.deltas.length;o++){var u=i.deltas[o];s.deltas[o]=t(u)}n[r]=s}return n}this.execute=function(e){var t=e.args[0];this.$doc=e.args[1],e.merge&&this.hasUndo()&&(this.dirtyCounter--,t=this.$undoStack.pop().concat(t)),this.$undoStack.push(t),this.$redoStack=[],this.dirtyCounter<0&&(this.dirtyCounter=NaN),this.dirtyCounter++},this.undo=function(e){var t=this.$undoStack.pop(),n=null;return t&&(n=this.$doc.undoChanges(t,e),this.$redoStack.push(t),this.dirtyCounter--),n},this.redo=function(e){var t=this.$redoStack.pop(),n=null;return t&&(n=this.$doc.redoChanges(this.$deserializeDeltas(t),e),this.$undoStack.push(t),this.dirtyCounter++),n},this.reset=function(){this.$undoStack=[],this.$redoStack=[],this.dirtyCounter=0},this.hasUndo=function(){return this.$undoStack.length>0},this.hasRedo=function(){return this.$redoStack.length>0},this.markClean=function(){this.dirtyCounter=0},this.isClean=function(){return this.dirtyCounter===0},this.$serializeDeltas=function(t){return n(t,e)},this.$deserializeDeltas=function(e){return n(e,t)}}).call(r.prototype),t.UndoManager=r}),define("ace/layer/gutter",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=e("../lib/oop"),s=e("../lib/lang"),o=e("../lib/event_emitter").EventEmitter,u=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_gutter-layer",e.appendChild(this.element),this.setShowFoldWidgets(this.$showFoldWidgets),this.gutterWidth=0,this.$annotations=[],this.$updateAnnotations=this.$updateAnnotations.bind(this),this.$cells=[]};(function(){i.implement(this,o),this.setSession=function(e){this.session&&this.session.removeEventListener("change",this.$updateAnnotations),this.session=e,e&&e.on("change",this.$updateAnnotations)},this.addGutterDecoration=function(e,t){window.console&&console.warn&&console.warn("deprecated use session.addGutterDecoration"),this.session.addGutterDecoration(e,t)},this.removeGutterDecoration=function(e,t){window.console&&console.warn&&console.warn("deprecated use session.removeGutterDecoration"),this.session.removeGutterDecoration(e,t)},this.setAnnotations=function(e){this.$annotations=[];for(var t=0;t<e.length;t++){var n=e[t],r=n.row,i=this.$annotations[r];i||(i=this.$annotations[r]={text:[]});var o=n.text;o=o?s.escapeHTML(o):n.html||"",i.text.indexOf(o)===-1&&i.text.push(o);var u=n.type;u=="error"?i.className=" ace_error":u=="warning"&&i.className!=" ace_error"?i.className=" ace_warning":u=="info"&&!i.className&&(i.className=" ace_info")}},this.$updateAnnotations=function(e){if(!this.$annotations.length)return;var t=e.start.row,n=e.end.row-t;if(n!==0)if(e.action=="remove")this.$annotations.splice(t,n+1,null);else{var r=new Array(n+1);r.unshift(t,1),this.$annotations.splice.apply(this.$annotations,r)}},this.update=function(e){var t=this.session,n=e.firstRow,i=Math.min(e.lastRow+e.gutterOffset,t.getLength()-1),s=t.getNextFoldLine(n),o=s?s.start.row:Infinity,u=this.$showFoldWidgets&&t.foldWidgets,a=t.$breakpoints,f=t.$decorations,l=t.$firstLineNumber,c=0,h=t.gutterRenderer||this.$renderer,p=null,d=-1,v=n;for(;;){v>o&&(v=s.end.row+1,s=t.getNextFoldLine(v,s),o=s?s.start.row:Infinity);if(v>i){while(this.$cells.length>d+1)p=this.$cells.pop(),this.element.removeChild(p.element);break}p=this.$cells[++d],p||(p={element:null,textNode:null,foldWidget:null},p.element=r.createElement("div"),p.textNode=document.createTextNode(""),p.element.appendChild(p.textNode),this.element.appendChild(p.element),this.$cells[d]=p);var m="ace_gutter-cell ";a[v]&&(m+=a[v]),f[v]&&(m+=f[v]),this.$annotations[v]&&(m+=this.$annotations[v].className),p.element.className!=m&&(p.element.className=m);var g=t.getRowLength(v)*e.lineHeight+"px";g!=p.element.style.height&&(p.element.style.height=g);if(u){var y=u[v];y==null&&(y=u[v]=t.getFoldWidget(v))}if(y){p.foldWidget||(p.foldWidget=r.createElement("span"),p.element.appendChild(p.foldWidget));var m="ace_fold-widget ace_"+y;y=="start"&&v==o&&v<s.end.row?m+=" ace_closed":m+=" ace_open",p.foldWidget.className!=m&&(p.foldWidget.className=m);var g=e.lineHeight+"px";p.foldWidget.style.height!=g&&(p.foldWidget.style.height=g)}else p.foldWidget&&(p.element.removeChild(p.foldWidget),p.foldWidget=null);var b=c=h?h.getText(t,v):v+l;b!==p.textNode.data&&(p.textNode.data=b),v++}this.element.style.height=e.minHeight+"px";if(this.$fixedWidth||t.$useWrapMode)c=t.getLength()+l;var w=h?h.getWidth(t,c,e):c.toString().length*e.characterWidth,E=this.$padding||this.$computePadding();w+=E.left+E.right,w!==this.gutterWidth&&!isNaN(w)&&(this.gutterWidth=w,this.element.style.width=Math.ceil(this.gutterWidth)+"px",this._emit("changeGutterWidth",w))},this.$fixedWidth=!1,this.$showLineNumbers=!0,this.$renderer="",this.setShowLineNumbers=function(e){this.$renderer=!e&&{getWidth:function(){return""},getText:function(){return""}}},this.getShowLineNumbers=function(){return this.$showLineNumbers},this.$showFoldWidgets=!0,this.setShowFoldWidgets=function(e){e?r.addCssClass(this.element,"ace_folding-enabled"):r.removeCssClass(this.element,"ace_folding-enabled"),this.$showFoldWidgets=e,this.$padding=null},this.getShowFoldWidgets=function(){return this.$showFoldWidgets},this.$computePadding=function(){if(!this.element.firstChild)return{left:0,right:0};var e=r.computedStyle(this.element.firstChild);return this.$padding={},this.$padding.left=parseInt(e.paddingLeft)+1||0,this.$padding.right=parseInt(e.paddingRight)||0,this.$padding},this.getRegion=function(e){var t=this.$padding||this.$computePadding(),n=this.element.getBoundingClientRect();if(e.x<t.left+n.left)return"markers";if(this.$showFoldWidgets&&e.x>n.right-t.right)return"foldWidgets"}}).call(u.prototype),t.Gutter=u}),define("ace/layer/marker",["require","exports","module","ace/range","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../range").Range,i=e("../lib/dom"),s=function(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_marker-layer",e.appendChild(this.element)};(function(){function e(e,t,n,r){return(e?1:0)|(t?2:0)|(n?4:0)|(r?8:0)}this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setMarkers=function(e){this.markers=e},this.update=function(e){var e=e||this.config;if(!e)return;this.config=e;var t=[];for(var n in this.markers){var r=this.markers[n];if(!r.range){r.update(t,this,this.session,e);continue}var i=r.range.clipRows(e.firstRow,e.lastRow);if(i.isEmpty())continue;i=i.toScreenRange(this.session);if(r.renderer){var s=this.$getTop(i.start.row,e),o=this.$padding+i.start.column*e.characterWidth;r.renderer(t,i,o,s,e)}else r.type=="fullLine"?this.drawFullLineMarker(t,i,r.clazz,e):r.type=="screenLine"?this.drawScreenLineMarker(t,i,r.clazz,e):i.isMultiLine()?r.type=="text"?this.drawTextMarker(t,i,r.clazz,e):this.drawMultiLineMarker(t,i,r.clazz,e):this.drawSingleLineMarker(t,i,r.clazz+" ace_start"+" ace_br15",e)}this.element.innerHTML=t.join("")},this.$getTop=function(e,t){return(e-t.firstRowScreen)*t.lineHeight},this.drawTextMarker=function(t,n,i,s,o){var u=this.session,a=n.start.row,f=n.end.row,l=a,c=0,h=0,p=u.getScreenLastRowColumn(l),d=new r(l,n.start.column,l,h);for(;l<=f;l++)d.start.row=d.end.row=l,d.start.column=l==a?n.start.column:u.getRowWrapIndent(l),d.end.column=p,c=h,h=p,p=l+1<f?u.getScreenLastRowColumn(l+1):l==f?0:n.end.column,this.drawSingleLineMarker(t,d,i+(l==a?" ace_start":"")+" ace_br"+e(l==a||l==a+1&&n.start.column,c<h,h>p,l==f),s,l==f?0:1,o)},this.drawMultiLineMarker=function(e,t,n,r,i){var s=this.$padding,o=r.lineHeight,u=this.$getTop(t.start.row,r),a=s+t.start.column*r.characterWidth;i=i||"",e.push("<div class='",n," ace_br1 ace_start' style='","height:",o,"px;","right:0;","top:",u,"px;","left:",a,"px;",i,"'></div>"),u=this.$getTop(t.end.row,r);var f=t.end.column*r.characterWidth;e.push("<div class='",n," ace_br12' style='","height:",o,"px;","width:",f,"px;","top:",u,"px;","left:",s,"px;",i,"'></div>"),o=(t.end.row-t.start.row-1)*r.lineHeight;if(o<=0)return;u=this.$getTop(t.start.row+1,r);var l=(t.start.column?1:0)|(t.end.column?0:8);e.push("<div class='",n,l?" ace_br"+l:"","' style='","height:",o,"px;","right:0;","top:",u,"px;","left:",s,"px;",i,"'></div>")},this.drawSingleLineMarker=function(e,t,n,r,i,s){var o=r.lineHeight,u=(t.end.column+(i||0)-t.start.column)*r.characterWidth,a=this.$getTop(t.start.row,r),f=this.$padding+t.start.column*r.characterWidth;e.push("<div class='",n,"' style='","height:",o,"px;","width:",u,"px;","top:",a,"px;","left:",f,"px;",s||"","'></div>")},this.drawFullLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;t.start.row!=t.end.row&&(o+=this.$getTop(t.end.row,r)-s),e.push("<div class='",n,"' style='","height:",o,"px;","top:",s,"px;","left:0;right:0;",i||"","'></div>")},this.drawScreenLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;e.push("<div class='",n,"' style='","height:",o,"px;","top:",s,"px;","left:0;right:0;",i||"","'></div>")}}).call(s.prototype),t.Marker=s}),define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("../lib/useragent"),u=e("../lib/event_emitter").EventEmitter,a=function(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_text-layer",e.appendChild(this.element),this.$updateEolChar=this.$updateEolChar.bind(this)};(function(){r.implement(this,u),this.EOF_CHAR="\u00b6",this.EOL_CHAR_LF="\u00ac",this.EOL_CHAR_CRLF="\u00a4",this.EOL_CHAR=this.EOL_CHAR_LF,this.TAB_CHAR="\u2014",this.SPACE_CHAR="\u00b7",this.$padding=0,this.$updateEolChar=function(){var e=this.session.doc.getNewLineCharacter()=="\n"?this.EOL_CHAR_LF:this.EOL_CHAR_CRLF;if(this.EOL_CHAR!=e)return this.EOL_CHAR=e,!0},this.setPadding=function(e){this.$padding=e,this.element.style.padding="0 "+e+"px"},this.getLineHeight=function(){return this.$fontMetrics.$characterSize.height||0},this.getCharacterWidth=function(){return this.$fontMetrics.$characterSize.width||0},this.$setFontMetrics=function(e){this.$fontMetrics=e,this.$fontMetrics.on("changeCharacterSize",function(e){this._signal("changeCharacterSize",e)}.bind(this)),this.$pollSizeChanges()},this.checkForSizeChanges=function(){this.$fontMetrics.checkForSizeChanges()},this.$pollSizeChanges=function(){return this.$pollSizeChangesTimer=this.$fontMetrics.$pollSizeChanges()},this.setSession=function(e){this.session=e,e&&this.$computeTabString()},this.showInvisibles=!1,this.setShowInvisibles=function(e){return this.showInvisibles==e?!1:(this.showInvisibles=e,this.$computeTabString(),!0)},this.displayIndentGuides=!0,this.setDisplayIndentGuides=function(e){return this.displayIndentGuides==e?!1:(this.displayIndentGuides=e,this.$computeTabString(),!0)},this.$tabStrings=[],this.onChangeTabSize=this.$computeTabString=function(){var e=this.session.getTabSize();this.tabSize=e;var t=this.$tabStrings=[0];for(var n=1;n<e+1;n++)this.showInvisibles?t.push("<span class='ace_invisible ace_invisible_tab'>"+s.stringRepeat(this.TAB_CHAR,n)+"</span>"):t.push(s.stringRepeat(" ",n));if(this.displayIndentGuides){this.$indentGuideRe=/\s\S| \t|\t |\s$/;var r="ace_indent-guide",i="",o="";if(this.showInvisibles){r+=" ace_invisible",i=" ace_invisible_space",o=" ace_invisible_tab";var u=s.stringRepeat(this.SPACE_CHAR,this.tabSize),a=s.stringRepeat(this.TAB_CHAR,this.tabSize)}else var u=s.stringRepeat(" ",this.tabSize),a=u;this.$tabStrings[" "]="<span class='"+r+i+"'>"+u+"</span>",this.$tabStrings["	"]="<span class='"+r+o+"'>"+a+"</span>"}},this.updateLines=function(e,t,n){(this.config.lastRow!=e.lastRow||this.config.firstRow!=e.firstRow)&&this.scrollLines(e),this.config=e;var r=Math.max(t,e.firstRow),i=Math.min(n,e.lastRow),s=this.element.childNodes,o=0;for(var u=e.firstRow;u<r;u++){var a=this.session.getFoldLine(u);if(a){if(a.containsRow(r)){r=a.start.row;break}u=a.end.row}o++}var u=r,a=this.session.getNextFoldLine(u),f=a?a.start.row:Infinity;for(;;){u>f&&(u=a.end.row+1,a=this.session.getNextFoldLine(u,a),f=a?a.start.row:Infinity);if(u>i)break;var l=s[o++];if(l){var c=[];this.$renderLine(c,u,!this.$useLineGroups(),u==f?a:!1),l.style.height=e.lineHeight*this.session.getRowLength(u)+"px",l.innerHTML=c.join("")}u++}},this.scrollLines=function(e){var t=this.config;this.config=e;if(!t||t.lastRow<e.firstRow)return this.update(e);if(e.lastRow<t.firstRow)return this.update(e);var n=this.element;if(t.firstRow<e.firstRow)for(var r=this.session.getFoldedRowCount(t.firstRow,e.firstRow-1);r>0;r--)n.removeChild(n.firstChild);if(t.lastRow>e.lastRow)for(var r=this.session.getFoldedRowCount(e.lastRow+1,t.lastRow);r>0;r--)n.removeChild(n.lastChild);if(e.firstRow<t.firstRow){var i=this.$renderLinesFragment(e,e.firstRow,t.firstRow-1);n.firstChild?n.insertBefore(i,n.firstChild):n.appendChild(i)}if(e.lastRow>t.lastRow){var i=this.$renderLinesFragment(e,t.lastRow+1,e.lastRow);n.appendChild(i)}},this.$renderLinesFragment=function(e,t,n){var r=this.element.ownerDocument.createDocumentFragment(),s=t,o=this.session.getNextFoldLine(s),u=o?o.start.row:Infinity;for(;;){s>u&&(s=o.end.row+1,o=this.session.getNextFoldLine(s,o),u=o?o.start.row:Infinity);if(s>n)break;var a=i.createElement("div"),f=[];this.$renderLine(f,s,!1,s==u?o:!1),a.innerHTML=f.join("");if(this.$useLineGroups())a.className="ace_line_group",r.appendChild(a),a.style.height=e.lineHeight*this.session.getRowLength(s)+"px";else while(a.firstChild)r.appendChild(a.firstChild);s++}return r},this.update=function(e){this.config=e;var t=[],n=e.firstRow,r=e.lastRow,i=n,s=this.session.getNextFoldLine(i),o=s?s.start.row:Infinity;for(;;){i>o&&(i=s.end.row+1,s=this.session.getNextFoldLine(i,s),o=s?s.start.row:Infinity);if(i>r)break;this.$useLineGroups()&&t.push("<div class='ace_line_group' style='height:",e.lineHeight*this.session.getRowLength(i),"px'>"),this.$renderLine(t,i,!1,i==o?s:!1),this.$useLineGroups()&&t.push("</div>"),i++}this.element.innerHTML=t.join("")},this.$textToken={text:!0,rparen:!0,lparen:!0},this.$renderToken=function(e,t,n,r){var i=this,o=/\t|&|<|>|( +)|([\x00-\x1f\x80-\xa0\xad\u1680\u180E\u2000-\u200f\u2028\u2029\u202F\u205F\u3000\uFEFF\uFFF9-\uFFFC])|[\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3000-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]/g,u=function(e,n,r,o,u){if(n)return i.showInvisibles?"<span class='ace_invisible ace_invisible_space'>"+s.stringRepeat(i.SPACE_CHAR,e.length)+"</span>":e;if(e=="&")return"&#38;";if(e=="<")return"&#60;";if(e==">")return"&#62;";if(e=="	"){var a=i.session.getScreenTabSize(t+o);return t+=a-1,i.$tabStrings[a]}if(e=="\u3000"){var f=i.showInvisibles?"ace_cjk ace_invisible ace_invisible_space":"ace_cjk",l=i.showInvisibles?i.SPACE_CHAR:"";return t+=1,"<span class='"+f+"' style='width:"+i.config.characterWidth*2+"px'>"+l+"</span>"}return r?"<span class='ace_invisible ace_invisible_space ace_invalid'>"+i.SPACE_CHAR+"</span>":(t+=1,"<span class='ace_cjk' style='width:"+i.config.characterWidth*2+"px'>"+e+"</span>")},a=r.replace(o,u);if(!this.$textToken[n.type]){var f="ace_"+n.type.replace(/\./g," ace_"),l="";n.type=="fold"&&(l=" style='width:"+n.value.length*this.config.characterWidth+"px;' "),e.push("<span class='",f,"'",l,">",a,"</span>")}else e.push(a);return t+r.length},this.renderIndentGuide=function(e,t,n){var r=t.search(this.$indentGuideRe);return r<=0||r>=n?t:t[0]==" "?(r-=r%this.tabSize,e.push(s.stringRepeat(this.$tabStrings[" "],r/this.tabSize)),t.substr(r)):t[0]=="	"?(e.push(s.stringRepeat(this.$tabStrings["	"],r)),t.substr(r)):t},this.$renderWrappedLine=function(e,t,n,r){var i=0,o=0,u=n[0],a=0;for(var f=0;f<t.length;f++){var l=t[f],c=l.value;if(f==0&&this.displayIndentGuides){i=c.length,c=this.renderIndentGuide(e,c,u);if(!c)continue;i-=c.length}if(i+c.length<u)a=this.$renderToken(e,a,l,c),i+=c.length;else{while(i+c.length>=u)a=this.$renderToken(e,a,l,c.substring(0,u-i)),c=c.substring(u-i),i=u,r||e.push("</div>","<div class='ace_line' style='height:",this.config.lineHeight,"px'>"),e.push(s.stringRepeat("\u00a0",n.indent)),o++,a=0,u=n[o]||Number.MAX_VALUE;c.length!=0&&(i+=c.length,a=this.$renderToken(e,a,l,c))}}},this.$renderSimpleLine=function(e,t){var n=0,r=t[0],i=r.value;this.displayIndentGuides&&(i=this.renderIndentGuide(e,i)),i&&(n=this.$renderToken(e,n,r,i));for(var s=1;s<t.length;s++)r=t[s],i=r.value,n=this.$renderToken(e,n,r,i)},this.$renderLine=function(e,t,n,r){!r&&r!=0&&(r=this.session.getFoldLine(t));if(r)var i=this.$getFoldLineTokens(t,r);else var i=this.session.getTokens(t);n||e.push("<div class='ace_line' style='height:",this.config.lineHeight*(this.$useLineGroups()?1:this.session.getRowLength(t)),"px'>");if(i.length){var s=this.session.getRowSplitData(t);s&&s.length?this.$renderWrappedLine(e,i,s,n):this.$renderSimpleLine(e,i)}this.showInvisibles&&(r&&(t=r.end.row),e.push("<span class='ace_invisible ace_invisible_eol'>",t==this.session.getLength()-1?this.EOF_CHAR:this.EOL_CHAR,"</span>")),n||e.push("</div>")},this.$getFoldLineTokens=function(e,t){function i(e,t,n){var i=0,s=0;while(s+e[i].value.length<t){s+=e[i].value.length,i++;if(i==e.length)return}if(s!=t){var o=e[i].value.substring(t-s);o.length>n-t&&(o=o.substring(0,n-t)),r.push({type:e[i].type,value:o}),s=t+o.length,i+=1}while(s<n&&i<e.length){var o=e[i].value;o.length+s>n?r.push({type:e[i].type,value:o.substring(0,n-s)}):r.push(e[i]),s+=o.length,i+=1}}var n=this.session,r=[],s=n.getTokens(e);return t.walk(function(e,t,o,u,a){e!=null?r.push({type:"fold",value:e}):(a&&(s=n.getTokens(t)),s.length&&i(s,u,o))},t.end.row,this.session.getLine(t.end.row).length),r},this.$useLineGroups=function(){return this.session.getUseWrapMode()},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.$measureNode&&this.$measureNode.parentNode.removeChild(this.$measureNode),delete this.$measureNode}}).call(a.prototype),t.Text=a}),define("ace/layer/cursor",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../lib/dom"),i,s=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_cursor-layer",e.appendChild(this.element),i===undefined&&(i=!("opacity"in this.element.style)),this.isVisible=!1,this.isBlinking=!0,this.blinkInterval=1e3,this.smoothBlinking=!1,this.cursors=[],this.cursor=this.addCursor(),r.addCssClass(this.element,"ace_hidden-cursors"),this.$updateCursors=(i?this.$updateVisibility:this.$updateOpacity).bind(this)};(function(){this.$updateVisibility=function(e){var t=this.cursors;for(var n=t.length;n--;)t[n].style.visibility=e?"":"hidden"},this.$updateOpacity=function(e){var t=this.cursors;for(var n=t.length;n--;)t[n].style.opacity=e?"":"0"},this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setBlinking=function(e){e!=this.isBlinking&&(this.isBlinking=e,this.restartTimer())},this.setBlinkInterval=function(e){e!=this.blinkInterval&&(this.blinkInterval=e,this.restartTimer())},this.setSmoothBlinking=function(e){e!=this.smoothBlinking&&!i&&(this.smoothBlinking=e,r.setCssClass(this.element,"ace_smooth-blinking",e),this.$updateCursors(!0),this.$updateCursors=this.$updateOpacity.bind(this),this.restartTimer())},this.addCursor=function(){var e=r.createElement("div");return e.className="ace_cursor",this.element.appendChild(e),this.cursors.push(e),e},this.removeCursor=function(){if(this.cursors.length>1){var e=this.cursors.pop();return e.parentNode.removeChild(e),e}},this.hideCursor=function(){this.isVisible=!1,r.addCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.showCursor=function(){this.isVisible=!0,r.removeCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.restartTimer=function(){var e=this.$updateCursors;clearInterval(this.intervalId),clearTimeout(this.timeoutId),this.smoothBlinking&&r.removeCssClass(this.element,"ace_smooth-blinking"),e(!0);if(!this.isBlinking||!this.blinkInterval||!this.isVisible)return;this.smoothBlinking&&setTimeout(function(){r.addCssClass(this.element,"ace_smooth-blinking")}.bind(this));var t=function(){this.timeoutId=setTimeout(function(){e(!1)},.6*this.blinkInterval)}.bind(this);this.intervalId=setInterval(function(){e(!0),t()},this.blinkInterval),t()},this.getPixelPosition=function(e,t){if(!this.config||!this.session)return{left:0,top:0};e||(e=this.session.selection.getCursor());var n=this.session.documentToScreenPosition(e),r=this.$padding+n.column*this.config.characterWidth,i=(n.row-(t?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:r,top:i}},this.update=function(e){this.config=e;var t=this.session.$selectionMarkers,n=0,r=0;if(t===undefined||t.length===0)t=[{cursor:null}];for(var n=0,i=t.length;n<i;n++){var s=this.getPixelPosition(t[n].cursor,!0);if((s.top>e.height+e.offset||s.top<0)&&n>1)continue;var o=(this.cursors[r++]||this.addCursor()).style;this.drawCursor?this.drawCursor(o,s,e,t[n],this.session):(o.left=s.left+"px",o.top=s.top+"px",o.width=e.characterWidth+"px",o.height=e.lineHeight+"px")}while(this.cursors.length>r)this.removeCursor();var u=this.session.getOverwrite();this.$setOverwrite(u),this.$pixelPos=s,this.restartTimer()},this.drawCursor=null,this.$setOverwrite=function(e){e!=this.overwrite&&(this.overwrite=e,e?r.addCssClass(this.element,"ace_overwrite-cursors"):r.removeCssClass(this.element,"ace_overwrite-cursors"))},this.destroy=function(){clearInterval(this.intervalId),clearTimeout(this.timeoutId)}}).call(s.prototype),t.Cursor=s}),define("ace/scrollbar",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./lib/event"),o=e("./lib/event_emitter").EventEmitter,u=32768,a=function(e){this.element=i.createElement("div"),this.element.className="ace_scrollbar ace_scrollbar"+this.classSuffix,this.inner=i.createElement("div"),this.inner.className="ace_scrollbar-inner",this.element.appendChild(this.inner),e.appendChild(this.element),this.setVisible(!1),this.skipEvent=!1,s.addListener(this.element,"scroll",this.onScroll.bind(this)),s.addListener(this.element,"mousedown",s.preventDefault)};(function(){r.implement(this,o),this.setVisible=function(e){this.element.style.display=e?"":"none",this.isVisible=e,this.coeff=1}}).call(a.prototype);var f=function(e,t){a.call(this,e),this.scrollTop=0,this.scrollHeight=0,t.$scrollbarWidth=this.width=i.scrollbarWidth(e.ownerDocument),this.inner.style.width=this.element.style.width=(this.width||15)+5+"px"};r.inherits(f,a),function(){this.classSuffix="-v",this.onScroll=function(){if(!this.skipEvent){this.scrollTop=this.element.scrollTop;if(this.coeff!=1){var e=this.element.clientHeight/this.scrollHeight;this.scrollTop=this.scrollTop*(1-e)/(this.coeff-e)}this._emit("scroll",{data:this.scrollTop})}this.skipEvent=!1},this.getWidth=function(){return this.isVisible?this.width:0},this.setHeight=function(e){this.element.style.height=e+"px"},this.setInnerHeight=this.setScrollHeight=function(e){this.scrollHeight=e,e>u?(this.coeff=u/e,e=u):this.coeff!=1&&(this.coeff=1),this.inner.style.height=e+"px"},this.setScrollTop=function(e){this.scrollTop!=e&&(this.skipEvent=!0,this.scrollTop=e,this.element.scrollTop=e*this.coeff)}}.call(f.prototype);var l=function(e,t){a.call(this,e),this.scrollLeft=0,this.height=t.$scrollbarWidth,this.inner.style.height=this.element.style.height=(this.height||15)+5+"px"};r.inherits(l,a),function(){this.classSuffix="-h",this.onScroll=function(){this.skipEvent||(this.scrollLeft=this.element.scrollLeft,this._emit("scroll",{data:this.scrollLeft})),this.skipEvent=!1},this.getHeight=function(){return this.isVisible?this.height:0},this.setWidth=function(e){this.element.style.width=e+"px"},this.setInnerWidth=function(e){this.inner.style.width=e+"px"},this.setScrollWidth=function(e){this.inner.style.width=e+"px"},this.setScrollLeft=function(e){this.scrollLeft!=e&&(this.skipEvent=!0,this.scrollLeft=this.element.scrollLeft=e)}}.call(l.prototype),t.ScrollBar=f,t.ScrollBarV=f,t.ScrollBarH=l,t.VScrollBar=f,t.HScrollBar=l}),define("ace/renderloop",["require","exports","module","ace/lib/event"],function(e,t,n){"use strict";var r=e("./lib/event"),i=function(e,t){this.onRender=e,this.pending=!1,this.changes=0,this.window=t||window};(function(){this.schedule=function(e){this.changes=this.changes|e;if(!this.pending&&this.changes){this.pending=!0;var t=this;r.nextFrame(function(){t.pending=!1;var e;while(e=t.changes)t.changes=0,t.onRender(e)},this.window)}}}).call(i.prototype),t.RenderLoop=i}),define("ace/layer/font_metrics",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/lib/event_emitter"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("../lib/useragent"),u=e("../lib/event_emitter").EventEmitter,a=0,f=t.FontMetrics=function(e){this.el=i.createElement("div"),this.$setMeasureNodeStyles(this.el.style,!0),this.$main=i.createElement("div"),this.$setMeasureNodeStyles(this.$main.style),this.$measureNode=i.createElement("div"),this.$setMeasureNodeStyles(this.$measureNode.style),this.el.appendChild(this.$main),this.el.appendChild(this.$measureNode),e.appendChild(this.el),a||this.$testFractionalRect(),this.$measureNode.innerHTML=s.stringRepeat("X",a),this.$characterSize={width:0,height:0},this.checkForSizeChanges()};(function(){r.implement(this,u),this.$characterSize={width:0,height:0},this.$testFractionalRect=function(){var e=i.createElement("div");this.$setMeasureNodeStyles(e.style),e.style.width="0.2px",document.documentElement.appendChild(e);var t=e.getBoundingClientRect().width;t>0&&t<1?a=50:a=100,e.parentNode.removeChild(e)},this.$setMeasureNodeStyles=function(e,t){e.width=e.height="auto",e.left=e.top="0px",e.visibility="hidden",e.position="absolute",e.whiteSpace="pre",o.isIE<8?e["font-family"]="inherit":e.font="inherit",e.overflow=t?"hidden":"visible"},this.checkForSizeChanges=function(){var e=this.$measureSizes();if(e&&(this.$characterSize.width!==e.width||this.$characterSize.height!==e.height)){this.$measureNode.style.fontWeight="bold";var t=this.$measureSizes();this.$measureNode.style.fontWeight="",this.$characterSize=e,this.charSizes=Object.create(null),this.allowBoldFonts=t&&t.width===e.width&&t.height===e.height,this._emit("changeCharacterSize",{data:e})}},this.$pollSizeChanges=function(){if(this.$pollSizeChangesTimer)return this.$pollSizeChangesTimer;var e=this;return this.$pollSizeChangesTimer=setInterval(function(){e.checkForSizeChanges()},500)},this.setPolling=function(e){e?this.$pollSizeChanges():this.$pollSizeChangesTimer&&(clearInterval(this.$pollSizeChangesTimer),this.$pollSizeChangesTimer=0)},this.$measureSizes=function(){if(a===50){var e=null;try{e=this.$measureNode.getBoundingClientRect()}catch(t){e={width:0,height:0}}var n={height:e.height,width:e.width/a}}else var n={height:this.$measureNode.clientHeight,width:this.$measureNode.clientWidth/a};return n.width===0||n.height===0?null:n},this.$measureCharWidth=function(e){this.$main.innerHTML=s.stringRepeat(e,a);var t=this.$main.getBoundingClientRect();return t.width/a},this.getCharacterWidth=function(e){var t=this.charSizes[e];return t===undefined&&(t=this.charSizes[e]=this.$measureCharWidth(e)/this.$characterSize.width),t},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.el&&this.el.parentNode&&this.el.parentNode.removeChild(this.el)}}).call(f.prototype)}),define("ace/virtual_renderer",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/config","ace/lib/useragent","ace/layer/gutter","ace/layer/marker","ace/layer/text","ace/layer/cursor","ace/scrollbar","ace/scrollbar","ace/renderloop","ace/layer/font_metrics","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./config"),o=e("./lib/useragent"),u=e("./layer/gutter").Gutter,a=e("./layer/marker").Marker,f=e("./layer/text").Text,l=e("./layer/cursor").Cursor,c=e("./scrollbar").HScrollBar,h=e("./scrollbar").VScrollBar,p=e("./renderloop").RenderLoop,d=e("./layer/font_metrics").FontMetrics,v=e("./lib/event_emitter").EventEmitter,m='.ace_editor {position: relative;overflow: hidden;font: 12px/normal \'Monaco\', \'Menlo\', \'Ubuntu Mono\', \'Consolas\', \'source-code-pro\', monospace;direction: ltr;text-align: left;-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}.ace_scroller {position: absolute;overflow: hidden;top: 0;bottom: 0;background-color: inherit;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;cursor: text;}.ace_content {position: absolute;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;min-width: 100%;}.ace_dragging .ace_scroller:before{position: absolute;top: 0;left: 0;right: 0;bottom: 0;content: \'\';background: rgba(250, 250, 250, 0.01);z-index: 1000;}.ace_dragging.ace_dark .ace_scroller:before{background: rgba(0, 0, 0, 0.01);}.ace_selecting, .ace_selecting * {cursor: text !important;}.ace_gutter {position: absolute;overflow : hidden;width: auto;top: 0;bottom: 0;left: 0;cursor: default;z-index: 4;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;}.ace_gutter-active-line {position: absolute;left: 0;right: 0;}.ace_scroller.ace_scroll-left {box-shadow: 17px 0 16px -16px rgba(0, 0, 0, 0.4) inset;}.ace_gutter-cell {padding-left: 19px;padding-right: 6px;background-repeat: no-repeat;}.ace_gutter-cell.ace_error {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABOFBMVEX/////////QRswFAb/Ui4wFAYwFAYwFAaWGAfDRymzOSH/PxswFAb/SiUwFAYwFAbUPRvjQiDllog5HhHdRybsTi3/Tyv9Tir+Syj/UC3////XurebMBIwFAb/RSHbPx/gUzfdwL3kzMivKBAwFAbbvbnhPx66NhowFAYwFAaZJg8wFAaxKBDZurf/RB6mMxb/SCMwFAYwFAbxQB3+RB4wFAb/Qhy4Oh+4QifbNRcwFAYwFAYwFAb/QRzdNhgwFAYwFAbav7v/Uy7oaE68MBK5LxLewr/r2NXewLswFAaxJw4wFAbkPRy2PyYwFAaxKhLm1tMwFAazPiQwFAaUGAb/QBrfOx3bvrv/VC/maE4wFAbRPBq6MRO8Qynew8Dp2tjfwb0wFAbx6eju5+by6uns4uH9/f36+vr/GkHjAAAAYnRSTlMAGt+64rnWu/bo8eAA4InH3+DwoN7j4eLi4xP99Nfg4+b+/u9B/eDs1MD1mO7+4PHg2MXa347g7vDizMLN4eG+Pv7i5evs/v79yu7S3/DV7/498Yv24eH+4ufQ3Ozu/v7+y13sRqwAAADLSURBVHjaZc/XDsFgGIBhtDrshlitmk2IrbHFqL2pvXf/+78DPokj7+Fz9qpU/9UXJIlhmPaTaQ6QPaz0mm+5gwkgovcV6GZzd5JtCQwgsxoHOvJO15kleRLAnMgHFIESUEPmawB9ngmelTtipwwfASilxOLyiV5UVUyVAfbG0cCPHig+GBkzAENHS0AstVF6bacZIOzgLmxsHbt2OecNgJC83JERmePUYq8ARGkJx6XtFsdddBQgZE2nPR6CICZhawjA4Fb/chv+399kfR+MMMDGOQAAAABJRU5ErkJggg==");background-repeat: no-repeat;background-position: 2px center;}.ace_gutter-cell.ace_warning {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAmVBMVEX///8AAAD///8AAAAAAABPSzb/5sAAAAB/blH/73z/ulkAAAAAAAD85pkAAAAAAAACAgP/vGz/rkDerGbGrV7/pkQICAf////e0IsAAAD/oED/qTvhrnUAAAD/yHD/njcAAADuv2r/nz//oTj/p064oGf/zHAAAAA9Nir/tFIAAAD/tlTiuWf/tkIAAACynXEAAAAAAAAtIRW7zBpBAAAAM3RSTlMAABR1m7RXO8Ln31Z36zT+neXe5OzooRDfn+TZ4p3h2hTf4t3k3ucyrN1K5+Xaks52Sfs9CXgrAAAAjklEQVR42o3PbQ+CIBQFYEwboPhSYgoYunIqqLn6/z8uYdH8Vmdnu9vz4WwXgN/xTPRD2+sgOcZjsge/whXZgUaYYvT8QnuJaUrjrHUQreGczuEafQCO/SJTufTbroWsPgsllVhq3wJEk2jUSzX3CUEDJC84707djRc5MTAQxoLgupWRwW6UB5fS++NV8AbOZgnsC7BpEAAAAABJRU5ErkJggg==");background-position: 2px center;}.ace_gutter-cell.ace_info {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAJ0Uk5TAAB2k804AAAAPklEQVQY02NgIB68QuO3tiLznjAwpKTgNyDbMegwisCHZUETUZV0ZqOquBpXj2rtnpSJT1AEnnRmL2OgGgAAIKkRQap2htgAAAAASUVORK5CYII=");background-position: 2px center;}.ace_dark .ace_gutter-cell.ace_info {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAChoaGAgIAqKiq+vr6tra1ZWVmUlJSbm5s8PDxubm56enrdgzg3AAAAAXRSTlMAQObYZgAAAClJREFUeNpjYMAPdsMYHegyJZFQBlsUlMFVCWUYKkAZMxZAGdxlDMQBAG+TBP4B6RyJAAAAAElFTkSuQmCC");}.ace_scrollbar {position: absolute;right: 0;bottom: 0;z-index: 6;}.ace_scrollbar-inner {position: absolute;cursor: text;left: 0;top: 0;}.ace_scrollbar-v{overflow-x: hidden;overflow-y: scroll;top: 0;}.ace_scrollbar-h {overflow-x: scroll;overflow-y: hidden;left: 0;}.ace_print-margin {position: absolute;height: 100%;}.ace_text-input {position: absolute;z-index: 0;width: 0.5em;height: 1em;opacity: 0;background: transparent;-moz-appearance: none;appearance: none;border: none;resize: none;outline: none;overflow: hidden;font: inherit;padding: 0 1px;margin: 0 -1px;text-indent: -1em;-ms-user-select: text;-moz-user-select: text;-webkit-user-select: text;user-select: text;white-space: pre!important;}.ace_text-input.ace_composition {background: inherit;color: inherit;z-index: 1000;opacity: 1;text-indent: 0;}.ace_layer {z-index: 1;position: absolute;overflow: hidden;word-wrap: normal;white-space: pre;height: 100%;width: 100%;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;pointer-events: none;}.ace_gutter-layer {position: relative;width: auto;text-align: right;pointer-events: auto;}.ace_text-layer {font: inherit !important;}.ace_cjk {display: inline-block;text-align: center;}.ace_cursor-layer {z-index: 4;}.ace_cursor {z-index: 4;position: absolute;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;border-left: 2px solid;transform: translatez(0);}.ace_multiselect .ace_cursor {border-left-width: 1px;}.ace_slim-cursors .ace_cursor {border-left-width: 1px;}.ace_overwrite-cursors .ace_cursor {border-left-width: 0;border-bottom: 1px solid;}.ace_hidden-cursors .ace_cursor {opacity: 0.2;}.ace_smooth-blinking .ace_cursor {-webkit-transition: opacity 0.18s;transition: opacity 0.18s;}.ace_marker-layer .ace_step, .ace_marker-layer .ace_stack {position: absolute;z-index: 3;}.ace_marker-layer .ace_selection {position: absolute;z-index: 5;}.ace_marker-layer .ace_bracket {position: absolute;z-index: 6;}.ace_marker-layer .ace_active-line {position: absolute;z-index: 2;}.ace_marker-layer .ace_selected-word {position: absolute;z-index: 4;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;}.ace_line .ace_fold {-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;display: inline-block;height: 11px;margin-top: -2px;vertical-align: middle;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACJJREFUeNpi+P//fxgTAwPDBxDxD078RSX+YeEyDFMCIMAAI3INmXiwf2YAAAAASUVORK5CYII=");background-repeat: no-repeat, repeat-x;background-position: center center, top left;color: transparent;border: 1px solid black;border-radius: 2px;cursor: pointer;pointer-events: auto;}.ace_dark .ace_fold {}.ace_fold:hover{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACBJREFUeNpi+P//fz4TAwPDZxDxD5X4i5fLMEwJgAADAEPVDbjNw87ZAAAAAElFTkSuQmCC");}.ace_tooltip {background-color: #FFF;background-image: -webkit-linear-gradient(top, transparent, rgba(0, 0, 0, 0.1));background-image: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.1));border: 1px solid gray;border-radius: 1px;box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);color: black;max-width: 100%;padding: 3px 4px;position: fixed;z-index: 999999;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;cursor: default;white-space: pre;word-wrap: break-word;line-height: normal;font-style: normal;font-weight: normal;letter-spacing: normal;pointer-events: none;}.ace_folding-enabled > .ace_gutter-cell {padding-right: 13px;}.ace_fold-widget {-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;margin: 0 -12px 0 1px;display: none;width: 11px;vertical-align: top;background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42mWKsQ0AMAzC8ixLlrzQjzmBiEjp0A6WwBCSPgKAXoLkqSot7nN3yMwR7pZ32NzpKkVoDBUxKAAAAABJRU5ErkJggg==");background-repeat: no-repeat;background-position: center;border-radius: 3px;border: 1px solid transparent;cursor: pointer;}.ace_folding-enabled .ace_fold-widget {display: inline-block;   }.ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42m3HwQkAMAhD0YzsRchFKI7sAikeWkrxwScEB0nh5e7KTPWimZki4tYfVbX+MNl4pyZXejUO1QAAAABJRU5ErkJggg==");}.ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAGCAYAAAAG5SQMAAAAOUlEQVR42jXKwQkAMAgDwKwqKD4EwQ26sSOkVWjgIIHAzPiCgaqiqnJHZnKICBERHN194O5b9vbLuAVRL+l0YWnZAAAAAElFTkSuQmCCXA==");}.ace_fold-widget:hover {border: 1px solid rgba(0, 0, 0, 0.3);background-color: rgba(255, 255, 255, 0.2);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);}.ace_fold-widget:active {border: 1px solid rgba(0, 0, 0, 0.4);background-color: rgba(0, 0, 0, 0.05);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);}.ace_dark .ace_fold-widget {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHklEQVQIW2P4//8/AzoGEQ7oGCaLLAhWiSwB146BAQCSTPYocqT0AAAAAElFTkSuQmCC");}.ace_dark .ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQIW2P4//8/AxQ7wNjIAjDMgC4AxjCVKBirIAAF0kz2rlhxpAAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAHElEQVQIW2P4//+/AxAzgDADlOOAznHAKgPWAwARji8UIDTfQQAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget:hover {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);background-color: rgba(255, 255, 255, 0.1);}.ace_dark .ace_fold-widget:active {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);}.ace_fold-widget.ace_invalid {background-color: #FFB4B4;border-color: #DE5555;}.ace_fade-fold-widgets .ace_fold-widget {-webkit-transition: opacity 0.4s ease 0.05s;transition: opacity 0.4s ease 0.05s;opacity: 0;}.ace_fade-fold-widgets:hover .ace_fold-widget {-webkit-transition: opacity 0.05s ease 0.05s;transition: opacity 0.05s ease 0.05s;opacity:1;}.ace_underline {text-decoration: underline;}.ace_bold {font-weight: bold;}.ace_nobold .ace_bold {font-weight: normal;}.ace_italic {font-style: italic;}.ace_error-marker {background-color: rgba(255, 0, 0,0.2);position: absolute;z-index: 9;}.ace_highlight-marker {background-color: rgba(255, 255, 0,0.2);position: absolute;z-index: 8;}.ace_br1 {border-top-left-radius    : 3px;}.ace_br2 {border-top-right-radius   : 3px;}.ace_br3 {border-top-left-radius    : 3px; border-top-right-radius:    3px;}.ace_br4 {border-bottom-right-radius: 3px;}.ace_br5 {border-top-left-radius    : 3px; border-bottom-right-radius: 3px;}.ace_br6 {border-top-right-radius   : 3px; border-bottom-right-radius: 3px;}.ace_br7 {border-top-left-radius    : 3px; border-top-right-radius:    3px; border-bottom-right-radius: 3px;}.ace_br8 {border-bottom-left-radius : 3px;}.ace_br9 {border-top-left-radius    : 3px; border-bottom-left-radius:  3px;}.ace_br10{border-top-right-radius   : 3px; border-bottom-left-radius:  3px;}.ace_br11{border-top-left-radius    : 3px; border-top-right-radius:    3px; border-bottom-left-radius:  3px;}.ace_br12{border-bottom-right-radius: 3px; border-bottom-left-radius:  3px;}.ace_br13{border-top-left-radius    : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius:  3px;}.ace_br14{border-top-right-radius   : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius:  3px;}.ace_br15{border-top-left-radius    : 3px; border-top-right-radius:    3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_text-input-ios {position: absolute !important;top: -100000px !important;left: -100000px !important;}';i.importCssString(m,"ace_editor.css");var g=function(e,t){var n=this;this.container=e||i.createElement("div"),this.$keepTextAreaAtCursor=!o.isOldIE,i.addCssClass(this.container,"ace_editor"),this.setTheme(t),this.$gutter=i.createElement("div"),this.$gutter.className="ace_gutter",this.container.appendChild(this.$gutter),this.scroller=i.createElement("div"),this.scroller.className="ace_scroller",this.container.appendChild(this.scroller),this.content=i.createElement("div"),this.content.className="ace_content",this.scroller.appendChild(this.content),this.$gutterLayer=new u(this.$gutter),this.$gutterLayer.on("changeGutterWidth",this.onGutterResize.bind(this)),this.$markerBack=new a(this.content);var r=this.$textLayer=new f(this.content);this.canvas=r.element,this.$markerFront=new a(this.content),this.$cursorLayer=new l(this.content),this.$horizScroll=!1,this.$vScroll=!1,this.scrollBar=this.scrollBarV=new h(this.container,this),this.scrollBarH=new c(this.container,this),this.scrollBarV.addEventListener("scroll",function(e){n.$scrollAnimation||n.session.setScrollTop(e.data-n.scrollMargin.top)}),this.scrollBarH.addEventListener("scroll",function(e){n.$scrollAnimation||n.session.setScrollLeft(e.data-n.scrollMargin.left)}),this.scrollTop=0,this.scrollLeft=0,this.cursorPos={row:0,column:0},this.$fontMetrics=new d(this.container),this.$textLayer.$setFontMetrics(this.$fontMetrics),this.$textLayer.addEventListener("changeCharacterSize",function(e){n.updateCharacterSize(),n.onResize(!0,n.gutterWidth,n.$size.width,n.$size.height),n._signal("changeCharacterSize",e)}),this.$size={width:0,height:0,scrollerHeight:0,scrollerWidth:0,$dirty:!0},this.layerConfig={width:1,padding:0,firstRow:0,firstRowScreen:0,lastRow:0,lineHeight:0,characterWidth:0,minHeight:1,maxHeight:1,offset:0,height:1,gutterOffset:1},this.scrollMargin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.$loop=new p(this.$renderChanges.bind(this),this.container.ownerDocument.defaultView),this.$loop.schedule(this.CHANGE_FULL),this.updateCharacterSize(),this.setPadding(4),s.resetOptions(this),s._emit("renderer",this)};(function(){this.CHANGE_CURSOR=1,this.CHANGE_MARKER=2,this.CHANGE_GUTTER=4,this.CHANGE_SCROLL=8,this.CHANGE_LINES=16,this.CHANGE_TEXT=32,this.CHANGE_SIZE=64,this.CHANGE_MARKER_BACK=128,this.CHANGE_MARKER_FRONT=256,this.CHANGE_FULL=512,this.CHANGE_H_SCROLL=1024,r.implement(this,v),this.updateCharacterSize=function(){this.$textLayer.allowBoldFonts!=this.$allowBoldFonts&&(this.$allowBoldFonts=this.$textLayer.allowBoldFonts,this.setStyle("ace_nobold",!this.$allowBoldFonts)),this.layerConfig.characterWidth=this.characterWidth=this.$textLayer.getCharacterWidth(),this.layerConfig.lineHeight=this.lineHeight=this.$textLayer.getLineHeight(),this.$updatePrintMargin()},this.setSession=function(e){this.session&&this.session.doc.off("changeNewLineMode",this.onChangeNewLineMode),this.session=e,e&&this.scrollMargin.top&&e.getScrollTop()<=0&&e.setScrollTop(-this.scrollMargin.top),this.$cursorLayer.setSession(e),this.$markerBack.setSession(e),this.$markerFront.setSession(e),this.$gutterLayer.setSession(e),this.$textLayer.setSession(e);if(!e)return;this.$loop.schedule(this.CHANGE_FULL),this.session.$setFontMetrics(this.$fontMetrics),this.scrollBarV.scrollLeft=this.scrollBarV.scrollTop=null,this.onChangeNewLineMode=this.onChangeNewLineMode.bind(this),this.onChangeNewLineMode(),this.session.doc.on("changeNewLineMode",this.onChangeNewLineMode)},this.updateLines=function(e,t,n){t===undefined&&(t=Infinity),this.$changedLines?(this.$changedLines.firstRow>e&&(this.$changedLines.firstRow=e),this.$changedLines.lastRow<t&&(this.$changedLines.lastRow=t)):this.$changedLines={firstRow:e,lastRow:t};if(this.$changedLines.lastRow<this.layerConfig.firstRow){if(!n)return;this.$changedLines.lastRow=this.layerConfig.lastRow}if(this.$changedLines.firstRow>this.layerConfig.lastRow)return;this.$loop.schedule(this.CHANGE_LINES)},this.onChangeNewLineMode=function(){this.$loop.schedule(this.CHANGE_TEXT),this.$textLayer.$updateEolChar()},this.onChangeTabSize=function(){this.$loop.schedule(this.CHANGE_TEXT|this.CHANGE_MARKER),this.$textLayer.onChangeTabSize()},this.updateText=function(){this.$loop.schedule(this.CHANGE_TEXT)},this.updateFull=function(e){e?this.$renderChanges(this.CHANGE_FULL,!0):this.$loop.schedule(this.CHANGE_FULL)},this.updateFontSize=function(){this.$textLayer.checkForSizeChanges()},this.$changes=0,this.$updateSizeAsync=function(){this.$loop.pending?this.$size.$dirty=!0:this.onResize()},this.onResize=function(e,t,n,r){if(this.resizing>2)return;this.resizing>0?this.resizing++:this.resizing=e?1:0;var i=this.container;r||(r=i.clientHeight||i.scrollHeight),n||(n=i.clientWidth||i.scrollWidth);var s=this.$updateCachedSize(e,t,n,r);if(!this.$size.scrollerHeight||!n&&!r)return this.resizing=0;e&&(this.$gutterLayer.$padding=null),e?this.$renderChanges(s|this.$changes,!0):this.$loop.schedule(s|this.$changes),this.resizing&&(this.resizing=0),this.scrollBarV.scrollLeft=this.scrollBarV.scrollTop=null},this.$updateCachedSize=function(e,t,n,r){r-=this.$extraHeight||0;var i=0,s=this.$size,o={width:s.width,height:s.height,scrollerHeight:s.scrollerHeight,scrollerWidth:s.scrollerWidth};r&&(e||s.height!=r)&&(s.height=r,i|=this.CHANGE_SIZE,s.scrollerHeight=s.height,this.$horizScroll&&(s.scrollerHeight-=this.scrollBarH.getHeight()),this.scrollBarV.element.style.bottom=this.scrollBarH.getHeight()+"px",i|=this.CHANGE_SCROLL);if(n&&(e||s.width!=n)){i|=this.CHANGE_SIZE,s.width=n,t==null&&(t=this.$showGutter?this.$gutter.offsetWidth:0),this.gutterWidth=t,this.scrollBarH.element.style.left=this.scroller.style.left=t+"px",s.scrollerWidth=Math.max(0,n-t-this.scrollBarV.getWidth()),this.scrollBarH.element.style.right=this.scroller.style.right=this.scrollBarV.getWidth()+"px",this.scroller.style.bottom=this.scrollBarH.getHeight()+"px";if(this.session&&this.session.getUseWrapMode()&&this.adjustWrapLimit()||e)i|=this.CHANGE_FULL}return s.$dirty=!n||!r,i&&this._signal("resize",o),i},this.onGutterResize=function(){var e=this.$showGutter?this.$gutter.offsetWidth:0;e!=this.gutterWidth&&(this.$changes|=this.$updateCachedSize(!0,e,this.$size.width,this.$size.height)),this.session.getUseWrapMode()&&this.adjustWrapLimit()?this.$loop.schedule(this.CHANGE_FULL):this.$size.$dirty?this.$loop.schedule(this.CHANGE_FULL):(this.$computeLayerConfig(),this.$loop.schedule(this.CHANGE_MARKER))},this.adjustWrapLimit=function(){var e=this.$size.scrollerWidth-this.$padding*2,t=Math.floor(e/this.characterWidth);return this.session.adjustWrapLimit(t,this.$showPrintMargin&&this.$printMarginColumn)},this.setAnimatedScroll=function(e){this.setOption("animatedScroll",e)},this.getAnimatedScroll=function(){return this.$animatedScroll},this.setShowInvisibles=function(e){this.setOption("showInvisibles",e)},this.getShowInvisibles=function(){return this.getOption("showInvisibles")},this.getDisplayIndentGuides=function(){return this.getOption("displayIndentGuides")},this.setDisplayIndentGuides=function(e){this.setOption("displayIndentGuides",e)},this.setShowPrintMargin=function(e){this.setOption("showPrintMargin",e)},this.getShowPrintMargin=function(){return this.getOption("showPrintMargin")},this.setPrintMarginColumn=function(e){this.setOption("printMarginColumn",e)},this.getPrintMarginColumn=function(){return this.getOption("printMarginColumn")},this.getShowGutter=function(){return this.getOption("showGutter")},this.setShowGutter=function(e){return this.setOption("showGutter",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.$updateGutterLineHighlight=function(){var e=this.$cursorLayer.$pixelPos,t=this.layerConfig.lineHeight;if(this.session.getUseWrapMode()){var n=this.session.selection.getCursor();n.column=0,e=this.$cursorLayer.getPixelPosition(n,!0),t*=this.session.getRowLength(n.row)}this.$gutterLineHighlight.style.top=e.top-this.layerConfig.offset+"px",this.$gutterLineHighlight.style.height=t+"px"},this.$updatePrintMargin=function(){if(!this.$showPrintMargin&&!this.$printMarginEl)return;if(!this.$printMarginEl){var e=i.createElement("div");e.className="ace_layer ace_print-margin-layer",this.$printMarginEl=i.createElement("div"),this.$printMarginEl.className="ace_print-margin",e.appendChild(this.$printMarginEl),this.content.insertBefore(e,this.content.firstChild)}var t=this.$printMarginEl.style;t.left=this.characterWidth*this.$printMarginColumn+this.$padding+"px",t.visibility=this.$showPrintMargin?"visible":"hidden",this.session&&this.session.$wrap==-1&&this.adjustWrapLimit()},this.getContainerElement=function(){return this.container},this.getMouseEventTarget=function(){return this.scroller},this.getTextAreaContainer=function(){return this.container},this.$moveTextAreaToCursor=function(){if(!this.$keepTextAreaAtCursor)return;var e=this.layerConfig,t=this.$cursorLayer.$pixelPos.top,n=this.$cursorLayer.$pixelPos.left;t-=e.offset;var r=this.textarea.style,i=this.lineHeight;if(t<0||t>e.height-i){r.top=r.left="0";return}var s=this.characterWidth;if(this.$composition){var o=this.textarea.value.replace(/^\x01+/,"");s*=this.session.$getStringScreenWidth(o)[0]+2,i+=2}n-=this.scrollLeft,n>this.$size.scrollerWidth-s&&(n=this.$size.scrollerWidth-s),n+=this.gutterWidth,r.height=i+"px",r.width=s+"px",r.left=Math.min(n,this.$size.scrollerWidth-s)+"px",r.top=Math.min(t,this.$size.height-i)+"px"},this.getFirstVisibleRow=function(){return this.layerConfig.firstRow},this.getFirstFullyVisibleRow=function(){return this.layerConfig.firstRow+(this.layerConfig.offset===0?0:1)},this.getLastFullyVisibleRow=function(){var e=this.layerConfig,t=e.lastRow,n=this.session.documentToScreenRow(t,0)*e.lineHeight;return n-this.session.getScrollTop()>e.height-e.lineHeight?t-1:t},this.getLastVisibleRow=function(){return this.layerConfig.lastRow},this.$padding=null,this.setPadding=function(e){this.$padding=e,this.$textLayer.setPadding(e),this.$cursorLayer.setPadding(e),this.$markerFront.setPadding(e),this.$markerBack.setPadding(e),this.$loop.schedule(this.CHANGE_FULL),this.$updatePrintMargin()},this.setScrollMargin=function(e,t,n,r){var i=this.scrollMargin;i.top=e|0,i.bottom=t|0,i.right=r|0,i.left=n|0,i.v=i.top+i.bottom,i.h=i.left+i.right,i.top&&this.scrollTop<=0&&this.session&&this.session.setScrollTop(-i.top),this.updateFull()},this.getHScrollBarAlwaysVisible=function(){return this.$hScrollBarAlwaysVisible},this.setHScrollBarAlwaysVisible=function(e){this.setOption("hScrollBarAlwaysVisible",e)},this.getVScrollBarAlwaysVisible=function(){return this.$vScrollBarAlwaysVisible},this.setVScrollBarAlwaysVisible=function(e){this.setOption("vScrollBarAlwaysVisible",e)},this.$updateScrollBarV=function(){var e=this.layerConfig.maxHeight,t=this.$size.scrollerHeight;!this.$maxLines&&this.$scrollPastEnd&&(e-=(t-this.lineHeight)*this.$scrollPastEnd,this.scrollTop>e-t&&(e=this.scrollTop+t,this.scrollBarV.scrollTop=null)),this.scrollBarV.setScrollHeight(e+this.scrollMargin.v),this.scrollBarV.setScrollTop(this.scrollTop+this.scrollMargin.top)},this.$updateScrollBarH=function(){this.scrollBarH.setScrollWidth(this.layerConfig.width+2*this.$padding+this.scrollMargin.h),this.scrollBarH.setScrollLeft(this.scrollLeft+this.scrollMargin.left)},this.$frozen=!1,this.freeze=function(){this.$frozen=!0},this.unfreeze=function(){this.$frozen=!1},this.$renderChanges=function(e,t){this.$changes&&(e|=this.$changes,this.$changes=0);if(!this.session||!this.container.offsetWidth||this.$frozen||!e&&!t){this.$changes|=e;return}if(this.$size.$dirty)return this.$changes|=e,this.onResize(!0);this.lineHeight||this.$textLayer.checkForSizeChanges(),this._signal("beforeRender");var n=this.layerConfig;if(e&this.CHANGE_FULL||e&this.CHANGE_SIZE||e&this.CHANGE_TEXT||e&this.CHANGE_LINES||e&this.CHANGE_SCROLL||e&this.CHANGE_H_SCROLL){e|=this.$computeLayerConfig();if(n.firstRow!=this.layerConfig.firstRow&&n.firstRowScreen==this.layerConfig.firstRowScreen){var r=this.scrollTop+(n.firstRow-this.layerConfig.firstRow)*this.lineHeight;r>0&&(this.scrollTop=r,e|=this.CHANGE_SCROLL,e|=this.$computeLayerConfig())}n=this.layerConfig,this.$updateScrollBarV(),e&this.CHANGE_H_SCROLL&&this.$updateScrollBarH(),this.$gutterLayer.element.style.marginTop=-n.offset+"px",this.content.style.marginTop=-n.offset+"px",this.content.style.width=n.width+2*this.$padding+"px",this.content.style.height=n.minHeight+"px"}e&this.CHANGE_H_SCROLL&&(this.content.style.marginLeft=-this.scrollLeft+"px",this.scroller.className=this.scrollLeft<=0?"ace_scroller":"ace_scroller ace_scroll-left");if(e&this.CHANGE_FULL){this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this.$highlightGutterLine&&this.$updateGutterLineHighlight(),this._signal("afterRender");return}if(e&this.CHANGE_SCROLL){e&this.CHANGE_TEXT||e&this.CHANGE_LINES?this.$textLayer.update(n):this.$textLayer.scrollLines(n),this.$showGutter&&this.$gutterLayer.update(n),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$highlightGutterLine&&this.$updateGutterLineHighlight(),this.$moveTextAreaToCursor(),this._signal("afterRender");return}e&this.CHANGE_TEXT?(this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n)):e&this.CHANGE_LINES?(this.$updateLines()||e&this.CHANGE_GUTTER&&this.$showGutter)&&this.$gutterLayer.update(n):(e&this.CHANGE_TEXT||e&this.CHANGE_GUTTER)&&this.$showGutter&&this.$gutterLayer.update(n),e&this.CHANGE_CURSOR&&(this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this.$highlightGutterLine&&this.$updateGutterLineHighlight()),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_FRONT)&&this.$markerFront.update(n),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_BACK)&&this.$markerBack.update(n),this._signal("afterRender")},this.$autosize=function(){var e=this.session.getScreenLength()*this.lineHeight,t=this.$maxLines*this.lineHeight,n=Math.min(t,Math.max((this.$minLines||1)*this.lineHeight,e))+this.scrollMargin.v+(this.$extraHeight||0);this.$horizScroll&&(n+=this.scrollBarH.getHeight()),this.$maxPixelHeight&&n>this.$maxPixelHeight&&(n=this.$maxPixelHeight);var r=e>t;if(n!=this.desiredHeight||this.$size.height!=this.desiredHeight||r!=this.$vScroll){r!=this.$vScroll&&(this.$vScroll=r,this.scrollBarV.setVisible(r));var i=this.container.clientWidth;this.container.style.height=n+"px",this.$updateCachedSize(!0,this.$gutterWidth,i,n),this.desiredHeight=n,this._signal("autosize")}},this.$computeLayerConfig=function(){var e=this.session,t=this.$size,n=t.height<=2*this.lineHeight,r=this.session.getScreenLength(),i=r*this.lineHeight,s=this.$getLongestLine(),o=!n&&(this.$hScrollBarAlwaysVisible||t.scrollerWidth-s-2*this.$padding<0),u=this.$horizScroll!==o;u&&(this.$horizScroll=o,this.scrollBarH.setVisible(o));var a=this.$vScroll;this.$maxLines&&this.lineHeight>1&&this.$autosize();var f=this.scrollTop%this.lineHeight,l=t.scrollerHeight+this.lineHeight,c=!this.$maxLines&&this.$scrollPastEnd?(t.scrollerHeight-this.lineHeight)*this.$scrollPastEnd:0;i+=c;var h=this.scrollMargin;this.session.setScrollTop(Math.max(-h.top,Math.min(this.scrollTop,i-t.scrollerHeight+h.bottom))),this.session.setScrollLeft(Math.max(-h.left,Math.min(this.scrollLeft,s+2*this.$padding-t.scrollerWidth+h.right)));var p=!n&&(this.$vScrollBarAlwaysVisible||t.scrollerHeight-i+c<0||this.scrollTop>h.top),d=a!==p;d&&(this.$vScroll=p,this.scrollBarV.setVisible(p));var v=Math.ceil(l/this.lineHeight)-1,m=Math.max(0,Math.round((this.scrollTop-f)/this.lineHeight)),g=m+v,y,b,w=this.lineHeight;m=e.screenToDocumentRow(m,0);var E=e.getFoldLine(m);E&&(m=E.start.row),y=e.documentToScreenRow(m,0),b=e.getRowLength(m)*w,g=Math.min(e.screenToDocumentRow(g,0),e.getLength()-1),l=t.scrollerHeight+e.getRowLength(g)*w+b,f=this.scrollTop-y*w;var S=0;this.layerConfig.width!=s&&(S=this.CHANGE_H_SCROLL);if(u||d)S=this.$updateCachedSize(!0,this.gutterWidth,t.width,t.height),this._signal("scrollbarVisibilityChanged"),d&&(s=this.$getLongestLine());return this.layerConfig={width:s,padding:this.$padding,firstRow:m,firstRowScreen:y,lastRow:g,lineHeight:w,characterWidth:this.characterWidth,minHeight:l,maxHeight:i,offset:f,gutterOffset:w?Math.max(0,Math.ceil((f+t.height-t.scrollerHeight)/w)):0,height:this.$size.scrollerHeight},S},this.$updateLines=function(){var e=this.$changedLines.firstRow,t=this.$changedLines.lastRow;this.$changedLines=null;var n=this.layerConfig;if(e>n.lastRow+1)return;if(t<n.firstRow)return;if(t===Infinity){this.$showGutter&&this.$gutterLayer.update(n),this.$textLayer.update(n);return}return this.$textLayer.updateLines(n,e,t),!0},this.$getLongestLine=function(){var e=this.session.getScreenWidth();return this.showInvisibles&&!this.session.$useWrapMode&&(e+=1),Math.max(this.$size.scrollerWidth-2*this.$padding,Math.round(e*this.characterWidth))},this.updateFrontMarkers=function(){this.$markerFront.setMarkers(this.session.getMarkers(!0)),this.$loop.schedule(this.CHANGE_MARKER_FRONT)},this.updateBackMarkers=function(){this.$markerBack.setMarkers(this.session.getMarkers()),this.$loop.schedule(this.CHANGE_MARKER_BACK)},this.addGutterDecoration=function(e,t){this.$gutterLayer.addGutterDecoration(e,t)},this.removeGutterDecoration=function(e,t){this.$gutterLayer.removeGutterDecoration(e,t)},this.updateBreakpoints=function(e){this.$loop.schedule(this.CHANGE_GUTTER)},this.setAnnotations=function(e){this.$gutterLayer.setAnnotations(e),this.$loop.schedule(this.CHANGE_GUTTER)},this.updateCursor=function(){this.$loop.schedule(this.CHANGE_CURSOR)},this.hideCursor=function(){this.$cursorLayer.hideCursor()},this.showCursor=function(){this.$cursorLayer.showCursor()},this.scrollSelectionIntoView=function(e,t,n){this.scrollCursorIntoView(e,n),this.scrollCursorIntoView(t,n)},this.scrollCursorIntoView=function(e,t,n){if(this.$size.scrollerHeight===0)return;var r=this.$cursorLayer.getPixelPosition(e),i=r.left,s=r.top,o=n&&n.top||0,u=n&&n.bottom||0,a=this.$scrollAnimation?this.session.getScrollTop():this.scrollTop;a+o>s?(t&&a+o>s+this.lineHeight&&(s-=t*this.$size.scrollerHeight),s===0&&(s=-this.scrollMargin.top),this.session.setScrollTop(s)):a+this.$size.scrollerHeight-u<s+this.lineHeight&&(t&&a+this.$size.scrollerHeight-u<s-this.lineHeight&&(s+=t*this.$size.scrollerHeight),this.session.setScrollTop(s+this.lineHeight-this.$size.scrollerHeight));var f=this.scrollLeft;f>i?(i<this.$padding+2*this.layerConfig.characterWidth&&(i=-this.scrollMargin.left),this.session.setScrollLeft(i)):f+this.$size.scrollerWidth<i+this.characterWidth?this.session.setScrollLeft(Math.round(i+this.characterWidth-this.$size.scrollerWidth)):f<=this.$padding&&i-f<this.characterWidth&&this.session.setScrollLeft(0)},this.getScrollTop=function(){return this.session.getScrollTop()},this.getScrollLeft=function(){return this.session.getScrollLeft()},this.getScrollTopRow=function(){return this.scrollTop/this.lineHeight},this.getScrollBottomRow=function(){return Math.max(0,Math.floor((this.scrollTop+this.$size.scrollerHeight)/this.lineHeight)-1)},this.scrollToRow=function(e){this.session.setScrollTop(e*this.lineHeight)},this.alignCursor=function(e,t){typeof e=="number"&&(e={row:e,column:0});var n=this.$cursorLayer.getPixelPosition(e),r=this.$size.scrollerHeight-this.lineHeight,i=n.top-r*(t||0);return this.session.setScrollTop(i),i},this.STEPS=8,this.$calcSteps=function(e,t){var n=0,r=this.STEPS,i=[],s=function(e,t,n){return n*(Math.pow(e-1,3)+1)+t};for(n=0;n<r;++n)i.push(s(n/this.STEPS,e,t-e));return i},this.scrollToLine=function(e,t,n,r){var i=this.$cursorLayer.getPixelPosition({row:e,column:0}),s=i.top;t&&(s-=this.$size.scrollerHeight/2);var o=this.scrollTop;this.session.setScrollTop(s),n!==!1&&this.animateScrolling(o,r)},this.animateScrolling=function(e,t){var n=this.scrollTop;if(!this.$animatedScroll)return;var r=this;if(e==n)return;if(this.$scrollAnimation){var i=this.$scrollAnimation.steps;if(i.length){e=i[0];if(e==n)return}}var s=r.$calcSteps(e,n);this.$scrollAnimation={from:e,to:n,steps:s},clearInterval(this.$timer),r.session.setScrollTop(s.shift()),r.session.$scrollTop=n,this.$timer=setInterval(function(){s.length?(r.session.setScrollTop(s.shift()),r.session.$scrollTop=n):n!=null?(r.session.$scrollTop=-1,r.session.setScrollTop(n),n=null):(r.$timer=clearInterval(r.$timer),r.$scrollAnimation=null,t&&t())},10)},this.scrollToY=function(e){this.scrollTop!==e&&(this.$loop.schedule(this.CHANGE_SCROLL),this.scrollTop=e)},this.scrollToX=function(e){this.scrollLeft!==e&&(this.scrollLeft=e),this.$loop.schedule(this.CHANGE_H_SCROLL)},this.scrollTo=function(e,t){this.session.setScrollTop(t),this.session.setScrollLeft(t)},this.scrollBy=function(e,t){t&&this.session.setScrollTop(this.session.getScrollTop()+t),e&&this.session.setScrollLeft(this.session.getScrollLeft()+e)},this.isScrollableBy=function(e,t){if(t<0&&this.session.getScrollTop()>=1-this.scrollMargin.top)return!0;if(t>0&&this.session.getScrollTop()+this.$size.scrollerHeight-this.layerConfig.maxHeight<-1+this.scrollMargin.bottom)return!0;if(e<0&&this.session.getScrollLeft()>=1-this.scrollMargin.left)return!0;if(e>0&&this.session.getScrollLeft()+this.$size.scrollerWidth-this.layerConfig.width<-1+this.scrollMargin.right)return!0},this.pixelToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=(e+this.scrollLeft-n.left-this.$padding)/this.characterWidth,i=Math.floor((t+this.scrollTop-n.top)/this.lineHeight),s=Math.round(r);return{row:i,column:s,side:r-s>0?1:-1}},this.screenToTextCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=Math.round((e+this.scrollLeft-n.left-this.$padding)/this.characterWidth),i=(t+this.scrollTop-n.top)/this.lineHeight;return this.session.screenToDocumentPosition(i,Math.max(r,0))},this.textToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=this.session.documentToScreenPosition(e,t),i=this.$padding+Math.round(r.column*this.characterWidth),s=r.row*this.lineHeight;return{pageX:n.left+i-this.scrollLeft,pageY:n.top+s-this.scrollTop}},this.visualizeFocus=function(){i.addCssClass(this.container,"ace_focus")},this.visualizeBlur=function(){i.removeCssClass(this.container,"ace_focus")},this.showComposition=function(e){this.$composition||(this.$composition={keepTextAreaAtCursor:this.$keepTextAreaAtCursor,cssText:this.textarea.style.cssText}),this.$keepTextAreaAtCursor=!0,i.addCssClass(this.textarea,"ace_composition"),this.textarea.style.cssText="",this.$moveTextAreaToCursor()},this.setCompositionText=function(e){this.$moveTextAreaToCursor()},this.hideComposition=function(){if(!this.$composition)return;i.removeCssClass(this.textarea,"ace_composition"),this.$keepTextAreaAtCursor=this.$composition.keepTextAreaAtCursor,this.textarea.style.cssText=this.$composition.cssText,this.$composition=null},this.setTheme=function(e,t){function o(r){if(n.$themeId!=e)return t&&t();if(!r||!r.cssClass)throw new Error("couldn't load module "+e+" or it didn't call define");i.importCssString(r.cssText,r.cssClass,n.container.ownerDocument),n.theme&&i.removeCssClass(n.container,n.theme.cssClass);var s="padding"in r?r.padding:"padding"in(n.theme||{})?4:n.$padding;n.$padding&&s!=n.$padding&&n.setPadding(s),n.$theme=r.cssClass,n.theme=r,i.addCssClass(n.container,r.cssClass),i.setCssClass(n.container,"ace_dark",r.isDark),n.$size&&(n.$size.width=0,n.$updateSizeAsync()),n._dispatchEvent("themeLoaded",{theme:r}),t&&t()}var n=this;this.$themeId=e,n._dispatchEvent("themeChange",{theme:e});if(!e||typeof e=="string"){var r=e||this.$options.theme.initialValue;s.loadModule(["theme",r],o)}else o(e)},this.getTheme=function(){return this.$themeId},this.setStyle=function(e,t){i.setCssClass(this.container,e,t!==!1)},this.unsetStyle=function(e){i.removeCssClass(this.container,e)},this.setCursorStyle=function(e){this.scroller.style.cursor!=e&&(this.scroller.style.cursor=e)},this.setMouseCursor=function(e){this.scroller.style.cursor=e},this.destroy=function(){this.$textLayer.destroy(),this.$cursorLayer.destroy()}}).call(g.prototype),s.defineOptions(g.prototype,"renderer",{animatedScroll:{initialValue:!1},showInvisibles:{set:function(e){this.$textLayer.setShowInvisibles(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!1},showPrintMargin:{set:function(){this.$updatePrintMargin()},initialValue:!0},printMarginColumn:{set:function(){this.$updatePrintMargin()},initialValue:80},printMargin:{set:function(e){typeof e=="number"&&(this.$printMarginColumn=e),this.$showPrintMargin=!!e,this.$updatePrintMargin()},get:function(){return this.$showPrintMargin&&this.$printMarginColumn}},showGutter:{set:function(e){this.$gutter.style.display=e?"block":"none",this.$loop.schedule(this.CHANGE_FULL),this.onGutterResize()},initialValue:!0},fadeFoldWidgets:{set:function(e){i.setCssClass(this.$gutter,"ace_fade-fold-widgets",e)},initialValue:!1},showFoldWidgets:{set:function(e){this.$gutterLayer.setShowFoldWidgets(e)},initialValue:!0},showLineNumbers:{set:function(e){this.$gutterLayer.setShowLineNumbers(e),this.$loop.schedule(this.CHANGE_GUTTER)},initialValue:!0},displayIndentGuides:{set:function(e){this.$textLayer.setDisplayIndentGuides(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!0},highlightGutterLine:{set:function(e){if(!this.$gutterLineHighlight){this.$gutterLineHighlight=i.createElement("div"),this.$gutterLineHighlight.className="ace_gutter-active-line",this.$gutter.appendChild(this.$gutterLineHighlight);return}this.$gutterLineHighlight.style.display=e?"":"none",this.$cursorLayer.$pixelPos&&this.$updateGutterLineHighlight()},initialValue:!1,value:!0},hScrollBarAlwaysVisible:{set:function(e){(!this.$hScrollBarAlwaysVisible||!this.$horizScroll)&&this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},vScrollBarAlwaysVisible:{set:function(e){(!this.$vScrollBarAlwaysVisible||!this.$vScroll)&&this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},fontSize:{set:function(e){typeof e=="number"&&(e+="px"),this.container.style.fontSize=e,this.updateFontSize()},initialValue:12},fontFamily:{set:function(e){this.container.style.fontFamily=e,this.updateFontSize()}},maxLines:{set:function(e){this.updateFull()}},minLines:{set:function(e){this.updateFull()}},maxPixelHeight:{set:function(e){this.updateFull()},initialValue:0},scrollPastEnd:{set:function(e){e=+e||0;if(this.$scrollPastEnd==e)return;this.$scrollPastEnd=e,this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:0,handlesSet:!0},fixedWidthGutter:{set:function(e){this.$gutterLayer.$fixedWidth=!!e,this.$loop.schedule(this.CHANGE_GUTTER)}},theme:{set:function(e){this.setTheme(e)},get:function(){return this.$themeId||this.theme},initialValue:"./theme/textmate",handlesSet:!0}}),t.VirtualRenderer=g}),define("ace/worker/worker_client",["require","exports","module","ace/lib/oop","ace/lib/net","ace/lib/event_emitter","ace/config"],function(e,t,n){"use strict";function u(e){var t="importScripts('"+i.qualifyURL(e)+"');";try{return new Blob([t],{type:"application/javascript"})}catch(n){var r=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder,s=new r;return s.append(t),s.getBlob("application/javascript")}}function a(e){var t=u(e),n=window.URL||window.webkitURL,r=n.createObjectURL(t);return new Worker(r)}var r=e("../lib/oop"),i=e("../lib/net"),s=e("../lib/event_emitter").EventEmitter,o=e("../config"),f=function(t,n,r,i,s){this.$sendDeltaQueue=this.$sendDeltaQueue.bind(this),this.changeListener=this.changeListener.bind(this),this.onMessage=this.onMessage.bind(this),e.nameToUrl&&!e.toUrl&&(e.toUrl=e.nameToUrl);if(o.get("packaged")||!e.toUrl)i=i||o.moduleUrl(n,"worker");else{var u=this.$normalizePath;i=i||u(e.toUrl("ace/worker/worker.js",null,"_"));var f={};t.forEach(function(t){f[t]=u(e.toUrl(t,null,"_").replace(/(\.js)?(\?.*)?$/,""))})}this.$worker=a(i),s&&this.send("importScripts",s),this.$worker.postMessage({init:!0,tlns:f,module:n,classname:r}),this.callbackId=1,this.callbacks={},this.$worker.onmessage=this.onMessage};(function(){r.implement(this,s),this.onMessage=function(e){var t=e.data;switch(t.type){case"event":this._signal(t.name,{data:t.data});break;case"call":var n=this.callbacks[t.id];n&&(n(t.data),delete this.callbacks[t.id]);break;case"error":this.reportError(t.data);break;case"log":window.console&&console.log&&console.log.apply(console,t.data)}},this.reportError=function(e){window.console&&console.error&&console.error(e)},this.$normalizePath=function(e){return i.qualifyURL(e)},this.terminate=function(){this._signal("terminate",{}),this.deltaQueue=null,this.$worker.terminate(),this.$worker=null,this.$doc&&this.$doc.off("change",this.changeListener),this.$doc=null},this.send=function(e,t){this.$worker.postMessage({command:e,args:t})},this.call=function(e,t,n){if(n){var r=this.callbackId++;this.callbacks[r]=n,t.push(r)}this.send(e,t)},this.emit=function(e,t){try{this.$worker.postMessage({event:e,data:{data:t.data}})}catch(n){console.error(n.stack)}},this.attachToDocument=function(e){this.$doc&&this.terminate(),this.$doc=e,this.call("setValue",[e.getValue()]),e.on("change",this.changeListener)},this.changeListener=function(e){this.deltaQueue||(this.deltaQueue=[],setTimeout(this.$sendDeltaQueue,0)),e.action=="insert"?this.deltaQueue.push(e.start,e.lines):this.deltaQueue.push(e.start,e.end)},this.$sendDeltaQueue=function(){var e=this.deltaQueue;if(!e)return;this.deltaQueue=null,e.length>50&&e.length>this.$doc.getLength()>>1?this.call("setValue",[this.$doc.getValue()]):this.emit("change",{data:e})}}).call(f.prototype);var l=function(e,t,n){this.$sendDeltaQueue=this.$sendDeltaQueue.bind(this),this.changeListener=this.changeListener.bind(this),this.callbackId=1,this.callbacks={},this.messageBuffer=[];var r=null,i=!1,u=Object.create(s),a=this;this.$worker={},this.$worker.terminate=function(){},this.$worker.postMessage=function(e){a.messageBuffer.push(e),r&&(i?setTimeout(f):f())},this.setEmitSync=function(e){i=e};var f=function(){var e=a.messageBuffer.shift();e.command?r[e.command].apply(r,e.args):e.event&&u._signal(e.event,e.data)};u.postMessage=function(e){a.onMessage({data:e})},u.callback=function(e,t){this.postMessage({type:"call",id:t,data:e})},u.emit=function(e,t){this.postMessage({type:"event",name:e,data:t})},o.loadModule(["worker",t],function(e){r=new e[n](u);while(a.messageBuffer.length)f()})};l.prototype=f.prototype,t.UIWorkerClient=l,t.WorkerClient=f,t.createWorker=a}),define("ace/placeholder",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/oop"],function(e,t,n){"use strict";var r=e("./range").Range,i=e("./lib/event_emitter").EventEmitter,s=e("./lib/oop"),o=function(e,t,n,r,i,s){var o=this;this.length=t,this.session=e,this.doc=e.getDocument(),this.mainClass=i,this.othersClass=s,this.$onUpdate=this.onUpdate.bind(this),this.doc.on("change",this.$onUpdate),this.$others=r,this.$onCursorChange=function(){setTimeout(function(){o.onCursorChange()})},this.$pos=n;var u=e.getUndoManager().$undoStack||e.getUndoManager().$undostack||{length:-1};this.$undoStackDepth=u.length,this.setup(),e.selection.on("changeCursor",this.$onCursorChange)};(function(){s.implement(this,i),this.setup=function(){var e=this,t=this.doc,n=this.session;this.selectionBefore=n.selection.toJSON(),n.selection.inMultiSelectMode&&n.selection.toSingleRange(),this.pos=t.createAnchor(this.$pos.row,this.$pos.column);var i=this.pos;i.$insertRight=!0,i.detach(),i.markerId=n.addMarker(new r(i.row,i.column,i.row,i.column+this.length),this.mainClass,null,!1),this.others=[],this.$others.forEach(function(n){var r=t.createAnchor(n.row,n.column);r.$insertRight=!0,r.detach(),e.others.push(r)}),n.setUndoSelect(!1)},this.showOtherMarkers=function(){if(this.othersActive)return;var e=this.session,t=this;this.othersActive=!0,this.others.forEach(function(n){n.markerId=e.addMarker(new r(n.row,n.column,n.row,n.column+t.length),t.othersClass,null,!1)})},this.hideOtherMarkers=function(){if(!this.othersActive)return;this.othersActive=!1;for(var e=0;e<this.others.length;e++)this.session.removeMarker(this.others[e].markerId)},this.onUpdate=function(e){if(this.$updating)return this.updateAnchors(e);var t=e;if(t.start.row!==t.end.row)return;if(t.start.row!==this.pos.row)return;this.$updating=!0;var n=e.action==="insert"?t.end.column-t.start.column:t.start.column-t.end.column,i=t.start.column>=this.pos.column&&t.start.column<=this.pos.column+this.length+1,s=t.start.column-this.pos.column;this.updateAnchors(e),i&&(this.length+=n);if(i&&!this.session.$fromUndo)if(e.action==="insert")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.insertMergedLines(a,e.lines)}else if(e.action==="remove")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.remove(new r(a.row,a.column,a.row,a.column-n))}this.$updating=!1,this.updateMarkers()},this.updateAnchors=function(e){this.pos.onChange(e);for(var t=this.others.length;t--;)this.others[t].onChange(e);this.updateMarkers()},this.updateMarkers=function(){if(this.$updating)return;var e=this,t=this.session,n=function(n,i){t.removeMarker(n.markerId),n.markerId=t.addMarker(new r(n.row,n.column,n.row,n.column+e.length),i,null,!1)};n(this.pos,this.mainClass);for(var i=this.others.length;i--;)n(this.others[i],this.othersClass)},this.onCursorChange=function(e){if(this.$updating||!this.session)return;var t=this.session.selection.getCursor();t.row===this.pos.row&&t.column>=this.pos.column&&t.column<=this.pos.column+this.length?(this.showOtherMarkers(),this._emit("cursorEnter",e)):(this.hideOtherMarkers(),this._emit("cursorLeave",e))},this.detach=function(){this.session.removeMarker(this.pos&&this.pos.markerId),this.hideOtherMarkers(),this.doc.removeEventListener("change",this.$onUpdate),this.session.selection.removeEventListener("changeCursor",this.$onCursorChange),this.session.setUndoSelect(!0),this.session=null},this.cancel=function(){if(this.$undoStackDepth===-1)return;var e=this.session.getUndoManager(),t=(e.$undoStack||e.$undostack).length-this.$undoStackDepth;for(var n=0;n<t;n++)e.undo(!0);this.selectionBefore&&this.session.selection.fromJSON(this.selectionBefore)}}).call(o.prototype),t.PlaceHolder=o}),define("ace/mouse/multi_select_handler",["require","exports","module","ace/lib/event","ace/lib/useragent"],function(e,t,n){function s(e,t){return e.row==t.row&&e.column==t.column}function o(e){var t=e.domEvent,n=t.altKey,o=t.shiftKey,u=t.ctrlKey,a=e.getAccelKey(),f=e.getButton();u&&i.isMac&&(f=t.button);if(e.editor.inMultiSelectMode&&f==2){e.editor.textInput.onContextMenu(e.domEvent);return}if(!u&&!n&&!a){f===0&&e.editor.inMultiSelectMode&&e.editor.exitMultiSelectMode();return}if(f!==0)return;var l=e.editor,c=l.selection,h=l.inMultiSelectMode,p=e.getDocumentPosition(),d=c.getCursor(),v=e.inSelection()||c.isEmpty()&&s(p,d),m=e.x,g=e.y,y=function(e){m=e.clientX,g=e.clientY},b=l.session,w=l.renderer.pixelToScreenCoordinates(m,g),E=w,S;if(l.$mouseHandler.$enableJumpToDef)u&&n||a&&n?S=o?"block":"add":n&&l.$blockSelectEnabled&&(S="block");else if(a&&!n){S="add";if(!h&&o)return}else n&&l.$blockSelectEnabled&&(S="block");S&&i.isMac&&t.ctrlKey&&l.$mouseHandler.cancelContextMenu();if(S=="add"){if(!h&&v)return;if(!h){var x=c.toOrientedRange();l.addSelectionMarker(x)}var T=c.rangeList.rangeAtPoint(p);l.$blockScrolling++,l.inVirtualSelectionMode=!0,o&&(T=null,x=c.ranges[0]||x,l.removeSelectionMarker(x)),l.once("mouseup",function(){var e=c.toOrientedRange();T&&e.isEmpty()&&s(T.cursor,e.cursor)?c.substractPoint(e.cursor):(o?c.substractPoint(x.cursor):x&&(l.removeSelectionMarker(x),c.addRange(x)),c.addRange(e)),l.$blockScrolling--,l.inVirtualSelectionMode=!1})}else if(S=="block"){e.stop(),l.inVirtualSelectionMode=!0;var N,C=[],k=function(){var e=l.renderer.pixelToScreenCoordinates(m,g),t=b.screenToDocumentPosition(e.row,e.column);if(s(E,e)&&s(t,c.lead))return;E=e,l.$blockScrolling++,l.selection.moveToPosition(t),l.renderer.scrollCursorIntoView(),l.removeSelectionMarkers(C),C=c.rectangularRangeBlock(E,w),l.$mouseHandler.$clickSelection&&C.length==1&&C[0].isEmpty()&&(C[0]=l.$mouseHandler.$clickSelection.clone()),C.forEach(l.addSelectionMarker,l),l.updateSelectionMarkers(),l.$blockScrolling--};l.$blockScrolling++,h&&!a?c.toSingleRange():!h&&a&&(N=c.toOrientedRange(),l.addSelectionMarker(N)),o?w=b.documentToScreenPosition(c.lead):c.moveToPosition(p),l.$blockScrolling--,E={row:-1,column:-1};var L=function(e){clearInterval(O),l.removeSelectionMarkers(C),C.length||(C=[c.toOrientedRange()]),l.$blockScrolling++,N&&(l.removeSelectionMarker(N),c.toSingleRange(N));for(var t=0;t<C.length;t++)c.addRange(C[t]);l.inVirtualSelectionMode=!1,l.$mouseHandler.$clickSelection=null,l.$blockScrolling--},A=k;r.capture(l.container,y,L);var O=setInterval(function(){A()},20);return e.preventDefault()}}var r=e("../lib/event"),i=e("../lib/useragent");t.onMouseDown=o}),define("ace/commands/multi_select_commands",["require","exports","module","ace/keyboard/hash_handler"],function(e,t,n){t.defaultCommands=[{name:"addCursorAbove",exec:function(e){e.selectMoreLines(-1)},bindKey:{win:"Ctrl-Alt-Up",mac:"Ctrl-Alt-Up"},scrollIntoView:"cursor",readOnly:!0},{name:"addCursorBelow",exec:function(e){e.selectMoreLines(1)},bindKey:{win:"Ctrl-Alt-Down",mac:"Ctrl-Alt-Down"},scrollIntoView:"cursor",readOnly:!0},{name:"addCursorAboveSkipCurrent",exec:function(e){e.selectMoreLines(-1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Up",mac:"Ctrl-Alt-Shift-Up"},scrollIntoView:"cursor",readOnly:!0},{name:"addCursorBelowSkipCurrent",exec:function(e){e.selectMoreLines(1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Down",mac:"Ctrl-Alt-Shift-Down"},scrollIntoView:"cursor",readOnly:!0},{name:"selectMoreBefore",exec:function(e){e.selectMore(-1)},bindKey:{win:"Ctrl-Alt-Left",mac:"Ctrl-Alt-Left"},scrollIntoView:"cursor",readOnly:!0},{name:"selectMoreAfter",exec:function(e){e.selectMore(1)},bindKey:{win:"Ctrl-Alt-Right",mac:"Ctrl-Alt-Right"},scrollIntoView:"cursor",readOnly:!0},{name:"selectNextBefore",exec:function(e){e.selectMore(-1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Left",mac:"Ctrl-Alt-Shift-Left"},scrollIntoView:"cursor",readOnly:!0},{name:"selectNextAfter",exec:function(e){e.selectMore(1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Right",mac:"Ctrl-Alt-Shift-Right"},scrollIntoView:"cursor",readOnly:!0},{name:"splitIntoLines",exec:function(e){e.multiSelect.splitIntoLines()},bindKey:{win:"Ctrl-Alt-L",mac:"Ctrl-Alt-L"},readOnly:!0},{name:"alignCursors",exec:function(e){e.alignCursors()},bindKey:{win:"Ctrl-Alt-A",mac:"Ctrl-Alt-A"},scrollIntoView:"cursor"},{name:"findAll",exec:function(e){e.findAll()},bindKey:{win:"Ctrl-Alt-K",mac:"Ctrl-Alt-G"},scrollIntoView:"cursor",readOnly:!0}],t.multiSelectCommands=[{name:"singleSelection",bindKey:"esc",exec:function(e){e.exitMultiSelectMode()},scrollIntoView:"cursor",readOnly:!0,isAvailable:function(e){return e&&e.inMultiSelectMode}}];var r=e("../keyboard/hash_handler").HashHandler;t.keyboardHandler=new r(t.multiSelectCommands)}),define("ace/multi_select",["require","exports","module","ace/range_list","ace/range","ace/selection","ace/mouse/multi_select_handler","ace/lib/event","ace/lib/lang","ace/commands/multi_select_commands","ace/search","ace/edit_session","ace/editor","ace/config"],function(e,t,n){function h(e,t,n){return c.$options.wrap=!0,c.$options.needle=t,c.$options.backwards=n==-1,c.find(e)}function v(e,t){return e.row==t.row&&e.column==t.column}function m(e){if(e.$multiselectOnSessionChange)return;e.$onAddRange=e.$onAddRange.bind(e),e.$onRemoveRange=e.$onRemoveRange.bind(e),e.$onMultiSelect=e.$onMultiSelect.bind(e),e.$onSingleSelect=e.$onSingleSelect.bind(e),e.$multiselectOnSessionChange=t.onSessionChange.bind(e),e.$checkMultiselectChange=e.$checkMultiselectChange.bind(e),e.$multiselectOnSessionChange(e),e.on("changeSession",e.$multiselectOnSessionChange),e.on("mousedown",o),e.commands.addCommands(f.defaultCommands),g(e)}function g(e){function r(t){n&&(e.renderer.setMouseCursor(""),n=!1)}var t=e.textInput.getElement(),n=!1;u.addListener(t,"keydown",function(t){var i=t.keyCode==18&&!(t.ctrlKey||t.shiftKey||t.metaKey);e.$blockSelectEnabled&&i?n||(e.renderer.setMouseCursor("crosshair"),n=!0):n&&r()}),u.addListener(t,"keyup",r),u.addListener(t,"blur",r)}var r=e("./range_list").RangeList,i=e("./range").Range,s=e("./selection").Selection,o=e("./mouse/multi_select_handler").onMouseDown,u=e("./lib/event"),a=e("./lib/lang"),f=e("./commands/multi_select_commands");t.commands=f.defaultCommands.concat(f.multiSelectCommands);var l=e("./search").Search,c=new l,p=e("./edit_session").EditSession;(function(){this.getSelectionMarkers=function(){return this.$selectionMarkers}}).call(p.prototype),function(){this.ranges=null,this.rangeList=null,this.addRange=function(e,t){if(!e)return;if(!this.inMultiSelectMode&&this.rangeCount===0){var n=this.toOrientedRange();this.rangeList.add(n),this.rangeList.add(e);if(this.rangeList.ranges.length!=2)return this.rangeList.removeAll(),t||this.fromOrientedRange(e);this.rangeList.removeAll(),this.rangeList.add(n),this.$onAddRange(n)}e.cursor||(e.cursor=e.end);var r=this.rangeList.add(e);return this.$onAddRange(e),r.length&&this.$onRemoveRange(r),this.rangeCount>1&&!this.inMultiSelectMode&&(this._signal("multiSelect"),this.inMultiSelectMode=!0,this.session.$undoSelect=!1,this.rangeList.attach(this.session)),t||this.fromOrientedRange(e)},this.toSingleRange=function(e){e=e||this.ranges[0];var t=this.rangeList.removeAll();t.length&&this.$onRemoveRange(t),e&&this.fromOrientedRange(e)},this.substractPoint=function(e){var t=this.rangeList.substractPoint(e);if(t)return this.$onRemoveRange(t),t[0]},this.mergeOverlappingRanges=function(){var e=this.rangeList.merge();e.length?this.$onRemoveRange(e):this.ranges[0]&&this.fromOrientedRange(this.ranges[0])},this.$onAddRange=function(e){this.rangeCount=this.rangeList.ranges.length,this.ranges.unshift(e),this._signal("addRange",{range:e})},this.$onRemoveRange=function(e){this.rangeCount=this.rangeList.ranges.length;if(this.rangeCount==1&&this.inMultiSelectMode){var t=this.rangeList.ranges.pop();e.push(t),this.rangeCount=0}for(var n=e.length;n--;){var r=this.ranges.indexOf(e[n]);this.ranges.splice(r,1)}this._signal("removeRange",{ranges:e}),this.rangeCount===0&&this.inMultiSelectMode&&(this.inMultiSelectMode=!1,this._signal("singleSelect"),this.session.$undoSelect=!0,this.rangeList.detach(this.session)),t=t||this.ranges[0],t&&!t.isEqual(this.getRange())&&this.fromOrientedRange(t)},this.$initRangeList=function(){if(this.rangeList)return;this.rangeList=new r,this.ranges=[],this.rangeCount=0},this.getAllRanges=function(){return this.rangeCount?this.rangeList.ranges.concat():[this.getRange()]},this.splitIntoLines=function(){if(this.rangeCount>1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var n=this.getRange(),r=this.isBackwards(),s=n.start.row,o=n.end.row;if(s==o){if(r)var u=n.end,a=n.start;else var u=n.start,a=n.end;this.addRange(i.fromPoints(a,a)),this.addRange(i.fromPoints(u,u));return}var f=[],l=this.getLineRange(s,!0);l.start.column=n.start.column,f.push(l);for(var c=s+1;c<o;c++)f.push(this.getLineRange(c,!0));l=this.getLineRange(o,!0),l.end.column=n.end.column,f.push(l),f.forEach(this.addRange,this)}},this.toggleBlockSelection=function(){if(this.rangeCount>1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var r=this.session.documentToScreenPosition(this.selectionLead),s=this.session.documentToScreenPosition(this.selectionAnchor),o=this.rectangularRangeBlock(r,s);o.forEach(this.addRange,this)}},this.rectangularRangeBlock=function(e,t,n){var r=[],s=e.column<t.column;if(s)var o=e.column,u=t.column;else var o=t.column,u=e.column;var a=e.row<t.row;if(a)var f=e.row,l=t.row;else var f=t.row,l=e.row;o<0&&(o=0),f<0&&(f=0),f==l&&(n=!0);for(var c=f;c<=l;c++){var h=i.fromPoints(this.session.screenToDocumentPosition(c,o),this.session.screenToDocumentPosition(c,u));if(h.isEmpty()){if(p&&v(h.end,p))break;var p=h.end}h.cursor=s?h.start:h.end,r.push(h)}a&&r.reverse();if(!n){var d=r.length-1;while(r[d].isEmpty()&&d>0)d--;if(d>0){var m=0;while(r[m].isEmpty())m++}for(var g=d;g>=m;g--)r[g].isEmpty()&&r.splice(g,1)}return r}}.call(s.prototype);var d=e("./editor").Editor;(function(){this.updateSelectionMarkers=function(){this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.addSelectionMarker=function(e){e.cursor||(e.cursor=e.end);var t=this.getSelectionStyle();return e.marker=this.session.addMarker(e,"ace_selection",t),this.session.$selectionMarkers.push(e),this.session.selectionMarkerCount=this.session.$selectionMarkers.length,e},this.removeSelectionMarker=function(e){if(!e.marker)return;this.session.removeMarker(e.marker);var t=this.session.$selectionMarkers.indexOf(e);t!=-1&&this.session.$selectionMarkers.splice(t,1),this.session.selectionMarkerCount=this.session.$selectionMarkers.length},this.removeSelectionMarkers=function(e){var t=this.session.$selectionMarkers;for(var n=e.length;n--;){var r=e[n];if(!r.marker)continue;this.session.removeMarker(r.marker);var i=t.indexOf(r);i!=-1&&t.splice(i,1)}this.session.selectionMarkerCount=t.length},this.$onAddRange=function(e){this.addSelectionMarker(e.range),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onRemoveRange=function(e){this.removeSelectionMarkers(e.ranges),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelect=function(e){if(this.inMultiSelectMode)return;this.inMultiSelectMode=!0,this.setStyle("ace_multiselect"),this.keyBinding.addKeyboardHandler(f.keyboardHandler),this.commands.setDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onSingleSelect=function(e){if(this.session.multiSelect.inVirtualMode)return;this.inMultiSelectMode=!1,this.unsetStyle("ace_multiselect"),this.keyBinding.removeKeyboardHandler(f.keyboardHandler),this.commands.removeDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers(),this._emit("changeSelection")},this.$onMultiSelectExec=function(e){var t=e.command,n=e.editor;if(!n.multiSelect)return;if(!t.multiSelectAction){var r=t.exec(n,e.args||{});n.multiSelect.addRange(n.multiSelect.toOrientedRange()),n.multiSelect.mergeOverlappingRanges()}else t.multiSelectAction=="forEach"?r=n.forEachSelection(t,e.args):t.multiSelectAction=="forEachLine"?r=n.forEachSelection(t,e.args,!0):t.multiSelectAction=="single"?(n.exitMultiSelectMode(),r=t.exec(n,e.args||{})):r=t.multiSelectAction(n,e.args||{});return r},this.forEachSelection=function(e,t,n){if(this.inVirtualSelectionMode)return;var r=n&&n.keepOrder,i=n==1||n&&n.$byLines,o=this.session,u=this.selection,a=u.rangeList,f=(r?u:a).ranges,l;if(!f.length)return e.exec?e.exec(this,t||{}):e(this,t||{});var c=u._eventRegistry;u._eventRegistry={};var h=new s(o);this.inVirtualSelectionMode=!0;for(var p=f.length;p--;){if(i)while(p>0&&f[p].start.row==f[p-1].end.row)p--;h.fromOrientedRange(f[p]),h.index=p,this.selection=o.selection=h;var d=e.exec?e.exec(this,t||{}):e(this,t||{});!l&&d!==undefined&&(l=d),h.toOrientedRange(f[p])}h.detach(),this.selection=o.selection=u,this.inVirtualSelectionMode=!1,u._eventRegistry=c,u.mergeOverlappingRanges();var v=this.renderer.$scrollAnimation;return this.onCursorChange(),this.onSelectionChange(),v&&v.from==v.to&&this.renderer.animateScrolling(v.from),l},this.exitMultiSelectMode=function(){if(!this.inMultiSelectMode||this.inVirtualSelectionMode)return;this.multiSelect.toSingleRange()},this.getSelectedText=function(){var e="";if(this.inMultiSelectMode&&!this.inVirtualSelectionMode){var t=this.multiSelect.rangeList.ranges,n=[];for(var r=0;r<t.length;r++)n.push(this.session.getTextRange(t[r]));var i=this.session.getDocument().getNewLineCharacter();e=n.join(i),e.length==(n.length-1)*i.length&&(e="")}else this.selection.isEmpty()||(e=this.session.getTextRange(this.getSelectionRange()));return e},this.$checkMultiselectChange=function(e,t){if(this.inMultiSelectMode&&!this.inVirtualSelectionMode){var n=this.multiSelect.ranges[0];if(this.multiSelect.isEmpty()&&t==this.multiSelect.anchor)return;var r=t==this.multiSelect.anchor?n.cursor==n.start?n.end:n.start:n.cursor;(r.row!=t.row||this.session.$clipPositionToDocument(r.row,r.column).column!=t.column)&&this.multiSelect.toSingleRange(this.multiSelect.toOrientedRange())}},this.findAll=function(e,t,n){t=t||{},t.needle=e||t.needle;if(t.needle==undefined){var r=this.selection.isEmpty()?this.selection.getWordRange():this.selection.getRange();t.needle=this.session.getTextRange(r)}this.$search.set(t);var i=this.$search.findAll(this.session);if(!i.length)return 0;this.$blockScrolling+=1;var s=this.multiSelect;n||s.toSingleRange(i[0]);for(var o=i.length;o--;)s.addRange(i[o],!0);return r&&s.rangeList.rangeAtPoint(r.start)&&s.addRange(r,!0),this.$blockScrolling-=1,i.length},this.selectMoreLines=function(e,t){var n=this.selection.toOrientedRange(),r=n.cursor==n.end,s=this.session.documentToScreenPosition(n.cursor);this.selection.$desiredColumn&&(s.column=this.selection.$desiredColumn);var o=this.session.screenToDocumentPosition(s.row+e,s.column);if(!n.isEmpty())var u=this.session.documentToScreenPosition(r?n.end:n.start),a=this.session.screenToDocumentPosition(u.row+e,u.column);else var a=o;if(r){var f=i.fromPoints(o,a);f.cursor=f.start}else{var f=i.fromPoints(a,o);f.cursor=f.end}f.desiredColumn=s.column;if(!this.selection.inMultiSelectMode)this.selection.addRange(n);else if(t)var l=n.cursor;this.selection.addRange(f),l&&this.selection.substractPoint(l)},this.transposeSelections=function(e){var t=this.session,n=t.multiSelect,r=n.ranges;for(var i=r.length;i--;){var s=r[i];if(s.isEmpty()){var o=t.getWordRange(s.start.row,s.start.column);s.start.row=o.start.row,s.start.column=o.start.column,s.end.row=o.end.row,s.end.column=o.end.column}}n.mergeOverlappingRanges();var u=[];for(var i=r.length;i--;){var s=r[i];u.unshift(t.getTextRange(s))}e<0?u.unshift(u.pop()):u.push(u.shift());for(var i=r.length;i--;){var s=r[i],o=s.clone();t.replace(s,u[i]),s.start.row=o.start.row,s.start.column=o.start.column}},this.selectMore=function(e,t,n){var r=this.session,i=r.multiSelect,s=i.toOrientedRange();if(s.isEmpty()){s=r.getWordRange(s.start.row,s.start.column),s.cursor=e==-1?s.start:s.end,this.multiSelect.addRange(s);if(n)return}var o=r.getTextRange(s),u=h(r,o,e);u&&(u.cursor=e==-1?u.start:u.end,this.$blockScrolling+=1,this.session.unfold(u),this.multiSelect.addRange(u),this.$blockScrolling-=1,this.renderer.scrollCursorIntoView(null,.5)),t&&this.multiSelect.substractPoint(s.cursor)},this.alignCursors=function(){var e=this.session,t=e.multiSelect,n=t.ranges,r=-1,s=n.filter(function(e){if(e.cursor.row==r)return!0;r=e.cursor.row});if(!n.length||s.length==n.length-1){var o=this.selection.getRange(),u=o.start.row,f=o.end.row,l=u==f;if(l){var c=this.session.getLength(),h;do h=this.session.getLine(f);while(/[=:]/.test(h)&&++f<c);do h=this.session.getLine(u);while(/[=:]/.test(h)&&--u>0);u<0&&(u=0),f>=c&&(f=c-1)}var p=this.session.removeFullLines(u,f);p=this.$reAlignText(p,l),this.session.insert({row:u,column:0},p.join("\n")+"\n"),l||(o.start.column=0,o.end.column=p[p.length-1].length),this.selection.setRange(o)}else{s.forEach(function(e){t.substractPoint(e.cursor)});var d=0,v=Infinity,m=n.map(function(t){var n=t.cursor,r=e.getLine(n.row),i=r.substr(n.column).search(/\S/g);return i==-1&&(i=0),n.column>d&&(d=n.column),i<v&&(v=i),i});n.forEach(function(t,n){var r=t.cursor,s=d-r.column,o=m[n]-v;s>o?e.insert(r,a.stringRepeat(" ",s-o)):e.remove(new i(r.row,r.column,r.row,r.column-s+o)),t.start.column=t.end.column=d,t.start.row=t.end.row=r.row,t.cursor=t.end}),t.fromOrientedRange(n[0]),this.renderer.updateCursor(),this.renderer.updateBackMarkers()}},this.$reAlignText=function(e,t){function u(e){return a.stringRepeat(" ",e)}function f(e){return e[2]?u(i)+e[2]+u(s-e[2].length+o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function l(e){return e[2]?u(i+s-e[2].length)+e[2]+u(o," ")+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function c(e){return e[2]?u(i)+e[2]+u(o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}var n=!0,r=!0,i,s,o;return e.map(function(e){var t=e.match(/(\s*)(.*?)(\s*)([=:].*)/);return t?i==null?(i=t[1].length,s=t[2].length,o=t[3].length,t):(i+s+o!=t[1].length+t[2].length+t[3].length&&(r=!1),i!=t[1].length&&(n=!1),i>t[1].length&&(i=t[1].length),s<t[2].length&&(s=t[2].length),o>t[3].length&&(o=t[3].length),t):[e]}).map(t?f:n?r?l:f:c)}}).call(d.prototype),t.onSessionChange=function(e){var t=e.session;t&&!t.multiSelect&&(t.$selectionMarkers=[],t.selection.$initRangeList(),t.multiSelect=t.selection),this.multiSelect=t&&t.multiSelect;var n=e.oldSession;n&&(n.multiSelect.off("addRange",this.$onAddRange),n.multiSelect.off("removeRange",this.$onRemoveRange),n.multiSelect.off("multiSelect",this.$onMultiSelect),n.multiSelect.off("singleSelect",this.$onSingleSelect),n.multiSelect.lead.off("change",this.$checkMultiselectChange),n.multiSelect.anchor.off("change",this.$checkMultiselectChange)),t&&(t.multiSelect.on("addRange",this.$onAddRange),t.multiSelect.on("removeRange",this.$onRemoveRange),t.multiSelect.on("multiSelect",this.$onMultiSelect),t.multiSelect.on("singleSelect",this.$onSingleSelect),t.multiSelect.lead.on("change",this.$checkMultiselectChange),t.multiSelect.anchor.on("change",this.$checkMultiselectChange)),t&&this.inMultiSelectMode!=t.selection.inMultiSelectMode&&(t.selection.inMultiSelectMode?this.$onMultiSelect():this.$onSingleSelect())},t.MultiSelect=m,e("./config").defineOptions(d.prototype,"editor",{enableMultiselect:{set:function(e){m(this),e?(this.on("changeSession",this.$multiselectOnSessionChange),this.on("mousedown",o)):(this.off("changeSession",this.$multiselectOnSessionChange),this.off("mousedown",o))},value:!0},enableBlockSelect:{set:function(e){this.$blockSelectEnabled=e},value:!0}})}),define("ace/mode/folding/fold_mode",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../../range").Range,i=t.FoldMode=function(){};(function(){this.foldingStartMarker=null,this.foldingStopMarker=null,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);return this.foldingStartMarker.test(r)?"start":t=="markbeginend"&&this.foldingStopMarker&&this.foldingStopMarker.test(r)?"end":""},this.getFoldWidgetRange=function(e,t,n){return null},this.indentationBlock=function(e,t,n){var i=/\S/,s=e.getLine(t),o=s.search(i);if(o==-1)return;var u=n||s.length,a=e.getLength(),f=t,l=t;while(++t<a){var c=e.getLine(t).search(i);if(c==-1)continue;if(c<=o)break;l=t}if(l>f){var h=e.getLine(l).length;return new r(f,u,l,h)}},this.openingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i+1},u=e.$findClosingBracket(t,o,s);if(!u)return;var a=e.foldWidgets[u.row];return a==null&&(a=e.getFoldWidget(u.row)),a=="start"&&u.row>o.row&&(u.row--,u.column=e.getLine(u.row).length),r.fromPoints(o,u)},this.closingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i},u=e.$findOpeningBracket(t,o);if(!u)return;return u.column++,o.column--,r.fromPoints(u,o)}}).call(i.prototype)}),define("ace/theme/textmate",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";t.isDark=!1,t.cssClass="ace-tm",t.cssText='.ace-tm .ace_gutter {background: #f0f0f0;color: #333;}.ace-tm .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-tm .ace_fold {background-color: #6B72E6;}.ace-tm {background-color: #FFFFFF;color: black;}.ace-tm .ace_cursor {color: black;}.ace-tm .ace_invisible {color: rgb(191, 191, 191);}.ace-tm .ace_storage,.ace-tm .ace_keyword {color: blue;}.ace-tm .ace_constant {color: rgb(197, 6, 11);}.ace-tm .ace_constant.ace_buildin {color: rgb(88, 72, 246);}.ace-tm .ace_constant.ace_language {color: rgb(88, 92, 246);}.ace-tm .ace_constant.ace_library {color: rgb(6, 150, 14);}.ace-tm .ace_invalid {background-color: rgba(255, 0, 0, 0.1);color: red;}.ace-tm .ace_support.ace_function {color: rgb(60, 76, 114);}.ace-tm .ace_support.ace_constant {color: rgb(6, 150, 14);}.ace-tm .ace_support.ace_type,.ace-tm .ace_support.ace_class {color: rgb(109, 121, 222);}.ace-tm .ace_keyword.ace_operator {color: rgb(104, 118, 135);}.ace-tm .ace_string {color: rgb(3, 106, 7);}.ace-tm .ace_comment {color: rgb(76, 136, 107);}.ace-tm .ace_comment.ace_doc {color: rgb(0, 102, 255);}.ace-tm .ace_comment.ace_doc.ace_tag {color: rgb(128, 159, 191);}.ace-tm .ace_constant.ace_numeric {color: rgb(0, 0, 205);}.ace-tm .ace_variable {color: rgb(49, 132, 149);}.ace-tm .ace_xml-pe {color: rgb(104, 104, 91);}.ace-tm .ace_entity.ace_name.ace_function {color: #0000A2;}.ace-tm .ace_heading {color: rgb(12, 7, 255);}.ace-tm .ace_list {color:rgb(185, 6, 144);}.ace-tm .ace_meta.ace_tag {color:rgb(0, 22, 142);}.ace-tm .ace_string.ace_regex {color: rgb(255, 0, 0)}.ace-tm .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-tm.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px white;}.ace-tm .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-tm .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-tm .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-tm .ace_marker-layer .ace_active-line {background: rgba(0, 0, 0, 0.07);}.ace-tm .ace_gutter-active-line {background-color : #dcdcdc;}.ace-tm .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-tm .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}';var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}),define("ace/line_widgets",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/range"],function(e,t,n){"use strict";function o(e){this.session=e,this.session.widgetManager=this,this.session.getRowLength=this.getRowLength,this.session.$getWidgetScreenLength=this.$getWidgetScreenLength,this.updateOnChange=this.updateOnChange.bind(this),this.renderWidgets=this.renderWidgets.bind(this),this.measureWidgets=this.measureWidgets.bind(this),this.session._changedWidgets=[],this.$onChangeEditor=this.$onChangeEditor.bind(this),this.session.on("change",this.updateOnChange),this.session.on("changeFold",this.updateOnFold),this.session.on("changeEditor",this.$onChangeEditor)}var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./range").Range;(function(){this.getRowLength=function(e){var t;return this.lineWidgets?t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0:t=0,!this.$useWrapMode||!this.$wrapData[e]?1+t:this.$wrapData[e].length+1+t},this.$getWidgetScreenLength=function(){var e=0;return this.lineWidgets.forEach(function(t){t&&t.rowCount&&!t.hidden&&(e+=t.rowCount)}),e},this.$onChangeEditor=function(e){this.attach(e.editor)},this.attach=function(e){e&&e.widgetManager&&e.widgetManager!=this&&e.widgetManager.detach();if(this.editor==e)return;this.detach(),this.editor=e,e&&(e.widgetManager=this,e.renderer.on("beforeRender",this.measureWidgets),e.renderer.on("afterRender",this.renderWidgets))},this.detach=function(e){var t=this.editor;if(!t)return;this.editor=null,t.widgetManager=null,t.renderer.off("beforeRender",this.measureWidgets),t.renderer.off("afterRender",this.renderWidgets);var n=this.session.lineWidgets;n&&n.forEach(function(e){e&&e.el&&e.el.parentNode&&(e._inDocument=!1,e.el.parentNode.removeChild(e.el))})},this.updateOnFold=function(e,t){var n=t.lineWidgets;if(!n||!e.action)return;var r=e.data,i=r.start.row,s=r.end.row,o=e.action=="add";for(var u=i+1;u<s;u++)n[u]&&(n[u].hidden=o);n[s]&&(o?n[i]?n[s].hidden=o:n[i]=n[s]:(n[i]==n[s]&&(n[i]=undefined),n[s].hidden=o))},this.updateOnChange=function(e){var t=this.session.lineWidgets;if(!t)return;var n=e.start.row,r=e.end.row-n;if(r!==0)if(e.action=="remove"){var i=t.splice(n+1,r);i.forEach(function(e){e&&this.removeLineWidget(e)},this),this.$updateRows()}else{var s=new Array(r);s.unshift(n,0),t.splice.apply(t,s),this.$updateRows()}},this.$updateRows=function(){var e=this.session.lineWidgets;if(!e)return;var t=!0;e.forEach(function(e,n){if(e){t=!1,e.row=n;while(e.$oldWidget)e.$oldWidget.row=n,e=e.$oldWidget}}),t&&(this.session.lineWidgets=null)},this.addLineWidget=function(e){this.session.lineWidgets||(this.session.lineWidgets=new Array(this.session.getLength()));var t=this.session.lineWidgets[e.row];t&&(e.$oldWidget=t,t.el&&t.el.parentNode&&(t.el.parentNode.removeChild(t.el),t._inDocument=!1)),this.session.lineWidgets[e.row]=e,e.session=this.session;var n=this.editor.renderer;e.html&&!e.el&&(e.el=i.createElement("div"),e.el.innerHTML=e.html),e.el&&(i.addCssClass(e.el,"ace_lineWidgetContainer"),e.el.style.position="absolute",e.el.style.zIndex=5,n.container.appendChild(e.el),e._inDocument=!0),e.coverGutter||(e.el.style.zIndex=3),e.pixelHeight==null&&(e.pixelHeight=e.el.offsetHeight),e.rowCount==null&&(e.rowCount=e.pixelHeight/n.layerConfig.lineHeight);var r=this.session.getFoldAt(e.row,0);e.$fold=r;if(r){var s=this.session.lineWidgets;e.row==r.end.row&&!s[r.start.row]?s[r.start.row]=e:e.hidden=!0}return this.session._emit("changeFold",{data:{start:{row:e.row}}}),this.$updateRows(),this.renderWidgets(null,n),this.onWidgetChanged(e),e},this.removeLineWidget=function(e){e._inDocument=!1,e.session=null,e.el&&e.el.parentNode&&e.el.parentNode.removeChild(e.el);if(e.editor&&e.editor.destroy)try{e.editor.destroy()}catch(t){}if(this.session.lineWidgets){var n=this.session.lineWidgets[e.row];if(n==e)this.session.lineWidgets[e.row]=e.$oldWidget,e.$oldWidget&&this.onWidgetChanged(e.$oldWidget);else while(n){if(n.$oldWidget==e){n.$oldWidget=e.$oldWidget;break}n=n.$oldWidget}}this.session._emit("changeFold",{data:{start:{row:e.row}}}),this.$updateRows()},this.getWidgetsAtRow=function(e){var t=this.session.lineWidgets,n=t&&t[e],r=[];while(n)r.push(n),n=n.$oldWidget;return r},this.onWidgetChanged=function(e){this.session._changedWidgets.push(e),this.editor&&this.editor.renderer.updateFull()},this.measureWidgets=function(e,t){var n=this.session._changedWidgets,r=t.layerConfig;if(!n||!n.length)return;var i=Infinity;for(var s=0;s<n.length;s++){var o=n[s];if(!o||!o.el)continue;if(o.session!=this.session)continue;if(!o._inDocument){if(this.session.lineWidgets[o.row]!=o)continue;o._inDocument=!0,t.container.appendChild(o.el)}o.h=o.el.offsetHeight,o.fixedWidth||(o.w=o.el.offsetWidth,o.screenWidth=Math.ceil(o.w/r.characterWidth));var u=o.h/r.lineHeight;o.coverLine&&(u-=this.session.getRowLineCount(o.row),u<0&&(u=0)),o.rowCount!=u&&(o.rowCount=u,o.row<i&&(i=o.row))}i!=Infinity&&(this.session._emit("changeFold",{data:{start:{row:i}}}),this.session.lineWidgetWidth=null),this.session._changedWidgets=[]},this.renderWidgets=function(e,t){var n=t.layerConfig,r=this.session.lineWidgets;if(!r)return;var i=Math.min(this.firstRow,n.firstRow),s=Math.max(this.lastRow,n.lastRow,r.length);while(i>0&&!r[i])i--;this.firstRow=n.firstRow,this.lastRow=n.lastRow,t.$cursorLayer.config=n;for(var o=i;o<=s;o++){var u=r[o];if(!u||!u.el)continue;if(u.hidden){u.el.style.top=-100-(u.pixelHeight||0)+"px";continue}u._inDocument||(u._inDocument=!0,t.container.appendChild(u.el));var a=t.$cursorLayer.getPixelPosition({row:o,column:0},!0).top;u.coverLine||(a+=n.lineHeight*this.session.getRowLineCount(u.row)),u.el.style.top=a-n.offset+"px";var f=u.coverGutter?0:t.gutterWidth;u.fixedWidth||(f-=t.scrollLeft),u.el.style.left=f+"px",u.fullWidth&&u.screenWidth&&(u.el.style.minWidth=n.width+2*n.padding+"px"),u.fixedWidth?u.el.style.right=t.scrollBar.getWidth()+"px":u.el.style.right=""}}}).call(o.prototype),t.LineWidgets=o}),define("ace/ext/error_marker",["require","exports","module","ace/line_widgets","ace/lib/dom","ace/range"],function(e,t,n){"use strict";function o(e,t,n){var r=0,i=e.length-1;while(r<=i){var s=r+i>>1,o=n(t,e[s]);if(o>0)r=s+1;else{if(!(o<0))return s;i=s-1}}return-(r+1)}function u(e,t,n){var r=e.getAnnotations().sort(s.comparePoints);if(!r.length)return;var i=o(r,{row:t,column:-1},s.comparePoints);i<0&&(i=-i-1),i>=r.length?i=n>0?0:r.length-1:i===0&&n<0&&(i=r.length-1);var u=r[i];if(!u||!n)return;if(u.row===t){do u=r[i+=n];while(u&&u.row===t);if(!u)return r.slice()}var a=[];t=u.row;do a[n<0?"unshift":"push"](u),u=r[i+=n];while(u&&u.row==t);return a.length&&a}var r=e("../line_widgets").LineWidgets,i=e("../lib/dom"),s=e("../range").Range;t.showErrorMarker=function(e,t){var n=e.session;n.widgetManager||(n.widgetManager=new r(n),n.widgetManager.attach(e));var s=e.getCursorPosition(),o=s.row,a=n.widgetManager.getWidgetsAtRow(o).filter(function(e){return e.type=="errorMarker"})[0];a?a.destroy():o-=t;var f=u(n,o,t),l;if(f){var c=f[0];s.column=(c.pos&&typeof c.column!="number"?c.pos.sc:c.column)||0,s.row=c.row,l=e.renderer.$gutterLayer.$annotations[s.row]}else{if(a)return;l={text:["Looks good!"],className:"ace_ok"}}e.session.unfold(s.row),e.selection.moveToPosition(s);var h={row:s.row,fixedWidth:!0,coverGutter:!0,el:i.createElement("div"),type:"errorMarker"},p=h.el.appendChild(i.createElement("div")),d=h.el.appendChild(i.createElement("div"));d.className="error_widget_arrow "+l.className;var v=e.renderer.$cursorLayer.getPixelPosition(s).left;d.style.left=v+e.renderer.gutterWidth-5+"px",h.el.className="error_widget_wrapper",p.className="error_widget "+l.className,p.innerHTML=l.text.join("<br>"),p.appendChild(i.createElement("div"));var m=function(e,t,n){if(t===0&&(n==="esc"||n==="return"))return h.destroy(),{command:"null"}};h.destroy=function(){if(e.$mouseHandler.isMousePressed)return;e.keyBinding.removeKeyboardHandler(m),n.widgetManager.removeLineWidget(h),e.off("changeSelection",h.destroy),e.off("changeSession",h.destroy),e.off("mouseup",h.destroy),e.off("change",h.destroy)},e.keyBinding.addKeyboardHandler(m),e.on("changeSelection",h.destroy),e.on("changeSession",h.destroy),e.on("mouseup",h.destroy),e.on("change",h.destroy),e.session.widgetManager.addLineWidget(h),h.el.onmousedown=e.focus.bind(e),e.renderer.scrollCursorIntoView(null,.5,{bottom:h.el.offsetHeight})},i.importCssString("    .error_widget_wrapper {        background: inherit;        color: inherit;        border:none    }    .error_widget {        border-top: solid 2px;        border-bottom: solid 2px;        margin: 5px 0;        padding: 10px 40px;        white-space: pre-wrap;    }    .error_widget.ace_error, .error_widget_arrow.ace_error{        border-color: #ff5a5a    }    .error_widget.ace_warning, .error_widget_arrow.ace_warning{        border-color: #F1D817    }    .error_widget.ace_info, .error_widget_arrow.ace_info{        border-color: #5a5a5a    }    .error_widget.ace_ok, .error_widget_arrow.ace_ok{        border-color: #5aaa5a    }    .error_widget_arrow {        position: absolute;        border: solid 5px;        border-top-color: transparent!important;        border-right-color: transparent!important;        border-left-color: transparent!important;        top: -5px;    }","")}),define("ace/ace",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/dom","ace/lib/event","ace/editor","ace/edit_session","ace/undomanager","ace/virtual_renderer","ace/worker/worker_client","ace/keyboard/hash_handler","ace/placeholder","ace/multi_select","ace/mode/folding/fold_mode","ace/theme/textmate","ace/ext/error_marker","ace/config"],function(e,t,n){"use strict";e("./lib/fixoldbrowsers");var r=e("./lib/dom"),i=e("./lib/event"),s=e("./editor").Editor,o=e("./edit_session").EditSession,u=e("./undomanager").UndoManager,a=e("./virtual_renderer").VirtualRenderer;e("./worker/worker_client"),e("./keyboard/hash_handler"),e("./placeholder"),e("./multi_select"),e("./mode/folding/fold_mode"),e("./theme/textmate"),e("./ext/error_marker"),t.config=e("./config"),t.require=e,typeof define=="function"&&(t.define=define),t.edit=function(e){if(typeof e=="string"){var n=e;e=document.getElementById(n);if(!e)throw new Error("ace.edit can't find div #"+n)}if(e&&e.env&&e.env.editor instanceof s)return e.env.editor;var o="";if(e&&/input|textarea/i.test(e.tagName)){var u=e;o=u.value,e=r.createElement("pre"),u.parentNode.replaceChild(e,u)}else e&&(o=r.getInnerText(e),e.innerHTML="");var f=t.createEditSession(o),l=new s(new a(e));l.setSession(f);var c={document:f,editor:l,onResize:l.resize.bind(l,null)};return u&&(c.textarea=u),i.addListener(window,"resize",c.onResize),l.on("destroy",function(){i.removeListener(window,"resize",c.onResize),c.editor.container.env=null}),l.container.env=l.env=c,l},t.createEditSession=function(e,t){var n=new o(e,t);return n.setUndoManager(new u),n},t.EditSession=o,t.UndoManager=u,t.version="1.2.7"});
            (function() {
                window.require(["ace/ace"], function(a) {
                    if (a) {
                        a.config.init(true);
                        a.define = window.define;
                    }
                    if (!window.ace)
                        window.ace = a;
                    for (var key in a) if (a.hasOwnProperty(key))
                        window.ace[key] = a[key];
                });
            })();
        
};


modules['helpcenter/primary/plugins/froala/font_size'] = function(require, exports, module, modules) {
/*!
 * froala_editor v2.3.4 (https://www.froala.com/wysiwyg-editor)
 * License https://froala.com/wysiwyg-editor/terms/
 * Copyright 2014-2016 Froala Labs
 */

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof module === 'object' && module.exports) {
        // Node/CommonJS
        module.exports = function( root, jQuery ) {
            if ( jQuery === undefined ) {
                // require('jQuery') returns a factory that requires window to
                // build a jQuery instance, we normalize how we use modules
                // that require this pattern but the window provided is a noop
                // if it's defined (how jquery works)
                if ( typeof window !== 'undefined' ) {
                    jQuery = require('jquery');
                }
                else {
                    jQuery = require('jquery')(root);
                }
            }
            factory(jQuery);
            return jQuery;
        };
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {

  'use strict';

  $.extend($.FE.DEFAULTS, {
    fontSize: ['8', '9', '10', '11', '12', '14', '18', '24', '30', '36', '48', '60', '72', '96'],
    fontSizeSelection: false,
    fontSizeDefaultSelection: '12'
  });

  $.FE.PLUGINS.fontSize = function (editor) {
    function apply (val) {
      editor.format.applyStyle('font-size', val);
    }

    function refreshOnShow($btn, $dropdown) {
      var val = $(editor.selection.element()).css('font-size');
      $dropdown.find('.fr-command.fr-active').removeClass('fr-active');
      $dropdown.find('.fr-command[data-param1="' + val + '"]').addClass('fr-active');

      var $list = $dropdown.find('.fr-dropdown-list');
      var $active = $dropdown.find('.fr-active').parent();
      if ($active.length) {
        $list.parent().scrollTop($active.offset().top - $list.offset().top - ($list.parent().outerHeight() / 2 - $active.outerHeight() / 2));
      }
      else {
        $list.parent().scrollTop(0);
      }
    }

    function refresh ($btn) {
      if (editor.opts.fontSizeSelection) {
        var val = editor.helpers.getPX($(editor.selection.element()).css('font-size'));
        $btn.find('> span').text(val);
      }
    }

    return {
      apply: apply,
      refreshOnShow: refreshOnShow,
      refresh: refresh
    }
  }

  // Register the font size command.
  $.FE.RegisterCommand('fontSize', {
    type: 'dropdown',
    title: 'Font Size',
    displaySelection: function (editor) {
      return editor.opts.fontSizeSelection;
    },
    displaySelectionWidth: 30,
    defaultSelection: function (editor) {
      return editor.opts.fontSizeDefaultSelection;
    },
    html: function () {
      var c = '<ul class="fr-dropdown-list">';
      var options =  this.opts.fontSize;
      for (var i = 0; i < options.length; i++) {
        var val = options[i];
        c += '<li><a class="fr-command" data-cmd="fontSize" data-param1="' + val + 'px" title="' + val + '">' + val + '</a></li>';
      }
      c += '</ul>';

      return c;
    },
    callback: function (cmd, val) {
      this.fontSize.apply(val);
    },
    refresh: function ($btn) {
      this.fontSize.refresh($btn);
    },
    refreshOnShow: function ($btn, $dropdown) {
      this.fontSize.refreshOnShow($btn, $dropdown);
    },
    plugin: 'fontSize'
  })

  // Add the font size icon.
  $.FE.DefineIcon('fontSize', {
    NAME: 'text-height'
  });

}));

};


modules['helpcenter/primary/views/manage/home'] = function(require, exports, module, modules) {
var Sortable = require('helpcenter/primary/plugins/sortable');

module.exports = Backbone.View.extend({
	initialize: function(options) {
		var categories = document.getElementById('categorylist-view');

		if (categories && categories.getAttribute('data-sortable')) {
			$(window).unload(function() {
				localStorage.removeItem('category-list--sort-order');
			});

			Sortable.create(categories, {
				group: "category-list--sort-order",
				store: {
					get: function(sortable) {
						var order = localStorage.getItem(sortable.options.group.name);
						return order ? order.split('|') : [];
					},
					set: function(sortable) {
						var categoryIds = sortable.toArray();
						var data = {
							'category_ids': categoryIds.join(','),
							'brand_id': _Payload.brand_id
						};
						$.ajax({
							type: 'PUT',
							url: '/api/v1/categories/reorder',
							headers: {'X-CSRF-Token': localStorage.getItem('ko:core:csrf') ? localStorage.getItem('ko:core:csrf').replace(/\"/g, '') : '', 'Content-Type': 'application/json'},
							data: JSON.stringify(data),
							success: function(res) {
								// store order to localStorage
								var order = sortable.toArray();
								localStorage.setItem(sortable.options.group.name, order.join('|'));
							},
							error: function(response) {
								// display error
								var alertTpl = _.template('<div class="alert-box--float is-error js-alert-box"><%= message %></div>');
								var res = response.responseJSON;
								$('body').append(alertTpl({message: res.errors && res.errors.length > 0 && res.errors[0].message ? res.errors[0].message : "Error while updating"}));
								setTimeout(function() {
									$('.js-alert-box').addClass('a-alert-box--out');
									setTimeout(function() {
										$('.js-alert-box').remove();
									}, 200);
								}, 3000);

								// reset ordering
								var el = localStorage.getItem(sortable.options.group.name);
								var order = el.split('|');
								sortable.sort(order);
							}
						});
					}
				}
			});
		}
	}
});

};


modules['helpcenter/primary/plugins/froala/code_beautifier'] = function(require, exports, module, modules) {
/*!
 * froala_editor v2.3.4 (https://www.froala.com/wysiwyg-editor)
 * License https://froala.com/wysiwyg-editor/terms/
 * Copyright 2014-2016 Froala Labs
 */

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof module === 'object' && module.exports) {
        // Node/CommonJS
        module.exports = function( root, jQuery ) {
            if ( jQuery === undefined ) {
                // require('jQuery') returns a factory that requires window to
                // build a jQuery instance, we normalize how we use modules
                // that require this pattern but the window provided is a noop
                // if it's defined (how jquery works)
                if ( typeof window !== 'undefined' ) {
                    jQuery = require('jquery');
                }
                else {
                    jQuery = require('jquery')(root);
                }
            }
            factory(jQuery);
            return jQuery;
        };
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {

  'use strict';

  $.FE.PLUGINS.codeBeautifier = function () {
    /**
     * HTML BEAUTIFIER
     *
     * LICENSE: The MIT License (MIT)
     *
     * Written by Nochum Sossonko, (nsossonko@hotmail.com)
     *
     * Based on code initially developed by: Einar Lielmanis, <einar@jsbeautifier.org>
     * http://jsbeautifier.org/
     *
     */
    /* jshint ignore:start */
    /* jscs:disable */


    var acorn = {};
    (function(exports) {
        /* jshint curly: false */
        // This section of code is taken from acorn.
        //
        // Acorn was written by Marijn Haverbeke and released under an MIT
        // license. The Unicode regexps (for identifiers and whitespace) were
        // taken from [Esprima](http://esprima.org) by Ariya Hidayat.
        //
        // Git repositories for Acorn are available at
        //
        //     http://marijnhaverbeke.nl/git/acorn
        //     https://github.com/marijnh/acorn.git

        // ## Character categories

        // Big ugly regular expressions that match characters in the
        // whitespace, identifier, and identifier-start categories. These
        // are only applied when a character is found to actually have a
        // code point above 128.

        var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/; // jshint ignore:line
        var nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc";
        var nonASCIIidentifierChars = "\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u0620-\u0649\u0672-\u06d3\u06e7-\u06e8\u06fb-\u06fc\u0730-\u074a\u0800-\u0814\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0840-\u0857\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962-\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09d7\u09df-\u09e0\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5f-\u0b60\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2-\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d46-\u0d48\u0d57\u0d62-\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e34-\u0e3a\u0e40-\u0e45\u0e50-\u0e59\u0eb4-\u0eb9\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f41-\u0f47\u0f71-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1029\u1040-\u1049\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u170e-\u1710\u1720-\u1730\u1740-\u1750\u1772\u1773\u1780-\u17b2\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1920-\u192b\u1930-\u193b\u1951-\u196d\u19b0-\u19c0\u19c8-\u19c9\u19d0-\u19d9\u1a00-\u1a15\u1a20-\u1a53\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b46-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1bb0-\u1bb9\u1be6-\u1bf3\u1c00-\u1c22\u1c40-\u1c49\u1c5b-\u1c7d\u1cd0-\u1cd2\u1d00-\u1dbe\u1e01-\u1f15\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2d81-\u2d96\u2de0-\u2dff\u3021-\u3028\u3099\u309a\ua640-\ua66d\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua7f8-\ua800\ua806\ua80b\ua823-\ua827\ua880-\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8f3-\ua8f7\ua900-\ua909\ua926-\ua92d\ua930-\ua945\ua980-\ua983\ua9b3-\ua9c0\uaa00-\uaa27\uaa40-\uaa41\uaa4c-\uaa4d\uaa50-\uaa59\uaa7b\uaae0-\uaae9\uaaf2-\uaaf3\uabc0-\uabe1\uabec\uabed\uabf0-\uabf9\ufb20-\ufb28\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f";
        var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
        var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");

        // Whether a single character denotes a newline.

        exports.newline = /[\n\r\u2028\u2029]/;

        // Matches a whole line break (where CRLF is considered a single
        // line break). Used to count lines.

        // in javascript, these two differ
        // in python they are the same, different methods are called on them
        exports.lineBreak = new RegExp('\r\n|' + exports.newline.source);
        exports.allLineBreaks = new RegExp(exports.lineBreak.source, 'g');


        // Test whether a given character code starts an identifier.

        exports.isIdentifierStart = function(code) {
            // permit $ (36) and @ (64). @ is used in ES7 decorators.
            if (code < 65) return code === 36 || code === 64;
            // 65 through 91 are uppercase letters.
            if (code < 91) return true;
            // permit _ (95).
            if (code < 97) return code === 95;
            // 97 through 123 are lowercase letters.
            if (code < 123) return true;
            return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code));
        };

        // Test whether a given character is part of an identifier.

        exports.isIdentifierChar = function(code) {
            if (code < 48) return code === 36;
            if (code < 58) return true;
            if (code < 65) return false;
            if (code < 91) return true;
            if (code < 97) return code === 95;
            if (code < 123) return true;
            return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));
        };
    })(acorn);

    function run (html_source, options) {
      function ltrim(s) {
        return s.replace(/^\s+/g, '');
      }

      function rtrim(s) {
        return s.replace(/\s+$/g, '');
      }

      var multi_parser;
      var indent_inner_html;
      var indent_size;
      var indent_character;
      var wrap_line_length;
      var brace_style;
      var unformatted;
      var preserve_newlines;
      var max_preserve_newlines;
      var indent_handlebars;
      var wrap_attributes;
      var wrap_attributes_indent_size;
      var end_with_newline;
      var extra_liners;

      options = options || {};

      // backwards compatibility to 1.3.4
      if ((options.wrap_line_length === undefined || parseInt(options.wrap_line_length, 10) === 0) &&
        (options.max_char !== undefined && parseInt(options.max_char, 10) !== 0)) {
        options.wrap_line_length = options.max_char;
      }

      indent_inner_html = (options.indent_inner_html === undefined) ? false : options.indent_inner_html;
      indent_size = (options.indent_size === undefined) ? 4 : parseInt(options.indent_size, 10);
      indent_character = (options.indent_char === undefined) ? ' ' : options.indent_char;
      brace_style = (options.brace_style === undefined) ? 'collapse' : options.brace_style;
      wrap_line_length = parseInt(options.wrap_line_length, 10) === 0 ? 32786 : parseInt(options.wrap_line_length || 250, 10);
      unformatted = options.unformatted || ['a', 'span', 'img', 'bdo', 'em', 'strong', 'dfn', 'code', 'samp', 'kbd',
        'var', 'cite', 'abbr', 'acronym', 'q', 'sub', 'sup', 'tt', 'i', 'b', 'big', 'small', 'u', 's', 'strike',
        'font', 'ins', 'del', 'address', 'pre'
      ];
      preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines;
      max_preserve_newlines = preserve_newlines ?
        (isNaN(parseInt(options.max_preserve_newlines, 10)) ? 32786 : parseInt(options.max_preserve_newlines, 10)) : 0;
      indent_handlebars = (options.indent_handlebars === undefined) ? false : options.indent_handlebars;
      wrap_attributes = (options.wrap_attributes === undefined) ? 'auto' : options.wrap_attributes;
      wrap_attributes_indent_size = (options.wrap_attributes_indent_size === undefined) ? indent_size : parseInt(options.wrap_attributes_indent_size, 10) || indent_size;
      end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
      extra_liners = Array.isArray(options.extra_liners) ?
        options.extra_liners.concat() : (typeof options.extra_liners == 'string') ?
        options.extra_liners.split(',') : 'head,body,/html'.split(',');

      if (options.indent_with_tabs) {
        indent_character = '\t';
        indent_size = 1;
      }

      function Parser() {

        this.pos = 0; //Parser position
        this.token = '';
        this.current_mode = 'CONTENT'; //reflects the current Parser mode: TAG/CONTENT
        this.tags = { //An object to hold tags, their position, and their parent-tags, initiated with default values
          parent: 'parent1',
          parentcount: 1,
          parent1: ''
        };
        this.tag_type = '';
        this.token_text = this.last_token = this.last_text = this.token_type = '';
        this.newlines = 0;
        this.indent_content = indent_inner_html;

        this.Utils = { //Uilities made available to the various functions
          whitespace: "\n\r\t ".split(''),
          single_token: 'br,input,link,meta,source,!doctype,basefont,base,area,hr,wbr,param,img,isindex,embed'.split(','), //all the single tags for HTML
          extra_liners: extra_liners, //for tags that need a line of whitespace before them
          in_array: function(what, arr) {
            for (var i = 0; i < arr.length; i++) {
              if (what == arr[i]) {
                return true;
              }
            }
            return false;
          }
        };

        // Return true if the given text is composed entirely of whitespace.
        this.is_whitespace = function(text) {
          for (var n = 0; n < text.length; text++) {
            if (!this.Utils.in_array(text.charAt(n), this.Utils.whitespace)) {
              return false;
            }
          }
          return true;
        };

        this.traverse_whitespace = function() {
          var input_char = '';

          input_char = this.input.charAt(this.pos);
          if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
            this.newlines = 0;
            while (this.Utils.in_array(input_char, this.Utils.whitespace)) {
              if (preserve_newlines && input_char == '\n' && this.newlines <= max_preserve_newlines) {
                this.newlines += 1;
              }

              this.pos++;
              input_char = this.input.charAt(this.pos);
            }
            return true;
          }
          return false;
        };

        // Append a space to the given content (string array) or, if we are
        // at the wrap_line_length, append a newline/indentation.
        this.space_or_wrap = function(content) {
          if (this.line_char_count >= this.wrap_line_length) { //insert a line when the wrap_line_length is reached
            this.print_newline(false, content);
            this.print_indentation(content);
          } else {
            this.line_char_count++;
            content.push(' ');
          }
        };

        this.get_content = function() { //function to capture regular content between tags
          var input_char = '',
              content = [];

          while (this.input.charAt(this.pos) != '<') {
            if (this.pos >= this.input.length) {
              return content.length ? content.join('') : ['', 'TK_EOF'];
            }

            if (this.traverse_whitespace()) {
              this.space_or_wrap(content);
              continue;
            }

            if (indent_handlebars) {
              // Handlebars parsing is complicated.
              // {{#foo}} and {{/foo}} are formatted tags.
              // {{something}} should get treated as content, except:
              // {{else}} specifically behaves like {{#if}} and {{/if}}
              var peek3 = this.input.substr(this.pos, 3);
              if (peek3 == '{{#' || peek3 == '{{/') {
                // These are tags and not content.
                break;
              } else if (peek3 == '{{!') {
                return [this.get_tag(), 'TK_TAG_HANDLEBARS_COMMENT'];
              } else if (this.input.substr(this.pos, 2) == '{{') {
                if (this.get_tag(true) == '{{else}}') {
                  break;
                }
              }
            }

            input_char = this.input.charAt(this.pos);
            this.pos++;
            this.line_char_count++;
            content.push(input_char); //letter at-a-time (or string) inserted to an array
          }
          return content.length ? content.join('') : '';
        };

        this.get_contents_to = function(name) { //get the full content of a script or style to pass to js_beautify
          if (this.pos == this.input.length) {
            return ['', 'TK_EOF'];
          }
          var content = '';
          var reg_match = new RegExp('</' + name + '\\s*>', 'igm');
          reg_match.lastIndex = this.pos;
          var reg_array = reg_match.exec(this.input);
          var end_script = reg_array ? reg_array.index : this.input.length; //absolute end of script
          if (this.pos < end_script) { //get everything in between the script tags
            content = this.input.substring(this.pos, end_script);
            this.pos = end_script;
          }
          return content;
        };

        this.record_tag = function(tag) { //function to record a tag and its parent in this.tags Object
          if (this.tags[tag + 'count']) { //check for the existence of this tag type
            this.tags[tag + 'count']++;
            this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
          } else { //otherwise initialize this tag type
            this.tags[tag + 'count'] = 1;
            this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
          }
          this.tags[tag + this.tags[tag + 'count'] + 'parent'] = this.tags.parent; //set the parent (i.e. in the case of a div this.tags.div1parent)
          this.tags.parent = tag + this.tags[tag + 'count']; //and make this the current parent (i.e. in the case of a div 'div1')
        };

        this.retrieve_tag = function(tag) { //function to retrieve the opening tag to the corresponding closer
          if (this.tags[tag + 'count']) { //if the openener is not in the Object we ignore it
            var temp_parent = this.tags.parent; //check to see if it's a closable tag.
            while (temp_parent) { //till we reach '' (the initial value);
              if (tag + this.tags[tag + 'count'] == temp_parent) { //if this is it use it
                break;
              }
              temp_parent = this.tags[temp_parent + 'parent']; //otherwise keep on climbing up the DOM Tree
            }
            if (temp_parent) { //if we caught something
              this.indent_level = this.tags[tag + this.tags[tag + 'count']]; //set the indent_level accordingly
              this.tags.parent = this.tags[temp_parent + 'parent']; //and set the current parent
            }
            delete this.tags[tag + this.tags[tag + 'count'] + 'parent']; //delete the closed tags parent reference...
            delete this.tags[tag + this.tags[tag + 'count']]; //...and the tag itself
            if (this.tags[tag + 'count'] == 1) {
              delete this.tags[tag + 'count'];
            } else {
              this.tags[tag + 'count']--;
            }
          }
        };

        this.indent_to_tag = function(tag) {
          // Match the indentation level to the last use of this tag, but don't remove it.
          if (!this.tags[tag + 'count']) {
            return;
          }
          var temp_parent = this.tags.parent;
          while (temp_parent) {
            if (tag + this.tags[tag + 'count'] == temp_parent) {
              break;
            }
            temp_parent = this.tags[temp_parent + 'parent'];
          }
          if (temp_parent) {
            this.indent_level = this.tags[tag + this.tags[tag + 'count']];
          }
        };

        this.get_tag = function(peek) { //function to get a full tag and parse its type
          var input_char = '',
            content = [],
            comment = '',
            space = false,
            first_attr = true,
            tag_start, tag_end,
            tag_start_char,
            orig_pos = this.pos,
            orig_line_char_count = this.line_char_count;

          peek = peek !== undefined ? peek : false;

          do {
            if (this.pos >= this.input.length) {
              if (peek) {
                this.pos = orig_pos;
                this.line_char_count = orig_line_char_count;
              }
              return content.length ? content.join('') : ['', 'TK_EOF'];
            }

            input_char = this.input.charAt(this.pos);
            this.pos++;

            if (this.Utils.in_array(input_char, this.Utils.whitespace)) { //don't want to insert unnecessary space
              space = true;
              continue;
            }

            if (input_char == "'" || input_char == '"') {
              input_char += this.get_unformatted(input_char);
              space = true;

            }

            if (input_char == '=') { //no space before =
              space = false;
            }

            if (content.length && content[content.length - 1] != '=' && input_char != '>' && space) {
              //no space after = or before >
              this.space_or_wrap(content);
              space = false;
              if (!first_attr && wrap_attributes == 'force' && input_char != '/') {
                this.print_newline(true, content);
                this.print_indentation(content);
                for (var count = 0; count < wrap_attributes_indent_size; count++) {
                  content.push(indent_character);
                }
              }
              for (var i = 0; i < content.length; i++) {
                if (content[i] == ' ') {
                  first_attr = false;
                  break;
                }
              }
            }

            if (indent_handlebars && tag_start_char == '<') {
              // When inside an angle-bracket tag, put spaces around
              // handlebars not inside of strings.
              if ((input_char + this.input.charAt(this.pos)) == '{{') {
                input_char += this.get_unformatted('}}');
                if (content.length && content[content.length - 1] != ' ' && content[content.length - 1] != '<') {
                  input_char = ' ' + input_char;
                }
                space = true;
              }
            }

            if (input_char == '<' && !tag_start_char) {
              tag_start = this.pos - 1;
              tag_start_char = '<';
            }

            if (indent_handlebars && !tag_start_char) {
              if (content.length >= 2 && content[content.length - 1] == '{' && content[content.length - 2] == '{') {
                if (input_char == '#' || input_char == '/' || input_char == '!') {
                  tag_start = this.pos - 3;
                } else {
                  tag_start = this.pos - 2;
                }
                tag_start_char = '{';
              }
            }

            this.line_char_count++;
            content.push(input_char); //inserts character at-a-time (or string)

            if (content[1] && (content[1] == '!' || content[1] == '?' || content[1] == '%')) { //if we're in a comment, do something special
              // We treat all comments as literals, even more than preformatted tags
              // we just look for the appropriate close tag
              content = [this.get_comment(tag_start)];
              break;
            }

            if (indent_handlebars && content[1] && content[1] == '{' && content[2] && content[2] == '!') { //if we're in a comment, do something special
              // We treat all comments as literals, even more than preformatted tags
              // we just look for the appropriate close tag
              content = [this.get_comment(tag_start)];
              break;
            }

            if (indent_handlebars && tag_start_char == '{' && content.length > 2 && content[content.length - 2] == '}' && content[content.length - 1] == '}') {
              break;
            }
          } while (input_char != '>');

          var tag_complete = content.join('');
          var tag_index;
          var tag_offset;

          if (tag_complete.indexOf(' ') != -1) { //if there's whitespace, thats where the tag name ends
            tag_index = tag_complete.indexOf(' ');
          } else if (tag_complete[0] == '{') {
            tag_index = tag_complete.indexOf('}');
          } else { //otherwise go with the tag ending
            tag_index = tag_complete.indexOf('>');
          }
          if (tag_complete[0] == '<' || !indent_handlebars) {
            tag_offset = 1;
          } else {
            tag_offset = tag_complete[2] == '#' ? 3 : 2;
          }
          var tag_check = tag_complete.substring(tag_offset, tag_index).toLowerCase();
          if (tag_complete.charAt(tag_complete.length - 2) == '/' ||
            this.Utils.in_array(tag_check, this.Utils.single_token)) { //if this tag name is a single tag type (either in the list or has a closing /)
            if (!peek) {
              this.tag_type = 'SINGLE';
            }
          } else if (indent_handlebars && tag_complete[0] == '{' && tag_check == 'else') {
            if (!peek) {
              this.indent_to_tag('if');
              this.tag_type = 'HANDLEBARS_ELSE';
              this.indent_content = true;
              this.traverse_whitespace();
            }
          } else if (this.is_unformatted(tag_check, unformatted)) { // do not reformat the "unformatted" tags
            comment = this.get_unformatted('</' + tag_check + '>', tag_complete); //...delegate to get_unformatted function
            content.push(comment);
            tag_end = this.pos - 1;
            this.tag_type = 'SINGLE';
          } else if (tag_check == 'script' &&
            (tag_complete.search('type') == -1 ||
              (tag_complete.search('type') > -1 &&
                tag_complete.search(/\b(text|application)\/(x-)?(javascript|ecmascript|jscript|livescript)/) > -1))) {
            if (!peek) {
              this.record_tag(tag_check);
              this.tag_type = 'SCRIPT';
            }
          } else if (tag_check == 'style' &&
            (tag_complete.search('type') == -1 ||
              (tag_complete.search('type') > -1 && tag_complete.search('text/css') > -1))) {
            if (!peek) {
              this.record_tag(tag_check);
              this.tag_type = 'STYLE';
            }
          } else if (tag_check.charAt(0) == '!') { //peek for <! comment
            // for comments content is already correct.
            if (!peek) {
              this.tag_type = 'SINGLE';
              this.traverse_whitespace();
            }
          } else if (!peek) {
            if (tag_check.charAt(0) == '/') { //this tag is a double tag so check for tag-ending
              this.retrieve_tag(tag_check.substring(1)); //remove it and all ancestors
              this.tag_type = 'END';
            } else { //otherwise it's a start-tag
              this.record_tag(tag_check); //push it on the tag stack
              if (tag_check.toLowerCase() != 'html') {
                this.indent_content = true;
              }
              this.tag_type = 'START';
            }

            // Allow preserving of newlines after a start or end tag
            if (this.traverse_whitespace()) {
              this.space_or_wrap(content);
            }

            if (this.Utils.in_array(tag_check, this.Utils.extra_liners)) { //check if this double needs an extra line
              this.print_newline(false, this.output);
              if (this.output.length && this.output[this.output.length - 2] != '\n') {
                this.print_newline(true, this.output);
              }
            }
          }

          if (peek) {
            this.pos = orig_pos;
            this.line_char_count = orig_line_char_count;
          }

          return content.join(''); //returns fully formatted tag
        };

        this.get_comment = function(start_pos) { //function to return comment content in its entirety
          // this is will have very poor perf, but will work for now.
          var comment = '',
            delimiter = '>',
            matched = false;

          this.pos = start_pos;
          var input_char = this.input.charAt(this.pos);
          this.pos++;

          while (this.pos <= this.input.length) {
            comment += input_char;

            // only need to check for the delimiter if the last chars match
            if (comment[comment.length - 1] == delimiter[delimiter.length - 1] &&
              comment.indexOf(delimiter) != -1) {
              break;
            }

            // only need to search for custom delimiter for the first few characters
            if (!matched && comment.length < 10) {
              if (comment.indexOf('<![if') === 0) { //peek for <![if conditional comment
                delimiter = '<![endif]>';
                matched = true;
              } else if (comment.indexOf('<![cdata[') === 0) { //if it's a <[cdata[ comment...
                delimiter = ']]>';
                matched = true;
              } else if (comment.indexOf('<![') === 0) { // some other ![ comment? ...
                delimiter = ']>';
                matched = true;
              } else if (comment.indexOf('<!--') === 0) { // <!-- comment ...
                delimiter = '-->';
                matched = true;
              } else if (comment.indexOf('{{!') === 0) { // {{! handlebars comment
                delimiter = '}}';
                matched = true;
              } else if (comment.indexOf('<?') === 0) { // {{! handlebars comment
                delimiter = '?>';
                matched = true;
              } else if (comment.indexOf('<%') === 0) { // {{! handlebars comment
                delimiter = '%>';
                matched = true;
              }
            }

            input_char = this.input.charAt(this.pos);
            this.pos++;
          }

          return comment;
        };

        this.get_unformatted = function(delimiter, orig_tag) { //function to return unformatted content in its entirety

          if (orig_tag && orig_tag.toLowerCase().indexOf(delimiter) != -1) {
            return '';
          }
          var input_char = '';
          var content = '';
          var min_index = 0;
          var space = true;
          do {

            if (this.pos >= this.input.length) {
              return content;
            }

            input_char = this.input.charAt(this.pos);
            this.pos++;

            if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
              if (!space) {
                this.line_char_count--;
                continue;
              }
              if (input_char == '\n' || input_char == '\r') {
                content += '\n';
                /*  Don't change tab indention for unformatted blocks.  If using code for html editing, this will greatly affect <pre> tags if they are specified in the 'unformatted array'
              for (var i=0; i<this.indent_level; i++) {
                content += this.indent_string;
              }
              space = false; //...and make sure other indentation is erased
              */
                this.line_char_count = 0;
                continue;
              }
            }
            content += input_char;
            this.line_char_count++;
            space = true;

            if (indent_handlebars && input_char == '{' && content.length && content[content.length - 2] == '{') {
              // Handlebars expressions in strings should also be unformatted.
              content += this.get_unformatted('}}');
              // These expressions are opaque.  Ignore delimiters found in them.
              min_index = content.length;
            }
          } while (content.toLowerCase().indexOf(delimiter, min_index) == -1);
          return content;
        };

        this.get_token = function() { //initial handler for token-retrieval
          var token;

          if (this.last_token == 'TK_TAG_SCRIPT' || this.last_token == 'TK_TAG_STYLE') { //check if we need to format javascript
            var type = this.last_token.substr(7);
            token = this.get_contents_to(type);
            if (typeof token != 'string') {
              return token;
            }
            return [token, 'TK_' + type];
          }
          if (this.current_mode == 'CONTENT') {
            token = this.get_content();
            if (typeof token != 'string') {
              return token;
            } else {
              return [token, 'TK_CONTENT'];
            }
          }

          if (this.current_mode == 'TAG') {
            token = this.get_tag();
            if (typeof token != 'string') {
              return token;
            } else {
              var tag_name_type = 'TK_TAG_' + this.tag_type;
              return [token, tag_name_type];
            }
          }
        };

        this.get_full_indent = function(level) {
          level = this.indent_level + level || 0;
          if (level < 1) {
            return '';
          }

          return (new Array(level + 1)).join(this.indent_string);
        };

        this.is_unformatted = function(tag_check, unformatted) {
          //is this an HTML5 block-level link?
          if (!this.Utils.in_array(tag_check, unformatted)) {
            return false;
          }

          if (tag_check.toLowerCase() != 'a' || !this.Utils.in_array('a', unformatted)) {
            return true;
          }

          //at this point we have an  tag; is its first child something we want to remain
          //unformatted?
          var next_tag = this.get_tag(true /* peek. */ );

          // test next_tag to see if it is just html tag (no external content)
          var tag = (next_tag || '').match(/^\s*<\s*\/?([a-z]*)\s*[^>]*>\s*$/);

          // if next_tag comes back but is not an isolated tag, then
          // let's treat the 'a' tag as having content
          // and respect the unformatted option
          if (!tag || this.Utils.in_array(tag, unformatted)) {
            return true;
          } else {
            return false;
          }
        };

        this.printer = function(js_source, indent_character, indent_size, wrap_line_length, brace_style) { //handles input/output and some other printing functions

          this.input = js_source || ''; //gets the input for the Parser
          this.output = [];
          this.indent_character = indent_character;
          this.indent_string = '';
          this.indent_size = indent_size;
          this.brace_style = brace_style;
          this.indent_level = 0;
          this.wrap_line_length = wrap_line_length;
          this.line_char_count = 0; //count to see if wrap_line_length was exceeded

          for (var i = 0; i < this.indent_size; i++) {
            this.indent_string += this.indent_character;
          }

          this.print_newline = function(force, arr) {
            this.line_char_count = 0;
            if (!arr || !arr.length) {
              return;
            }
            if (force || (arr[arr.length - 1] != '\n')) { //we might want the extra line
              if ((arr[arr.length - 1] != '\n')) {
                arr[arr.length - 1] = rtrim(arr[arr.length - 1]);
              }
              arr.push('\n');
            }
          };

          this.print_indentation = function(arr) {
            for (var i = 0; i < this.indent_level; i++) {
              arr.push(this.indent_string);
              this.line_char_count += this.indent_string.length;
            }
          };

          this.print_token = function(text) {
            // Avoid printing initial whitespace.
            if (this.is_whitespace(text) && !this.output.length) {
              return;
            }
            if (text || text !== '') {
              if (this.output.length && this.output[this.output.length - 1] == '\n') {
                this.print_indentation(this.output);
                text = ltrim(text);
              }
            }
            this.print_token_raw(text);
          };

          this.print_token_raw = function(text) {
            // If we are going to print newlines, truncate trailing
            // whitespace, as the newlines will represent the space.
            if (this.newlines > 0) {
              text = rtrim(text);
            }

            if (text && text !== '') {
              if (text.length > 1 && text[text.length - 1] == '\n') {
                // unformatted tags can grab newlines as their last character
                this.output.push(text.slice(0, -1));
                this.print_newline(false, this.output);
              } else {
                this.output.push(text);
              }
            }

            for (var n = 0; n < this.newlines; n++) {
              this.print_newline(n > 0, this.output);
            }
            this.newlines = 0;
          };

          this.indent = function() {
            this.indent_level++;
          };

          this.unindent = function() {
            if (this.indent_level > 0) {
              this.indent_level--;
            }
          };
        };
        return this;
      }

      /*_____________________--------------------_____________________*/

      multi_parser = new Parser(); //wrapping functions Parser
      multi_parser.printer(html_source, indent_character, indent_size, wrap_line_length, brace_style); //initialize starting values

      while (true) {
        var t = multi_parser.get_token();
        multi_parser.token_text = t[0];
        multi_parser.token_type = t[1];

        if (multi_parser.token_type == 'TK_EOF') {
          break;
        }

        switch (multi_parser.token_type) {
          case 'TK_TAG_START':
            multi_parser.print_newline(false, multi_parser.output);
            multi_parser.print_token(multi_parser.token_text);
            if (multi_parser.indent_content) {
              multi_parser.indent();
              multi_parser.indent_content = false;
            }
            multi_parser.current_mode = 'CONTENT';
            break;
          case 'TK_TAG_STYLE':
          case 'TK_TAG_SCRIPT':
            multi_parser.print_newline(false, multi_parser.output);
            multi_parser.print_token(multi_parser.token_text);
            multi_parser.current_mode = 'CONTENT';
            break;
          case 'TK_TAG_END':
            // Print new line only if the tag has no content and has child
            if (multi_parser.last_token == 'TK_CONTENT' && multi_parser.last_text === '') {
              var tag_name = multi_parser.token_text.match(/\w+/)[0];
              var tag_extracted_from_last_output = null;
              if (multi_parser.output.length) {
                tag_extracted_from_last_output = multi_parser.output[multi_parser.output.length - 1].match(/(?:<|{{#)\s*(\w+)/);
              }
              if (tag_extracted_from_last_output == null ||
                (tag_extracted_from_last_output[1] != tag_name && !multi_parser.Utils.in_array(tag_extracted_from_last_output[1], unformatted))) {
                multi_parser.print_newline(false, multi_parser.output);
              }
            }
            multi_parser.print_token(multi_parser.token_text);
            multi_parser.current_mode = 'CONTENT';
            break;
          case 'TK_TAG_SINGLE':
            // Don't add a newline before elements that should remain unformatted.
            var tag_check = multi_parser.token_text.match(/^\s*<([a-z-]+)/i);
            if (!tag_check || !multi_parser.Utils.in_array(tag_check[1], unformatted)) {
              multi_parser.print_newline(false, multi_parser.output);
            }
            multi_parser.print_token(multi_parser.token_text);
            multi_parser.current_mode = 'CONTENT';
            break;
          case 'TK_TAG_HANDLEBARS_ELSE':
            multi_parser.print_token(multi_parser.token_text);
            if (multi_parser.indent_content) {
              multi_parser.indent();
              multi_parser.indent_content = false;
            }
            multi_parser.current_mode = 'CONTENT';
            break;
          case 'TK_TAG_HANDLEBARS_COMMENT':
            multi_parser.print_token(multi_parser.token_text);
            multi_parser.current_mode = 'TAG';
            break;
          case 'TK_CONTENT':
            multi_parser.print_token(multi_parser.token_text);
            multi_parser.current_mode = 'TAG';
            break;
          case 'TK_STYLE':
          case 'TK_SCRIPT':
            if (multi_parser.token_text !== '') {
              multi_parser.print_newline(false, multi_parser.output);
              var text = multi_parser.token_text;
              var _beautifier;
              var script_indent_level = 1;

              if (multi_parser.token_type == 'TK_SCRIPT') {
                _beautifier = typeof js_beautify == 'function' && js_beautify;
              } else if (multi_parser.token_type == 'TK_STYLE') {
                _beautifier = typeof css_beautify == 'function' && css_beautify;
              }

              if (options.indent_scripts == 'keep') {
                script_indent_level = 0;
              } else if (options.indent_scripts == 'separate') {
                script_indent_level = -multi_parser.indent_level;
              }

              var indentation = multi_parser.get_full_indent(script_indent_level);
              if (_beautifier) {
                // call the Beautifier if avaliable
                text = _beautifier(text.replace(/^\s*/, indentation), options);
              } else {
                // simply indent the string otherwise
                var white = text.match(/^\s*/)[0];
                var _level = white.match(/[^\n\r]*$/)[0].split(multi_parser.indent_string).length - 1;
                var reindent = multi_parser.get_full_indent(script_indent_level - _level);

                text = text.replace(/^\s*/, indentation)
                  .replace(/\r\n|\r|\n/g, '\n' + reindent)
                  .replace(/\s+$/, '');
              }
              if (text) {
                multi_parser.print_token_raw(text);
                multi_parser.print_newline(true, multi_parser.output);
              }
            }
            multi_parser.current_mode = 'TAG';
            break;
          default:
            // We should not be getting here but we don't want to drop input on the floor
            // Just output the text and move on
            if (multi_parser.token_text !== '') {
              multi_parser.print_token(multi_parser.token_text);
            }
            break;
        }
        multi_parser.last_token = multi_parser.token_type;
        multi_parser.last_text = multi_parser.token_text;
      }
      var sweet_code = multi_parser.output.join('').replace(/[\r\n\t ]+$/, '');
      if (end_with_newline) {
        sweet_code += '\n';
      }
      return sweet_code;
    }

    function css_beautify (source_text, options) {
        var NESTED_AT_RULE = {
            "@page": true,
            "@font-face": true,
            "@keyframes": true,
            // also in CONDITIONAL_GROUP_RULE below
            "@media": true,
            "@supports": true,
            "@document": true
        };
        var CONDITIONAL_GROUP_RULE = {
            "@media": true,
            "@supports": true,
            "@document": true
        };

        options = options || {};
        source_text = source_text || '';
        // HACK: newline parsing inconsistent. This brute force normalizes the input.
        source_text = source_text.replace(/\r\n|[\r\u2028\u2029]/g, '\n')

        var indentSize = options.indent_size || 4;
        var indentCharacter = options.indent_char || ' ';
        var selectorSeparatorNewline = (options.selector_separator_newline === undefined) ? true : options.selector_separator_newline;
        var end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
        var newline_between_rules = (options.newline_between_rules === undefined) ? true : options.newline_between_rules;
        var eol = options.eol ? options.eol : '\n';

        // compatibility
        if (typeof indentSize === "string") {
            indentSize = parseInt(indentSize, 10);
        }

        if(options.indent_with_tabs){
            indentCharacter = '\t';
            indentSize = 1;
        }

        eol = eol.replace(/\\r/, '\r').replace(/\\n/, '\n')


        // tokenizer
        var whiteRe = /^\s+$/;
        var wordRe = /[\w$\-_]/;

        var pos = -1,
            ch;
        var parenLevel = 0;

        function next() {
            ch = source_text.charAt(++pos);
            return ch || '';
        }

        function peek(skipWhitespace) {
            var result = '';
            var prev_pos = pos;
            if (skipWhitespace) {
                eatWhitespace();
            }
            result = source_text.charAt(pos + 1) || '';
            pos = prev_pos - 1;
            next();
            return result;
        }

        function eatString(endChars) {
            var start = pos;
            while (next()) {
                if (ch === "\\") {
                    next();
                } else if (endChars.indexOf(ch) !== -1) {
                    break;
                } else if (ch === "\n") {
                    break;
                }
            }
            return source_text.substring(start, pos + 1);
        }

        function peekString(endChar) {
            var prev_pos = pos;
            var str = eatString(endChar);
            pos = prev_pos - 1;
            next();
            return str;
        }

        function eatWhitespace() {
            var result = '';
            while (whiteRe.test(peek())) {
                next();
                result += ch;
            }
            return result;
        }

        function skipWhitespace() {
            var result = '';
            if (ch && whiteRe.test(ch)) {
                result = ch;
            }
            while (whiteRe.test(next())) {
                result += ch;
            }
            return result;
        }

        function eatComment(singleLine) {
            var start = pos;
            singleLine = peek() === "/";
            next();
            while (next()) {
                if (!singleLine && ch === "*" && peek() === "/") {
                    next();
                    break;
                } else if (singleLine && ch === "\n") {
                    return source_text.substring(start, pos);
                }
            }

            return source_text.substring(start, pos) + ch;
        }


        function lookBack(str) {
            return source_text.substring(pos - str.length, pos).toLowerCase() ===
                str;
        }

        // Nested pseudo-class if we are insideRule
        // and the next special character found opens
        // a new block
        function foundNestedPseudoClass() {
            var openParen = 0;
            for (var i = pos + 1; i < source_text.length; i++) {
                var ch = source_text.charAt(i);
                if (ch === "{") {
                    return true;
                } else if (ch === '(') {
                    // pseudoclasses can contain ()
                    openParen += 1;
                } else if (ch === ')') {
                    if (openParen == 0) {
                        return false;
                    }
                    openParen -= 1;
                } else if (ch === ";" || ch === "}") {
                    return false;
                }
            }
            return false;
        }

        // printer
        var basebaseIndentString = source_text.match(/^[\t ]*/)[0];
        var singleIndent = new Array(indentSize + 1).join(indentCharacter);
        var indentLevel = 0;
        var nestedLevel = 0;

        function indent() {
            indentLevel++;
            basebaseIndentString += singleIndent;
        }

        function outdent() {
            indentLevel--;
            basebaseIndentString = basebaseIndentString.slice(0, -indentSize);
        }

        var print = {};
        print["{"] = function(ch) {
            print.singleSpace();
            output.push(ch);
            print.newLine();
        };
        print["}"] = function(ch) {
            print.newLine();
            output.push(ch);
            print.newLine();
        };

        print._lastCharWhitespace = function() {
            return whiteRe.test(output[output.length - 1]);
        };

        print.newLine = function(keepWhitespace) {
            if (output.length) {
                if (!keepWhitespace && output[output.length - 1] !== '\n') {
                    print.trim();
                }

                output.push('\n');

                if (basebaseIndentString) {
                    output.push(basebaseIndentString);
                }
            }
        };
        print.singleSpace = function() {
            if (output.length && !print._lastCharWhitespace()) {
                output.push(' ');
            }
        };

        print.preserveSingleSpace = function() {
            if (isAfterSpace) {
                print.singleSpace();
            }
        };

        print.trim = function() {
            while (print._lastCharWhitespace()) {
                output.pop();
            }
        };


        var output = [];
        /*_____________________--------------------_____________________*/

        var insideRule = false;
        var insidePropertyValue = false;
        var enteringConditionalGroup = false;
        var top_ch = '';
        var last_top_ch = '';

        while (true) {
            var whitespace = skipWhitespace();
            var isAfterSpace = whitespace !== '';
            var isAfterNewline = whitespace.indexOf('\n') !== -1;
            last_top_ch = top_ch;
            top_ch = ch;

            if (!ch) {
                break;
            } else if (ch === '/' && peek() === '*') { /* css comment */
                var header = indentLevel === 0;

                if (isAfterNewline || header) {
                    print.newLine();
                }

                output.push(eatComment());
                print.newLine();
                if (header) {
                    print.newLine(true);
                }
            } else if (ch === '/' && peek() === '/') { // single line comment
                if (!isAfterNewline && last_top_ch !== '{' ) {
                    print.trim();
                }
                print.singleSpace();
                output.push(eatComment());
                print.newLine();
            } else if (ch === '@') {
                print.preserveSingleSpace();
                output.push(ch);

                // strip trailing space, if present, for hash property checks
                var variableOrRule = peekString(": ,;{}()[]/='\"");

                if (variableOrRule.match(/[ :]$/)) {
                    // we have a variable or pseudo-class, add it and insert one space before continuing
                    next();
                    variableOrRule = eatString(": ").replace(/\s$/, '');
                    output.push(variableOrRule);
                    print.singleSpace();
                }

                variableOrRule = variableOrRule.replace(/\s$/, '')

                // might be a nesting at-rule
                if (variableOrRule in NESTED_AT_RULE) {
                    nestedLevel += 1;
                    if (variableOrRule in CONDITIONAL_GROUP_RULE) {
                        enteringConditionalGroup = true;
                    }
                }
            } else if (ch === '#' && peek() === '{') {
              print.preserveSingleSpace();
              output.push(eatString('}'));
            } else if (ch === '{') {
                if (peek(true) === '}') {
                    eatWhitespace();
                    next();
                    print.singleSpace();
                    output.push("{}");
                    print.newLine();
                    if (newline_between_rules && indentLevel === 0) {
                        print.newLine(true);
                    }
                } else {
                    indent();
                    print["{"](ch);
                    // when entering conditional groups, only rulesets are allowed
                    if (enteringConditionalGroup) {
                        enteringConditionalGroup = false;
                        insideRule = (indentLevel > nestedLevel);
                    } else {
                        // otherwise, declarations are also allowed
                        insideRule = (indentLevel >= nestedLevel);
                    }
                }
            } else if (ch === '}') {
                outdent();
                print["}"](ch);
                insideRule = false;
                insidePropertyValue = false;
                if (nestedLevel) {
                    nestedLevel--;
                }
                if (newline_between_rules && indentLevel === 0) {
                    print.newLine(true);
                }
            } else if (ch === ":") {
                eatWhitespace();
                if ((insideRule || enteringConditionalGroup) &&
                    !(lookBack("&") || foundNestedPseudoClass())) {
                    // 'property: value' delimiter
                    // which could be in a conditional group query
                    insidePropertyValue = true;
                    output.push(':');
                    print.singleSpace();
                } else {
                    // sass/less parent reference don't use a space
                    // sass nested pseudo-class don't use a space
                    if (peek() === ":") {
                        // pseudo-element
                        next();
                        output.push("::");
                    } else {
                        // pseudo-class
                        output.push(':');
                    }
                }
            } else if (ch === '"' || ch === '\'') {
                print.preserveSingleSpace();
                output.push(eatString(ch));
            } else if (ch === ';') {
                insidePropertyValue = false;
                output.push(ch);
                print.newLine();
            } else if (ch === '(') { // may be a url
                if (lookBack("url")) {
                    output.push(ch);
                    eatWhitespace();
                    if (next()) {
                        if (ch !== ')' && ch !== '"' && ch !== '\'') {
                            output.push(eatString(')'));
                        } else {
                            pos--;
                        }
                    }
                } else {
                    parenLevel++;
                    print.preserveSingleSpace();
                    output.push(ch);
                    eatWhitespace();
                }
            } else if (ch === ')') {
                output.push(ch);
                parenLevel--;
            } else if (ch === ',') {
                output.push(ch);
                eatWhitespace();
                if (selectorSeparatorNewline && !insidePropertyValue && parenLevel < 1) {
                    print.newLine();
                } else {
                    print.singleSpace();
                }
            } else if (ch === ']') {
                output.push(ch);
            } else if (ch === '[') {
                print.preserveSingleSpace();
                output.push(ch);
            } else if (ch === '=') { // no whitespace before or after
                eatWhitespace()
                ch = '=';
                output.push(ch);
            } else {
                print.preserveSingleSpace();
                output.push(ch);
            }
        }


        var sweetCode = '';
        if (basebaseIndentString) {
            sweetCode += basebaseIndentString;
        }

        sweetCode += output.join('').replace(/[\r\n\t ]+$/, '');

        // establish end_with_newline
        if (end_with_newline) {
            sweetCode += '\n';
        }

        if (eol != '\n') {
            sweetCode = sweetCode.replace(/[\n]/g, eol);
        }

        return sweetCode;
    }

    function in_array(what, arr) {
        for (var i = 0; i < arr.length; i += 1) {
            if (arr[i] === what) {
                return true;
            }
        }
        return false;
    }

    function trim(s) {
        return s.replace(/^\s+|\s+$/g, '');
    }

    function ltrim(s) {
        return s.replace(/^\s+/g, '');
    }

    function rtrim(s) {
        return s.replace(/\s+$/g, '');
    }

    function js_beautify(js_source_text, options) {
        "use strict";
        var beautifier = new Beautifier(js_source_text, options);
        return beautifier.beautify();
    }

    var MODE = {
            BlockStatement: 'BlockStatement', // 'BLOCK'
            Statement: 'Statement', // 'STATEMENT'
            ObjectLiteral: 'ObjectLiteral', // 'OBJECT',
            ArrayLiteral: 'ArrayLiteral', //'[EXPRESSION]',
            ForInitializer: 'ForInitializer', //'(FOR-EXPRESSION)',
            Conditional: 'Conditional', //'(COND-EXPRESSION)',
            Expression: 'Expression' //'(EXPRESSION)'
        };

    function Beautifier(js_source_text, options) {
        "use strict";
        var output
        var tokens = [], token_pos;
        var Tokenizer;
        var current_token;
        var last_type, last_last_text, indent_string;
        var flags, previous_flags, flag_store;
        var prefix;

        var handlers, opt;
        var baseIndentString = '';

        handlers = {
            'TK_START_EXPR': handle_start_expr,
            'TK_END_EXPR': handle_end_expr,
            'TK_START_BLOCK': handle_start_block,
            'TK_END_BLOCK': handle_end_block,
            'TK_WORD': handle_word,
            'TK_RESERVED': handle_word,
            'TK_SEMICOLON': handle_semicolon,
            'TK_STRING': handle_string,
            'TK_EQUALS': handle_equals,
            'TK_OPERATOR': handle_operator,
            'TK_COMMA': handle_comma,
            'TK_BLOCK_COMMENT': handle_block_comment,
            'TK_COMMENT': handle_comment,
            'TK_DOT': handle_dot,
            'TK_UNKNOWN': handle_unknown,
            'TK_EOF': handle_eof
        };

        function create_flags(flags_base, mode) {
            var next_indent_level = 0;
            if (flags_base) {
                next_indent_level = flags_base.indentation_level;
                if (!output.just_added_newline() &&
                    flags_base.line_indent_level > next_indent_level) {
                    next_indent_level = flags_base.line_indent_level;
                }
            }

            var next_flags = {
                mode: mode,
                parent: flags_base,
                last_text: flags_base ? flags_base.last_text : '', // last token text
                last_word: flags_base ? flags_base.last_word : '', // last 'TK_WORD' passed
                declaration_statement: false,
                declaration_assignment: false,
                multiline_frame: false,
                if_block: false,
                else_block: false,
                do_block: false,
                do_while: false,
                in_case_statement: false, // switch(..){ INSIDE HERE }
                in_case: false, // we're on the exact line with "case 0:"
                case_body: false, // the indented case-action block
                indentation_level: next_indent_level,
                line_indent_level: flags_base ? flags_base.line_indent_level : next_indent_level,
                start_line_index: output.get_line_number(),
                ternary_depth: 0
            };
            return next_flags;
        }

        // Some interpreters have unexpected results with foo = baz || bar;
        options = options ? options : {};
        opt = {};

        // compatibility
        if (options.braces_on_own_line !== undefined) { //graceful handling of deprecated option
            opt.brace_style = options.braces_on_own_line ? "expand" : "collapse";
        }
        opt.brace_style = options.brace_style ? options.brace_style : (opt.brace_style ? opt.brace_style : "collapse");

        // graceful handling of deprecated option
        if (opt.brace_style === "expand-strict") {
            opt.brace_style = "expand";
        }


        opt.indent_size = options.indent_size ? parseInt(options.indent_size, 10) : 4;
        opt.indent_char = options.indent_char ? options.indent_char : ' ';
        opt.eol = options.eol ? options.eol : '\n';
        opt.preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines;
        opt.break_chained_methods = (options.break_chained_methods === undefined) ? false : options.break_chained_methods;
        opt.max_preserve_newlines = (options.max_preserve_newlines === undefined) ? 0 : parseInt(options.max_preserve_newlines, 10);
        opt.space_in_paren = (options.space_in_paren === undefined) ? false : options.space_in_paren;
        opt.space_in_empty_paren = (options.space_in_empty_paren === undefined) ? false : options.space_in_empty_paren;
        opt.jslint_happy = (options.jslint_happy === undefined) ? false : options.jslint_happy;
        opt.space_after_anon_function = (options.space_after_anon_function === undefined) ? false : options.space_after_anon_function;
        opt.keep_array_indentation = (options.keep_array_indentation === undefined) ? false : options.keep_array_indentation;
        opt.space_before_conditional = (options.space_before_conditional === undefined) ? true : options.space_before_conditional;
        opt.unescape_strings = (options.unescape_strings === undefined) ? false : options.unescape_strings;
        opt.wrap_line_length = (options.wrap_line_length === undefined) ? 0 : parseInt(options.wrap_line_length, 10);
        opt.e4x = (options.e4x === undefined) ? false : options.e4x;
        opt.end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
        opt.comma_first = (options.comma_first === undefined) ? false : options.comma_first;

        // For testing of beautify ignore:start directive
        opt.test_output_raw = (options.test_output_raw === undefined) ? false : options.test_output_raw;

        // force opt.space_after_anon_function to true if opt.jslint_happy
        if(opt.jslint_happy) {
            opt.space_after_anon_function = true;
        }

        if(options.indent_with_tabs){
            opt.indent_char = '\t';
            opt.indent_size = 1;
        }

        opt.eol = opt.eol.replace(/\\r/, '\r').replace(/\\n/, '\n')

        //----------------------------------
        indent_string = '';
        while (opt.indent_size > 0) {
            indent_string += opt.indent_char;
            opt.indent_size -= 1;
        }

        var preindent_index = 0;
        if(js_source_text && js_source_text.length) {
            while ( (js_source_text.charAt(preindent_index) === ' ' ||
                    js_source_text.charAt(preindent_index) === '\t')) {
                baseIndentString += js_source_text.charAt(preindent_index);
                preindent_index += 1;
            }
            js_source_text = js_source_text.substring(preindent_index);
        }

        last_type = 'TK_START_BLOCK'; // last token type
        last_last_text = ''; // pre-last token text
        output = new Output(indent_string, baseIndentString);

        // If testing the ignore directive, start with output disable set to true
        output.raw = opt.test_output_raw;


        // Stack of parsing/formatting states, including MODE.
        // We tokenize, parse, and output in an almost purely a forward-only stream of token input
        // and formatted output.  This makes the beautifier less accurate than full parsers
        // but also far more tolerant of syntax errors.
        //
        // For example, the default mode is MODE.BlockStatement. If we see a '{' we push a new frame of type
        // MODE.BlockStatement on the the stack, even though it could be object literal.  If we later
        // encounter a ":", we'll switch to to MODE.ObjectLiteral.  If we then see a ";",
        // most full parsers would die, but the beautifier gracefully falls back to
        // MODE.BlockStatement and continues on.
        flag_store = [];
        set_mode(MODE.BlockStatement);

        this.beautify = function() {

            /*jshint onevar:true */
            var local_token, sweet_code;
            Tokenizer = new tokenizer(js_source_text, opt, indent_string);
            tokens = Tokenizer.tokenize();
            token_pos = 0;

            while (local_token = get_token()) {
                for(var i = 0; i < local_token.comments_before.length; i++) {
                    // The cleanest handling of inline comments is to treat them as though they aren't there.
                    // Just continue formatting and the behavior should be logical.
                    // Also ignore unknown tokens.  Again, this should result in better behavior.
                    handle_token(local_token.comments_before[i]);
                }
                handle_token(local_token);

                last_last_text = flags.last_text;
                last_type = local_token.type;
                flags.last_text = local_token.text;

                token_pos += 1;
            }

            sweet_code = output.get_code();
            if (opt.end_with_newline) {
                sweet_code += '\n';
            }

            if (opt.eol != '\n') {
                sweet_code = sweet_code.replace(/[\n]/g, opt.eol);
            }

            return sweet_code;
        };

        function handle_token(local_token) {
            var newlines = local_token.newlines;
            var keep_whitespace = opt.keep_array_indentation && is_array(flags.mode);

            if (keep_whitespace) {
                for (i = 0; i < newlines; i += 1) {
                    print_newline(i > 0);
                }
            } else {
                if (opt.max_preserve_newlines && newlines > opt.max_preserve_newlines) {
                    newlines = opt.max_preserve_newlines;
                }

                if (opt.preserve_newlines) {
                    if (local_token.newlines > 1) {
                        print_newline();
                        for (var i = 1; i < newlines; i += 1) {
                            print_newline(true);
                        }
                    }
                }
            }

            current_token = local_token;
            handlers[current_token.type]();
        }

        // we could use just string.split, but
        // IE doesn't like returning empty strings
        function split_newlines(s) {
            //return s.split(/\x0d\x0a|\x0a/);

            s = s.replace(/\x0d/g, '');
            var out = [],
                idx = s.indexOf("\n");
            while (idx !== -1) {
                out.push(s.substring(0, idx));
                s = s.substring(idx + 1);
                idx = s.indexOf("\n");
            }
            if (s.length) {
                out.push(s);
            }
            return out;
        }

        function allow_wrap_or_preserved_newline(force_linewrap) {
            force_linewrap = (force_linewrap === undefined) ? false : force_linewrap;

            // Never wrap the first token on a line
            if (output.just_added_newline()) {
                return
            }

            if ((opt.preserve_newlines && current_token.wanted_newline) || force_linewrap) {
                print_newline(false, true);
            } else if (opt.wrap_line_length) {
                var proposed_line_length = output.current_line.get_character_count() + current_token.text.length +
                    (output.space_before_token ? 1 : 0);
                if (proposed_line_length >= opt.wrap_line_length) {
                    print_newline(false, true);
                }
            }
        }

        function print_newline(force_newline, preserve_statement_flags) {
            if (!preserve_statement_flags) {
                if (flags.last_text !== ';' && flags.last_text !== ',' && flags.last_text !== '=' && last_type !== 'TK_OPERATOR') {
                    while (flags.mode === MODE.Statement && !flags.if_block && !flags.do_block) {
                        restore_mode();
                    }
                }
            }

            if (output.add_new_line(force_newline)) {
                flags.multiline_frame = true;
            }
        }

        function print_token_line_indentation() {
            if (output.just_added_newline()) {
                if (opt.keep_array_indentation && is_array(flags.mode) && current_token.wanted_newline) {
                    output.current_line.push(current_token.whitespace_before);
                    output.space_before_token = false;
                } else if (output.set_indent(flags.indentation_level)) {
                    flags.line_indent_level = flags.indentation_level;
                }
            }
        }

        function print_token(printable_token) {
            if (output.raw) {
                output.add_raw_token(current_token)
                return;
            }

            if (opt.comma_first && last_type === 'TK_COMMA'
                && output.just_added_newline()) {
                if(output.previous_line.last() === ',') {
                    output.previous_line.pop();
                    print_token_line_indentation();
                    output.add_token(',');
                    output.space_before_token = true;
                }
            }

            printable_token = printable_token || current_token.text;
            print_token_line_indentation();
            output.add_token(printable_token);
        }

        function indent() {
            flags.indentation_level += 1;
        }

        function deindent() {
            if (flags.indentation_level > 0 &&
                ((!flags.parent) || flags.indentation_level > flags.parent.indentation_level))
                flags.indentation_level -= 1;
        }

        function set_mode(mode) {
            if (flags) {
                flag_store.push(flags);
                previous_flags = flags;
            } else {
                previous_flags = create_flags(null, mode);
            }

            flags = create_flags(previous_flags, mode);
        }

        function is_array(mode) {
            return mode === MODE.ArrayLiteral;
        }

        function is_expression(mode) {
            return in_array(mode, [MODE.Expression, MODE.ForInitializer, MODE.Conditional]);
        }

        function restore_mode() {
            if (flag_store.length > 0) {
                previous_flags = flags;
                flags = flag_store.pop();
                if (previous_flags.mode === MODE.Statement) {
                    output.remove_redundant_indentation(previous_flags);
                }
            }
        }

        function start_of_object_property() {
            return flags.parent.mode === MODE.ObjectLiteral && flags.mode === MODE.Statement && (
                (flags.last_text === ':' && flags.ternary_depth === 0) || (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set'])));
        }

        function start_of_statement() {
            if (
                    (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && current_token.type === 'TK_WORD') ||
                    (last_type === 'TK_RESERVED' && flags.last_text === 'do') ||
                    (last_type === 'TK_RESERVED' && flags.last_text === 'return' && !current_token.wanted_newline) ||
                    (last_type === 'TK_RESERVED' && flags.last_text === 'else' && !(current_token.type === 'TK_RESERVED' && current_token.text === 'if')) ||
                    (last_type === 'TK_END_EXPR' && (previous_flags.mode === MODE.ForInitializer || previous_flags.mode === MODE.Conditional)) ||
                    (last_type === 'TK_WORD' && flags.mode === MODE.BlockStatement
                        && !flags.in_case
                        && !(current_token.text === '--' || current_token.text === '++')
                        && last_last_text !== 'function'
                        && current_token.type !== 'TK_WORD' && current_token.type !== 'TK_RESERVED') ||
                    (flags.mode === MODE.ObjectLiteral && (
                        (flags.last_text === ':' && flags.ternary_depth === 0) || (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set']))))
                ) {

                set_mode(MODE.Statement);
                indent();

                if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && current_token.type === 'TK_WORD') {
                    flags.declaration_statement = true;
                }

                // Issue #276:
                // If starting a new statement with [if, for, while, do], push to a new line.
                // if (a) if (b) if(c) d(); else e(); else f();
                if (!start_of_object_property()) {
                    allow_wrap_or_preserved_newline(
                        current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['do', 'for', 'if', 'while']));
                }

                return true;
            }
            return false;
        }

        function all_lines_start_with(lines, c) {
            for (var i = 0; i < lines.length; i++) {
                var line = trim(lines[i]);
                if (line.charAt(0) !== c) {
                    return false;
                }
            }
            return true;
        }

        function each_line_matches_indent(lines, indent) {
            var i = 0,
                len = lines.length,
                line;
            for (; i < len; i++) {
                line = lines[i];
                // allow empty lines to pass through
                if (line && line.indexOf(indent) !== 0) {
                    return false;
                }
            }
            return true;
        }

        function is_special_word(word) {
            return in_array(word, ['case', 'return', 'do', 'if', 'throw', 'else']);
        }

        function get_token(offset) {
            var index = token_pos + (offset || 0);
            return (index < 0 || index >= tokens.length) ? null : tokens[index];
        }

        function handle_start_expr() {
            if (start_of_statement()) {
                // The conditional starts the statement if appropriate.
            }

            var next_mode = MODE.Expression;
            if (current_token.text === '[') {

                if (last_type === 'TK_WORD' || flags.last_text === ')') {
                    // this is array index specifier, break immediately
                    // a[x], fn()[x]
                    if (last_type === 'TK_RESERVED' && in_array(flags.last_text, Tokenizer.line_starters)) {
                        output.space_before_token = true;
                    }
                    set_mode(next_mode);
                    print_token();
                    indent();
                    if (opt.space_in_paren) {
                        output.space_before_token = true;
                    }
                    return;
                }

                next_mode = MODE.ArrayLiteral;
                if (is_array(flags.mode)) {
                    if (flags.last_text === '[' ||
                        (flags.last_text === ',' && (last_last_text === ']' || last_last_text === '}'))) {
                        // ], [ goes to new line
                        // }, [ goes to new line
                        if (!opt.keep_array_indentation) {
                            print_newline();
                        }
                    }
                }

            } else {
                if (last_type === 'TK_RESERVED' && flags.last_text === 'for') {
                    next_mode = MODE.ForInitializer;
                } else if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['if', 'while'])) {
                    next_mode = MODE.Conditional;
                } else {
                    // next_mode = MODE.Expression;
                }
            }

            if (flags.last_text === ';' || last_type === 'TK_START_BLOCK') {
                print_newline();
            } else if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK' || flags.last_text === '.') {
                // TODO: Consider whether forcing this is required.  Review failing tests when removed.
                allow_wrap_or_preserved_newline(current_token.wanted_newline);
                // do nothing on (( and )( and ][ and ]( and .(
            } else if (!(last_type === 'TK_RESERVED' && current_token.text === '(') && last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') {
                output.space_before_token = true;
            } else if ((last_type === 'TK_RESERVED' && (flags.last_word === 'function' || flags.last_word === 'typeof')) ||
                (flags.last_text === '*' && last_last_text === 'function')) {
                // function() vs function ()
                if (opt.space_after_anon_function) {
                    output.space_before_token = true;
                }
            } else if (last_type === 'TK_RESERVED' && (in_array(flags.last_text, Tokenizer.line_starters) || flags.last_text === 'catch')) {
                if (opt.space_before_conditional) {
                    output.space_before_token = true;
                }
            }

            // Should be a space between await and an IIFE
            if(current_token.text === '(' && last_type === 'TK_RESERVED' && flags.last_word === 'await'){
                output.space_before_token = true;
            }

            // Support of this kind of newline preservation.
            // a = (b &&
            //     (c || d));
            if (current_token.text === '(') {
                if (last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
                    if (!start_of_object_property()) {
                        allow_wrap_or_preserved_newline();
                    }
                }
            }

            set_mode(next_mode);
            print_token();
            if (opt.space_in_paren) {
                output.space_before_token = true;
            }

            // In all cases, if we newline while inside an expression it should be indented.
            indent();
        }

        function handle_end_expr() {
            // statements inside expressions are not valid syntax, but...
            // statements must all be closed when their container closes
            while (flags.mode === MODE.Statement) {
                restore_mode();
            }

            if (flags.multiline_frame) {
                allow_wrap_or_preserved_newline(current_token.text === ']' && is_array(flags.mode) && !opt.keep_array_indentation);
            }

            if (opt.space_in_paren) {
                if (last_type === 'TK_START_EXPR' && ! opt.space_in_empty_paren) {
                    // () [] no inner space in empty parens like these, ever, ref #320
                    output.trim();
                    output.space_before_token = false;
                } else {
                    output.space_before_token = true;
                }
            }
            if (current_token.text === ']' && opt.keep_array_indentation) {
                print_token();
                restore_mode();
            } else {
                restore_mode();
                print_token();
            }
            output.remove_redundant_indentation(previous_flags);

            // do {} while () // no statement required after
            if (flags.do_while && previous_flags.mode === MODE.Conditional) {
                previous_flags.mode = MODE.Expression;
                flags.do_block = false;
                flags.do_while = false;

            }
        }

        function handle_start_block() {
            // Check if this is should be treated as a ObjectLiteral
            var next_token = get_token(1)
            var second_token = get_token(2)
            if (second_token && (
                    (second_token.text === ':' && in_array(next_token.type, ['TK_STRING', 'TK_WORD', 'TK_RESERVED']))
                    || (in_array(next_token.text, ['get', 'set']) && in_array(second_token.type, ['TK_WORD', 'TK_RESERVED']))
                )) {
                // We don't support TypeScript,but we didn't break it for a very long time.
                // We'll try to keep not breaking it.
                if (!in_array(last_last_text, ['class','interface'])) {
                    set_mode(MODE.ObjectLiteral);
                } else {
                    set_mode(MODE.BlockStatement);
                }
            } else {
                set_mode(MODE.BlockStatement);
            }

            var empty_braces = !next_token.comments_before.length &&  next_token.text === '}';
            var empty_anonymous_function = empty_braces && flags.last_word === 'function' &&
                last_type === 'TK_END_EXPR';

            if (opt.brace_style === "expand" ||
                (opt.brace_style === "none" && current_token.wanted_newline)) {
                if (last_type !== 'TK_OPERATOR' &&
                    (empty_anonymous_function ||
                        last_type === 'TK_EQUALS' ||
                        (last_type === 'TK_RESERVED' && is_special_word(flags.last_text) && flags.last_text !== 'else'))) {
                    output.space_before_token = true;
                } else {
                    print_newline(false, true);
                }
            } else { // collapse
                if (last_type !== 'TK_OPERATOR' && last_type !== 'TK_START_EXPR') {
                    if (last_type === 'TK_START_BLOCK') {
                        print_newline();
                    } else {
                        output.space_before_token = true;
                    }
                } else {
                    // if TK_OPERATOR or TK_START_EXPR
                    if (is_array(previous_flags.mode) && flags.last_text === ',') {
                        if (last_last_text === '}') {
                            // }, { in array context
                            output.space_before_token = true;
                        } else {
                            print_newline(); // [a, b, c, {
                        }
                    }
                }
            }
            print_token();
            indent();
        }

        function handle_end_block() {
            // statements must all be closed when their container closes
            while (flags.mode === MODE.Statement) {
                restore_mode();
            }
            var empty_braces = last_type === 'TK_START_BLOCK';

            if (opt.brace_style === "expand") {
                if (!empty_braces) {
                    print_newline();
                }
            } else {
                // skip {}
                if (!empty_braces) {
                    if (is_array(flags.mode) && opt.keep_array_indentation) {
                        // we REALLY need a newline here, but newliner would skip that
                        opt.keep_array_indentation = false;
                        print_newline();
                        opt.keep_array_indentation = true;

                    } else {
                        print_newline();
                    }
                }
            }
            restore_mode();
            print_token();
        }

        function handle_word() {
            if (current_token.type === 'TK_RESERVED' && flags.mode !== MODE.ObjectLiteral &&
                in_array(current_token.text, ['set', 'get'])) {
                current_token.type = 'TK_WORD';
            }

            if (current_token.type === 'TK_RESERVED' && flags.mode === MODE.ObjectLiteral) {
                var next_token = get_token(1);
                if (next_token.text == ':') {
                    current_token.type = 'TK_WORD';
                }
            }

            if (start_of_statement()) {
                // The conditional starts the statement if appropriate.
            } else if (current_token.wanted_newline && !is_expression(flags.mode) &&
                (last_type !== 'TK_OPERATOR' || (flags.last_text === '--' || flags.last_text === '++')) &&
                last_type !== 'TK_EQUALS' &&
                (opt.preserve_newlines || !(last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const', 'set', 'get'])))) {

                print_newline();
            }

            if (flags.do_block && !flags.do_while) {
                if (current_token.type === 'TK_RESERVED' && current_token.text === 'while') {
                    // do {} ## while ()
                    output.space_before_token = true;
                    print_token();
                    output.space_before_token = true;
                    flags.do_while = true;
                    return;
                } else {
                    // do {} should always have while as the next word.
                    // if we don't see the expected while, recover
                    print_newline();
                    flags.do_block = false;
                }
            }

            // if may be followed by else, or not
            // Bare/inline ifs are tricky
            // Need to unwind the modes correctly: if (a) if (b) c(); else d(); else e();
            if (flags.if_block) {
                if (!flags.else_block && (current_token.type === 'TK_RESERVED' && current_token.text === 'else')) {
                    flags.else_block = true;
                } else {
                    while (flags.mode === MODE.Statement) {
                        restore_mode();
                    }
                    flags.if_block = false;
                    flags.else_block = false;
                }
            }

            if (current_token.type === 'TK_RESERVED' && (current_token.text === 'case' || (current_token.text === 'default' && flags.in_case_statement))) {
                print_newline();
                if (flags.case_body || opt.jslint_happy) {
                    // switch cases following one another
                    deindent();
                    flags.case_body = false;
                }
                print_token();
                flags.in_case = true;
                flags.in_case_statement = true;
                return;
            }

            if (current_token.type === 'TK_RESERVED' && current_token.text === 'function') {
                if (in_array(flags.last_text, ['}', ';']) || (output.just_added_newline() && ! in_array(flags.last_text, ['[', '{', ':', '=', ',']))) {
                    // make sure there is a nice clean space of at least one blank line
                    // before a new function definition
                    if ( !output.just_added_blankline() && !current_token.comments_before.length) {
                        print_newline();
                        print_newline(true);
                    }
                }
                if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD') {
                    if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set', 'new', 'return', 'export', 'async'])) {
                        output.space_before_token = true;
                    } else if (last_type === 'TK_RESERVED' && flags.last_text === 'default' && last_last_text === 'export') {
                        output.space_before_token = true;
                    } else {
                        print_newline();
                    }
                } else if (last_type === 'TK_OPERATOR' || flags.last_text === '=') {
                    // foo = function
                    output.space_before_token = true;
                } else if (!flags.multiline_frame && (is_expression(flags.mode) || is_array(flags.mode))) {
                    // (function
                } else {
                    print_newline();
                }
            }

            if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
                if (!start_of_object_property()) {
                    allow_wrap_or_preserved_newline();
                }
            }

            if (current_token.type === 'TK_RESERVED' &&  in_array(current_token.text, ['function', 'get', 'set'])) {
                print_token();
                flags.last_word = current_token.text;
                return;
            }

            prefix = 'NONE';

            if (last_type === 'TK_END_BLOCK') {
                if (!(current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['else', 'catch', 'finally']))) {
                    prefix = 'NEWLINE';
                } else {
                    if (opt.brace_style === "expand" ||
                        opt.brace_style === "end-expand" ||
                        (opt.brace_style === "none" && current_token.wanted_newline)) {
                        prefix = 'NEWLINE';
                    } else {
                        prefix = 'SPACE';
                        output.space_before_token = true;
                    }
                }
            } else if (last_type === 'TK_SEMICOLON' && flags.mode === MODE.BlockStatement) {
                // TODO: Should this be for STATEMENT as well?
                prefix = 'NEWLINE';
            } else if (last_type === 'TK_SEMICOLON' && is_expression(flags.mode)) {
                prefix = 'SPACE';
            } else if (last_type === 'TK_STRING') {
                prefix = 'NEWLINE';
            } else if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD' ||
                (flags.last_text === '*' && last_last_text === 'function')) {
                prefix = 'SPACE';
            } else if (last_type === 'TK_START_BLOCK') {
                prefix = 'NEWLINE';
            } else if (last_type === 'TK_END_EXPR') {
                output.space_before_token = true;
                prefix = 'NEWLINE';
            }

            if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, Tokenizer.line_starters) && flags.last_text !== ')') {
                if (flags.last_text === 'else' || flags.last_text === 'export') {
                    prefix = 'SPACE';
                } else {
                    prefix = 'NEWLINE';
                }

            }

            if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['else', 'catch', 'finally'])) {
                if (last_type !== 'TK_END_BLOCK' ||
                    opt.brace_style === "expand" ||
                    opt.brace_style === "end-expand" ||
                    (opt.brace_style === "none" && current_token.wanted_newline)) {
                    print_newline();
                } else {
                    output.trim(true);
                    var line = output.current_line;
                    // If we trimmed and there's something other than a close block before us
                    // put a newline back in.  Handles '} // comment' scenario.
                    if (line.last() !== '}') {
                        print_newline();
                    }
                    output.space_before_token = true;
                }
            } else if (prefix === 'NEWLINE') {
                if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
                    // no newline between 'return nnn'
                    output.space_before_token = true;
                } else if (last_type !== 'TK_END_EXPR') {
                    if ((last_type !== 'TK_START_EXPR' || !(current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['var', 'let', 'const']))) && flags.last_text !== ':') {
                        // no need to force newline on 'var': for (var x = 0...)
                        if (current_token.type === 'TK_RESERVED' && current_token.text === 'if' && flags.last_text === 'else') {
                            // no newline for } else if {
                            output.space_before_token = true;
                        } else {
                            print_newline();
                        }
                    }
                } else if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, Tokenizer.line_starters) && flags.last_text !== ')') {
                    print_newline();
                }
            } else if (flags.multiline_frame && is_array(flags.mode) && flags.last_text === ',' && last_last_text === '}') {
                print_newline(); // }, in lists get a newline treatment
            } else if (prefix === 'SPACE') {
                output.space_before_token = true;
            }
            print_token();
            flags.last_word = current_token.text;

            if (current_token.type === 'TK_RESERVED' && current_token.text === 'do') {
                flags.do_block = true;
            }

            if (current_token.type === 'TK_RESERVED' && current_token.text === 'if') {
                flags.if_block = true;
            }
        }

        function handle_semicolon() {
            if (start_of_statement()) {
                // The conditional starts the statement if appropriate.
                // Semicolon can be the start (and end) of a statement
                output.space_before_token = false;
            }
            while (flags.mode === MODE.Statement && !flags.if_block && !flags.do_block) {
                restore_mode();
            }
            print_token();
        }

        function handle_string() {
            if (start_of_statement()) {
                // The conditional starts the statement if appropriate.
                // One difference - strings want at least a space before
                output.space_before_token = true;
            } else if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD') {
                output.space_before_token = true;
            } else if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
                if (!start_of_object_property()) {
                    allow_wrap_or_preserved_newline();
                }
            } else {
                print_newline();
            }
            print_token();
        }

        function handle_equals() {
            if (start_of_statement()) {
                // The conditional starts the statement if appropriate.
            }

            if (flags.declaration_statement) {
                // just got an '=' in a var-line, different formatting/line-breaking, etc will now be done
                flags.declaration_assignment = true;
            }
            output.space_before_token = true;
            print_token();
            output.space_before_token = true;
        }

        function handle_comma() {
            if (flags.declaration_statement) {
                if (is_expression(flags.parent.mode)) {
                    // do not break on comma, for(var a = 1, b = 2)
                    flags.declaration_assignment = false;
                }

                print_token();

                if (flags.declaration_assignment) {
                    flags.declaration_assignment = false;
                    print_newline(false, true);
                } else {
                    output.space_before_token = true;
                    // for comma-first, we want to allow a newline before the comma
                    // to turn into a newline after the comma, which we will fixup later
                    if (opt.comma_first) {
                        allow_wrap_or_preserved_newline();
                    }
                }
                return;
            }

            print_token();
            if (flags.mode === MODE.ObjectLiteral ||
                (flags.mode === MODE.Statement && flags.parent.mode === MODE.ObjectLiteral)) {
                if (flags.mode === MODE.Statement) {
                    restore_mode();
                }
                print_newline();
            } else {
                // EXPR or DO_BLOCK
                output.space_before_token = true;
                // for comma-first, we want to allow a newline before the comma
                // to turn into a newline after the comma, which we will fixup later
                if (opt.comma_first) {
                    allow_wrap_or_preserved_newline();
                }
            }

        }

        function handle_operator() {
            if (start_of_statement()) {
                // The conditional starts the statement if appropriate.
            }

            if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
                // "return" had a special handling in TK_WORD. Now we need to return the favor
                output.space_before_token = true;
                print_token();
                return;
            }

            // hack for actionscript's import .*;
            if (current_token.text === '*' && last_type === 'TK_DOT') {
                print_token();
                return;
            }

            if (current_token.text === ':' && flags.in_case) {
                flags.case_body = true;
                indent();
                print_token();
                print_newline();
                flags.in_case = false;
                return;
            }

            if (current_token.text === '::') {
                // no spaces around exotic namespacing syntax operator
                print_token();
                return;
            }

            // Allow line wrapping between operators
            if (last_type === 'TK_OPERATOR') {
                allow_wrap_or_preserved_newline();
            }

            var space_before = true;
            var space_after = true;

            if (in_array(current_token.text, ['--', '++', '!', '~']) || (in_array(current_token.text, ['-', '+']) && (in_array(last_type, ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']) || in_array(flags.last_text, Tokenizer.line_starters) || flags.last_text === ','))) {
                // unary operators (and binary +/- pretending to be unary) special cases

                space_before = false;
                space_after = false;

                // http://www.ecma-international.org/ecma-262/5.1/#sec-7.9.1
                // if there is a newline between -- or ++ and anything else we should preserve it.
                if (current_token.wanted_newline && (current_token.text === '--' || current_token.text === '++')) {
                    print_newline(false, true);
                }

                if (flags.last_text === ';' && is_expression(flags.mode)) {
                    // for (;; ++i)
                    //        ^^^
                    space_before = true;
                }

                if (last_type === 'TK_RESERVED') {
                    space_before = true;
                } else if (last_type === 'TK_END_EXPR') {
                    space_before = !(flags.last_text === ']' && (current_token.text === '--' || current_token.text === '++'));
                } else if (last_type === 'TK_OPERATOR') {
                    // a++ + ++b;
                    // a - -b
                    space_before = in_array(current_token.text, ['--', '-', '++', '+']) && in_array(flags.last_text, ['--', '-', '++', '+']);
                    // + and - are not unary when preceeded by -- or ++ operator
                    // a-- + b
                    // a * +b
                    // a - -b
                    if (in_array(current_token.text, ['+', '-']) && in_array(flags.last_text, ['--', '++'])) {
                        space_after = true;
                    }
                }

                if ((flags.mode === MODE.BlockStatement || flags.mode === MODE.Statement) && (flags.last_text === '{' || flags.last_text === ';')) {
                    // { foo; --i }
                    // foo(); --bar;
                    print_newline();
                }
            } else if (current_token.text === ':') {
                if (flags.ternary_depth === 0) {
                    // Colon is invalid javascript outside of ternary and object, but do our best to guess what was meant.
                    space_before = false;
                } else {
                    flags.ternary_depth -= 1;
                }
            } else if (current_token.text === '?') {
                flags.ternary_depth += 1;
            } else if (current_token.text === '*' && last_type === 'TK_RESERVED' && flags.last_text === 'function') {
                space_before = false;
                space_after = false;
            }
            output.space_before_token = output.space_before_token || space_before;
            print_token();
            output.space_before_token = space_after;
        }

        function handle_block_comment() {
            if (output.raw) {
                output.add_raw_token(current_token)
                if (current_token.directives && current_token.directives['preserve'] === 'end') {
                    // If we're testing the raw output behavior, do not allow a directive to turn it off.
                    if (!opt.test_output_raw) {
                        output.raw = false;
                    }
                }
                return;
            }

            if (current_token.directives) {
                print_newline(false, true);
                print_token();
                if (current_token.directives['preserve'] === 'start') {
                    output.raw = true;
                }
                print_newline(false, true);
                return;
            }

            // inline block
            if (!acorn.newline.test(current_token.text) && !current_token.wanted_newline) {
                output.space_before_token = true;
                print_token();
                output.space_before_token = true;
                return;
            }

            var lines = split_newlines(current_token.text);
            var j; // iterator for this case
            var javadoc = false;
            var starless = false;
            var lastIndent = current_token.whitespace_before;
            var lastIndentLength = lastIndent.length;

            // block comment starts with a new line
            print_newline(false, true);
            if (lines.length > 1) {
                if (all_lines_start_with(lines.slice(1), '*')) {
                    javadoc = true;
                }
                else if (each_line_matches_indent(lines.slice(1), lastIndent)) {
                    starless = true;
                }
            }

            // first line always indented
            print_token(lines[0]);
            for (j = 1; j < lines.length; j++) {
                print_newline(false, true);
                if (javadoc) {
                    // javadoc: reformat and re-indent
                    print_token(' ' + ltrim(lines[j]));
                } else if (starless && lines[j].length > lastIndentLength) {
                    // starless: re-indent non-empty content, avoiding trim
                    print_token(lines[j].substring(lastIndentLength));
                } else {
                    // normal comments output raw
                    output.add_token(lines[j]);
                }
            }

            // for comments of more than one line, make sure there's a new line after
            print_newline(false, true);
        }

        function handle_comment() {
            if (current_token.wanted_newline) {
                print_newline(false, true);
            } else {
                output.trim(true);
            }

            output.space_before_token = true;
            print_token();
            print_newline(false, true);
        }

        function handle_dot() {
            if (start_of_statement()) {
                // The conditional starts the statement if appropriate.
            }

            if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
                output.space_before_token = true;
            } else {
                // allow preserved newlines before dots in general
                // force newlines on dots after close paren when break_chained - for bar().baz()
                allow_wrap_or_preserved_newline(flags.last_text === ')' && opt.break_chained_methods);
            }

            print_token();
        }

        function handle_unknown() {
            print_token();

            if (current_token.text[current_token.text.length - 1] === '\n') {
                print_newline();
            }
        }

        function handle_eof() {
            // Unwind any open statements
            while (flags.mode === MODE.Statement) {
                restore_mode();
            }
        }
    }


    function OutputLine(parent) {
        var _character_count = 0;
        // use indent_count as a marker for lines that have preserved indentation
        var _indent_count = -1;

        var _items = [];
        var _empty = true;

        this.set_indent = function(level) {
            _character_count = parent.baseIndentLength + level * parent.indent_length
            _indent_count = level;
        }

        this.get_character_count = function() {
            return _character_count;
        }

        this.is_empty = function() {
            return _empty;
        }

        this.last = function() {
            if (!this._empty) {
              return _items[_items.length - 1];
            } else {
              return null;
            }
        }

        this.push = function(input) {
            _items.push(input);
            _character_count += input.length;
            _empty = false;
        }

        this.pop = function() {
            var item = null;
            if (!_empty) {
                item = _items.pop();
                _character_count -= item.length;
                _empty = _items.length === 0;
            }
            return item;
        }

        this.remove_indent = function() {
            if (_indent_count > 0) {
                _indent_count -= 1;
                _character_count -= parent.indent_length
            }
        }

        this.trim = function() {
            while (this.last() === ' ') {
                var item = _items.pop();
                _character_count -= 1;
            }
            _empty = _items.length === 0;
        }

        this.toString = function() {
            var result = '';
            if (!this._empty) {
                if (_indent_count >= 0) {
                    result = parent.indent_cache[_indent_count];
                }
                result += _items.join('')
            }
            return result;
        }
    }

    function Output(indent_string, baseIndentString) {
        baseIndentString = baseIndentString || '';
        this.indent_cache = [ baseIndentString ];
        this.baseIndentLength = baseIndentString.length;
        this.indent_length = indent_string.length;
        this.raw = false;

        var lines =[];
        this.baseIndentString = baseIndentString;
        this.indent_string = indent_string;
        this.previous_line = null;
        this.current_line = null;
        this.space_before_token = false;

        this.add_outputline = function() {
            this.previous_line = this.current_line;
            this.current_line = new OutputLine(this);
            lines.push(this.current_line);
        }

        // initialize
        this.add_outputline();


        this.get_line_number = function() {
            return lines.length;
        }

        // Using object instead of string to allow for later expansion of info about each line
        this.add_new_line = function(force_newline) {
            if (this.get_line_number() === 1 && this.just_added_newline()) {
                return false; // no newline on start of file
            }

            if (force_newline || !this.just_added_newline()) {
                if (!this.raw) {
                    this.add_outputline();
                }
                return true;
            }

            return false;
        }

        this.get_code = function() {
            var sweet_code = lines.join('\n').replace(/[\r\n\t ]+$/, '');
            return sweet_code;
        }

        this.set_indent = function(level) {
            // Never indent your first output indent at the start of the file
            if (lines.length > 1) {
                while(level >= this.indent_cache.length) {
                    this.indent_cache.push(this.indent_cache[this.indent_cache.length - 1] + this.indent_string);
                }

                this.current_line.set_indent(level);
                return true;
            }
            this.current_line.set_indent(0);
            return false;
        }

        this.add_raw_token = function(token) {
            for (var x = 0; x < token.newlines; x++) {
                this.add_outputline();
            }
            this.current_line.push(token.whitespace_before);
            this.current_line.push(token.text);
            this.space_before_token = false;
        }

        this.add_token = function(printable_token) {
            this.add_space_before_token();
            this.current_line.push(printable_token);
        }

        this.add_space_before_token = function() {
            if (this.space_before_token && !this.just_added_newline()) {
                this.current_line.push(' ');
            }
            this.space_before_token = false;
        }

        this.remove_redundant_indentation = function (frame) {
            // This implementation is effective but has some issues:
            //     - can cause line wrap to happen too soon due to indent removal
            //           after wrap points are calculated
            // These issues are minor compared to ugly indentation.

            if (frame.multiline_frame ||
                frame.mode === MODE.ForInitializer ||
                frame.mode === MODE.Conditional) {
                return;
            }

            // remove one indent from each line inside this section
            var index = frame.start_line_index;
            var line;

            var output_length = lines.length;
            while (index < output_length) {
                lines[index].remove_indent();
                index++;
            }
        }

        this.trim = function(eat_newlines) {
            eat_newlines = (eat_newlines === undefined) ? false : eat_newlines;

            this.current_line.trim(indent_string, baseIndentString);

            while (eat_newlines && lines.length > 1 &&
                this.current_line.is_empty()) {
                lines.pop();
                this.current_line = lines[lines.length - 1]
                this.current_line.trim();
            }

            this.previous_line = lines.length > 1 ? lines[lines.length - 2] : null;
        }

        this.just_added_newline = function() {
            return this.current_line.is_empty();
        }

        this.just_added_blankline = function() {
            if (this.just_added_newline()) {
                if (lines.length === 1) {
                    return true; // start of the file and newline = blank
                }

                var line = lines[lines.length - 2];
                return line.is_empty();
            }
            return false;
        }
    }


    var Token = function(type, text, newlines, whitespace_before, mode, parent) {
        this.type = type;
        this.text = text;
        this.comments_before = [];
        this.newlines = newlines || 0;
        this.wanted_newline = newlines > 0;
        this.whitespace_before = whitespace_before || '';
        this.parent = null;
        this.directives = null;
    }

    function tokenizer(input, opts, indent_string) {

        var whitespace = "\n\r\t ".split('');
        var digit = /[0-9]/;
        var digit_oct = /[01234567]/;
        var digit_hex = /[0123456789abcdefABCDEF]/;

        var punct = ('+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! ~ , : ? ^ ^= |= :: =>').split(' ');
        // words which should always start on new line.
        this.line_starters = 'continue,try,throw,return,var,let,const,if,switch,case,default,for,while,break,function,import,export'.split(',');
        var reserved_words = this.line_starters.concat(['do', 'in', 'else', 'get', 'set', 'new', 'catch', 'finally', 'typeof', 'yield', 'async', 'await']);

        //  /* ... */ comment ends with nearest */ or end of file
        var block_comment_pattern = /([\s\S]*?)((?:\*\/)|$)/g;

        // comment ends just before nearest linefeed or end of file
        var comment_pattern = /([^\n\r\u2028\u2029]*)/g;

        var directives_block_pattern = /\/\* beautify( \w+[:]\w+)+ \*\//g;
        var directive_pattern = / (\w+)[:](\w+)/g;
        var directives_end_ignore_pattern = /([\s\S]*?)((?:\/\*\sbeautify\signore:end\s\*\/)|$)/g;

        var template_pattern = /((<\?php|<\?=)[\s\S]*?\?>)|(<%[\s\S]*?%>)/g

        var n_newlines, whitespace_before_token, in_html_comment, tokens, parser_pos;
        var input_length;

        this.tokenize = function() {
            // cache the source's length.
            input_length = input.length
            parser_pos = 0;
            in_html_comment = false
            tokens = [];

            var next, last;
            var token_values;
            var open = null;
            var open_stack = [];
            var comments = [];

            while (!(last && last.type === 'TK_EOF')) {
                token_values = tokenize_next();
                next = new Token(token_values[1], token_values[0], n_newlines, whitespace_before_token);
                while(next.type === 'TK_COMMENT' || next.type === 'TK_BLOCK_COMMENT' || next.type === 'TK_UNKNOWN') {
                    if (next.type === 'TK_BLOCK_COMMENT') {
                        next.directives = token_values[2];
                    }
                    comments.push(next);
                    token_values = tokenize_next();
                    next = new Token(token_values[1], token_values[0], n_newlines, whitespace_before_token);
                }

                if (comments.length) {
                    next.comments_before = comments;
                    comments = [];
                }

                if (next.type === 'TK_START_BLOCK' || next.type === 'TK_START_EXPR') {
                    next.parent = last;
                    open_stack.push(open);
                    open = next;
                }  else if ((next.type === 'TK_END_BLOCK' || next.type === 'TK_END_EXPR') &&
                    (open && (
                        (next.text === ']' && open.text === '[') ||
                        (next.text === ')' && open.text === '(') ||
                        (next.text === '}' && open.text === '{')))) {
                    next.parent = open.parent;
                    open = open_stack.pop();
                }

                tokens.push(next);
                last = next;
            }

            return tokens;
        }

        function get_directives (text) {
            if (!text.match(directives_block_pattern)) {
                return null;
            }

            var directives = {};
            directive_pattern.lastIndex = 0;
            var directive_match = directive_pattern.exec(text);

            while (directive_match) {
                directives[directive_match[1]] = directive_match[2];
                directive_match = directive_pattern.exec(text);
            }

            return directives;
        }

        function tokenize_next() {
            var i, resulting_string;
            var whitespace_on_this_line = [];

            n_newlines = 0;
            whitespace_before_token = '';

            if (parser_pos >= input_length) {
                return ['', 'TK_EOF'];
            }

            var last_token;
            if (tokens.length) {
                last_token = tokens[tokens.length-1];
            } else {
                // For the sake of tokenizing we can pretend that there was on open brace to start
                last_token = new Token('TK_START_BLOCK', '{');
            }


            var c = input.charAt(parser_pos);
            parser_pos += 1;

            while (in_array(c, whitespace)) {

                if (acorn.newline.test(c)) {
                    if (!(c === '\n' && input.charAt(parser_pos-2) === '\r')) {
                        n_newlines += 1;
                        whitespace_on_this_line = [];
                    }
                } else {
                    whitespace_on_this_line.push(c);
                }

                if (parser_pos >= input_length) {
                    return ['', 'TK_EOF'];
                }

                c = input.charAt(parser_pos);
                parser_pos += 1;
            }

            if(whitespace_on_this_line.length) {
                whitespace_before_token = whitespace_on_this_line.join('');
            }

            if (digit.test(c)) {
                var allow_decimal = true;
                var allow_e = true;
                var local_digit = digit;

                if (c === '0' && parser_pos < input_length && /[Xxo]/.test(input.charAt(parser_pos))) {
                    // switch to hex/oct number, no decimal or e, just hex/oct digits
                    allow_decimal = false;
                    allow_e = false;
                    c += input.charAt(parser_pos);
                    parser_pos += 1;
                    local_digit = /[o]/.test(input.charAt(parser_pos)) ? digit_oct : digit_hex;
                } else {
                    // we know this first loop will run.  It keeps the logic simpler.
                    c = '';
                    parser_pos -= 1;
                }

                // Add the digits
                while (parser_pos < input_length && local_digit.test(input.charAt(parser_pos))) {
                    c += input.charAt(parser_pos);
                    parser_pos += 1;

                    if (allow_decimal && parser_pos < input_length && input.charAt(parser_pos) === '.') {
                        c += input.charAt(parser_pos);
                        parser_pos += 1;
                        allow_decimal = false;
                    }

                    if (allow_e && parser_pos < input_length && /[Ee]/.test(input.charAt(parser_pos))) {
                        c += input.charAt(parser_pos);
                        parser_pos += 1;

                        if (parser_pos < input_length && /[+-]/.test(input.charAt(parser_pos))) {
                            c += input.charAt(parser_pos);
                            parser_pos += 1;
                        }

                        allow_e = false;
                        allow_decimal = false;
                    }
                }

                return [c, 'TK_WORD'];
            }

            if (acorn.isIdentifierStart(input.charCodeAt(parser_pos-1))) {
                if (parser_pos < input_length) {
                    while (acorn.isIdentifierChar(input.charCodeAt(parser_pos))) {
                        c += input.charAt(parser_pos);
                        parser_pos += 1;
                        if (parser_pos === input_length) {
                            break;
                        }
                    }
                }

                if (!(last_token.type === 'TK_DOT' ||
                        (last_token.type === 'TK_RESERVED' && in_array(last_token.text, ['set', 'get'])))
                    && in_array(c, reserved_words)) {
                    if (c === 'in') { // hack for 'in' operator
                        return [c, 'TK_OPERATOR'];
                    }
                    return [c, 'TK_RESERVED'];
                }

                return [c, 'TK_WORD'];
            }

            if (c === '(' || c === '[') {
                return [c, 'TK_START_EXPR'];
            }

            if (c === ')' || c === ']') {
                return [c, 'TK_END_EXPR'];
            }

            if (c === '{') {
                return [c, 'TK_START_BLOCK'];
            }

            if (c === '}') {
                return [c, 'TK_END_BLOCK'];
            }

            if (c === ';') {
                return [c, 'TK_SEMICOLON'];
            }

            if (c === '/') {
                var comment = '';
                // peek for comment /* ... */
                if (input.charAt(parser_pos) === '*') {
                    parser_pos += 1;
                    block_comment_pattern.lastIndex = parser_pos;
                    var comment_match = block_comment_pattern.exec(input);
                    comment = '/*' + comment_match[0];
                    parser_pos += comment_match[0].length;
                    var directives = get_directives(comment);
                    if (directives && directives['ignore'] === 'start') {
                        directives_end_ignore_pattern.lastIndex = parser_pos;
                        comment_match = directives_end_ignore_pattern.exec(input)
                        comment += comment_match[0];
                        parser_pos += comment_match[0].length;
                    }
                    comment = comment.replace(acorn.lineBreak, '\n');
                    return [comment, 'TK_BLOCK_COMMENT', directives];
                }
                // peek for comment // ...
                if (input.charAt(parser_pos) === '/') {
                    parser_pos += 1;
                    comment_pattern.lastIndex = parser_pos;
                    var comment_match = comment_pattern.exec(input);
                    comment = '//' + comment_match[0];
                    parser_pos += comment_match[0].length;
                    return [comment, 'TK_COMMENT'];
                }

            }

            if (c === '`' || c === "'" || c === '"' || // string
                (
                    (c === '/') || // regexp
                    (opts.e4x && c === "<" && input.slice(parser_pos - 1).match(/^<([-a-zA-Z:0-9_.]+|{[^{}]*}|!\[CDATA\[[\s\S]*?\]\])(\s+[-a-zA-Z:0-9_.]+\s*=\s*('[^']*'|"[^"]*"|{.*?}))*\s*(\/?)\s*>/)) // xml
                ) && ( // regex and xml can only appear in specific locations during parsing
                    (last_token.type === 'TK_RESERVED' && in_array(last_token.text , ['return', 'case', 'throw', 'else', 'do', 'typeof', 'yield'])) ||
                    (last_token.type === 'TK_END_EXPR' && last_token.text === ')' &&
                        last_token.parent && last_token.parent.type === 'TK_RESERVED' && in_array(last_token.parent.text, ['if', 'while', 'for'])) ||
                    (in_array(last_token.type, ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK',
                        'TK_END_BLOCK', 'TK_OPERATOR', 'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON', 'TK_COMMA'
                    ]))
                )) {

                var sep = c,
                    esc = false,
                    has_char_escapes = false;

                resulting_string = c;

                if (sep === '/') {
                    //
                    // handle regexp
                    //
                    var in_char_class = false;
                    while (parser_pos < input_length &&
                            ((esc || in_char_class || input.charAt(parser_pos) !== sep) &&
                            !acorn.newline.test(input.charAt(parser_pos)))) {
                        resulting_string += input.charAt(parser_pos);
                        if (!esc) {
                            esc = input.charAt(parser_pos) === '\\';
                            if (input.charAt(parser_pos) === '[') {
                                in_char_class = true;
                            } else if (input.charAt(parser_pos) === ']') {
                                in_char_class = false;
                            }
                        } else {
                            esc = false;
                        }
                        parser_pos += 1;
                    }
                } else if (opts.e4x && sep === '<') {
                    //
                    // handle e4x xml literals
                    //
                    var xmlRegExp = /<(\/?)([-a-zA-Z:0-9_.]+|{[^{}]*}|!\[CDATA\[[\s\S]*?\]\])(\s+[-a-zA-Z:0-9_.]+\s*=\s*('[^']*'|"[^"]*"|{.*?}))*\s*(\/?)\s*>/g;
                    var xmlStr = input.slice(parser_pos - 1);
                    var match = xmlRegExp.exec(xmlStr);
                    if (match && match.index === 0) {
                        var rootTag = match[2];
                        var depth = 0;
                        while (match) {
                            var isEndTag = !! match[1];
                            var tagName = match[2];
                            var isSingletonTag = ( !! match[match.length - 1]) || (tagName.slice(0, 8) === "![CDATA[");
                            if (tagName === rootTag && !isSingletonTag) {
                                if (isEndTag) {
                                    --depth;
                                } else {
                                    ++depth;
                                }
                            }
                            if (depth <= 0) {
                                break;
                            }
                            match = xmlRegExp.exec(xmlStr);
                        }
                        var xmlLength = match ? match.index + match[0].length : xmlStr.length;
                        xmlStr = xmlStr.slice(0, xmlLength);
                        parser_pos += xmlLength - 1;
                        xmlStr = xmlStr.replace(acorn.lineBreak, '\n');
                        return [xmlStr, "TK_STRING"];
                    }
                } else {
                    //
                    // handle string
                    //
                    // Template strings can travers lines without escape characters.
                    // Other strings cannot
                    while (parser_pos < input_length &&
                            (esc || (input.charAt(parser_pos) !== sep &&
                            (sep === '`' || !acorn.newline.test(input.charAt(parser_pos)))))) {
                        // Handle \r\n linebreaks after escapes or in template strings
                        if ((esc || sep === '`') && acorn.newline.test(input.charAt(parser_pos))) {
                            if (input.charAt(parser_pos) === '\r' && input.charAt(parser_pos + 1) === '\n') {
                                parser_pos += 1;
                            }
                            resulting_string += '\n';
                        } else {
                            resulting_string += input.charAt(parser_pos);
                        }
                        if (esc) {
                            if (input.charAt(parser_pos) === 'x' || input.charAt(parser_pos) === 'u') {
                                has_char_escapes = true;
                            }
                            esc = false;
                        } else {
                            esc = input.charAt(parser_pos) === '\\';
                        }
                        parser_pos += 1;
                    }

                }

                if (has_char_escapes && opts.unescape_strings) {
                    resulting_string = unescape_string(resulting_string);
                }

                if (parser_pos < input_length && input.charAt(parser_pos) === sep) {
                    resulting_string += sep;
                    parser_pos += 1;

                    if (sep === '/') {
                        // regexps may have modifiers /regexp/MOD , so fetch those, too
                        // Only [gim] are valid, but if the user puts in garbage, do what we can to take it.
                        while (parser_pos < input_length && acorn.isIdentifierStart(input.charCodeAt(parser_pos))) {
                            resulting_string += input.charAt(parser_pos);
                            parser_pos += 1;
                        }
                    }
                }
                return [resulting_string, 'TK_STRING'];
            }

            if (c === '#') {

                if (tokens.length === 0 && input.charAt(parser_pos) === '!') {
                    // shebang
                    resulting_string = c;
                    while (parser_pos < input_length && c !== '\n') {
                        c = input.charAt(parser_pos);
                        resulting_string += c;
                        parser_pos += 1;
                    }
                    return [trim(resulting_string) + '\n', 'TK_UNKNOWN'];
                }



                // Spidermonkey-specific sharp variables for circular references
                // https://developer.mozilla.org/En/Sharp_variables_in_JavaScript
                // http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935
                var sharp = '#';
                if (parser_pos < input_length && digit.test(input.charAt(parser_pos))) {
                    do {
                        c = input.charAt(parser_pos);
                        sharp += c;
                        parser_pos += 1;
                    } while (parser_pos < input_length && c !== '#' && c !== '=');
                    if (c === '#') {
                        //
                    } else if (input.charAt(parser_pos) === '[' && input.charAt(parser_pos + 1) === ']') {
                        sharp += '[]';
                        parser_pos += 2;
                    } else if (input.charAt(parser_pos) === '{' && input.charAt(parser_pos + 1) === '}') {
                        sharp += '{}';
                        parser_pos += 2;
                    }
                    return [sharp, 'TK_WORD'];
                }
            }

            if (c === '<' && (input.charAt(parser_pos) === '?' || input.charAt(parser_pos) === '%')) {
                template_pattern.lastIndex = parser_pos - 1;
                var template_match = template_pattern.exec(input);
                if(template_match) {
                    c = template_match[0];
                    parser_pos += c.length - 1;
                    c = c.replace(acorn.lineBreak, '\n');
                    return [c, 'TK_STRING'];
                }
            }

            if (c === '<' && input.substring(parser_pos - 1, parser_pos + 3) === '<!--') {
                parser_pos += 3;
                c = '<!--';
                while (!acorn.newline.test(input.charAt(parser_pos)) && parser_pos < input_length) {
                    c += input.charAt(parser_pos);
                    parser_pos++;
                }
                in_html_comment = true;
                return [c, 'TK_COMMENT'];
            }

            if (c === '-' && in_html_comment && input.substring(parser_pos - 1, parser_pos + 2) === '-->') {
                in_html_comment = false;
                parser_pos += 2;
                return ['-->', 'TK_COMMENT'];
            }

            if (c === '.') {
                return [c, 'TK_DOT'];
            }

            if (in_array(c, punct)) {
                while (parser_pos < input_length && in_array(c + input.charAt(parser_pos), punct)) {
                    c += input.charAt(parser_pos);
                    parser_pos += 1;
                    if (parser_pos >= input_length) {
                        break;
                    }
                }

                if (c === ',') {
                    return [c, 'TK_COMMA'];
                } else if (c === '=') {
                    return [c, 'TK_EQUALS'];
                } else {
                    return [c, 'TK_OPERATOR'];
                }
            }

            return [c, 'TK_UNKNOWN'];
        }


        function unescape_string(s) {
            var esc = false,
                out = '',
                pos = 0,
                s_hex = '',
                escaped = 0,
                c;

            while (esc || pos < s.length) {

                c = s.charAt(pos);
                pos++;

                if (esc) {
                    esc = false;
                    if (c === 'x') {
                        // simple hex-escape \x24
                        s_hex = s.substr(pos, 2);
                        pos += 2;
                    } else if (c === 'u') {
                        // unicode-escape, \u2134
                        s_hex = s.substr(pos, 4);
                        pos += 4;
                    } else {
                        // some common escape, e.g \n
                        out += '\\' + c;
                        continue;
                    }
                    if (!s_hex.match(/^[0123456789abcdefABCDEF]+$/)) {
                        // some weird escaping, bail out,
                        // leaving whole string intact
                        return s;
                    }

                    escaped = parseInt(s_hex, 16);

                    if (escaped >= 0x00 && escaped < 0x20) {
                        // leave 0x00...0x1f escaped
                        if (c === 'x') {
                            out += '\\x' + s_hex;
                        } else {
                            out += '\\u' + s_hex;
                        }
                        continue;
                    } else if (escaped === 0x22 || escaped === 0x27 || escaped === 0x5c) {
                        // single-quote, apostrophe, backslash - escape these
                        out += '\\' + String.fromCharCode(escaped);
                    } else if (c === 'x' && escaped > 0x7e && escaped <= 0xff) {
                        // we bail out on \x7f..\xff,
                        // leaving whole string escaped,
                        // as it's probably completely binary
                        return s;
                    } else {
                        out += String.fromCharCode(escaped);
                    }
                } else if (c === '\\') {
                    esc = true;
                } else {
                    out += c;
                }
            }
            return out;
        }

    }

    /* jshint ignore:end */
    /* jscs:enable */

    return {
      run: run
    }
  };

}));

};


modules['helpcenter/primary/views/profile'] = function(require, exports, module, modules) {
/**
 * @fileoverview Client-side functionality of profile page:
 * - Change/remove avatar
 * - Add/remove fields
 * - Verify emails
 * - Connect/disconnect social profiles
 */

var LinkProfile = require('helpcenter/primary/models/link-profile');
var DeleteIdentity = require('helpcenter/primary/models/deletefields');

module.exports = Backbone.View.extend({
	el: '#profile-container',

	events: {
		'click #change-avatar'                    : 'changeAvatar',
		'click #delete-avatar'                    : 'deleteAvatar',
		'click .js-add-field'                     : 'add',
		'click .js-delete-field'                  : 'delete',
		'click .js-verify-email'									: 'verifyEmail',
		'click .js-social-connect'                : 'connect',
		'click .js-social-disconnect'             : 'disconnect',
		'change .textfield, .file-input, .select' : 'change',
		'submit .js-validate'                     : 'submit'
	},

	unsaved: false,

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Opens file input dialog and selects file to be uploaded
	 */
	changeAvatar: function() {
		var $fileInput = $('#avatar-input');
		$fileInput.click();
		$fileInput.on('change', function() {
			$('#avatar-hint').text($fileInput.val().split('\\').pop());
		});
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Deletes user avatar
	 */
	deleteAvatar: function() {
		var $deleteBtn = $('#delete-avatar');

		// Disable button and show loading animation
		$deleteBtn
		  .addClass('is-disabled button--loading')
		  .prop('disabled', true)
		  .after(_.template($('#tpl-loader').html()));

		// Send API request
		new DeleteIdentity({ 'id': 1 }).destroy({
		  success: function() {
		    var $avatar = $('#edit-avatar');
		    $avatar.attr('src', $avatar.attr('src') + '/?refresh');
		    $deleteBtn.removeClass('button--loading');
		  },
		  error: function() {
		    $deleteBtn.removeClass('is-disabled button--loading').prop('disabled', false);
		  }
		});
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Adds a new phone or email field
	 */
	add: function(e) {
		var $el = $(e.target);
		var tplField = _.template($('#tpl-field').html());
		var tplFieldHtml = tplField({
		  type: $el.data('add')
		});

		// Append the field
		$(tplFieldHtml).appendTo($el.parent()).slideDown(100);

		// Hide 'Add' link
		$el.fadeOut(100);
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Removes phone/email
	 * @param  {Object} e
	 */
	delete: function(e) {
		var $deleteLink = $(e.target);
		var fieldType = $deleteLink.data('remove');
		var fieldId = $deleteLink.data('id');

		// Show loading animation
		$deleteLink
		  .addClass('is-disabled button--loading')
		  .after(_.template($('#tpl-loader').html()));

		// Send API request
		new DeleteIdentity({
		  'id': fieldId
		}, {
		  'fieldId': fieldId,
		  'fieldType': fieldType
		}).destroy({
		  success: function() {
		    $deleteLink.parents('.js-field-value').remove();
		  },
		  error: function() {
		    $deleteLink
		      .removeClass('is-disabled button--loading')
		      .next().remove();
		  }
		});
	},

	/**
	 * @author Prsanjit Singh <prasanjit.singh@kayako.com>
	 * @description Sends verification email
	 * @param {Object} e
	 */
	verifyEmail: function(e) {
		var view = this;
		var $verifyLink = $(e.target);

		// Disable link and show loading animation
		$verifyLink.addClass('is-disabled');
		$verifyLink.next('.js-delete-field')
			.addClass('button--loading')
			.after(_.template($('#tpl-loader').html()));

		// Send API request
		new LinkProfile({
			'id': $verifyLink.data('id'),
			'query': 'verifyemail'
		}).save({}, {
			success: function() {
				view.notification($verifyLink, 'success');
			},
			error: function() {
				view.notification($verifyLink, 'error');
			}
		});
	},

	/**
	 * @author Prsanjit Singh <prasanjit.singh@kayako.com>
	 * @description Shows success/error notification (auto-hide)
	 * @param {Object} $verifyLink
	 * @param {String} errorType
	 */
	notification: function($verifyLink, errorType) {
		// Re-enable link and remove loading animation
		$verifyLink.removeClass('is-disabled');
		$verifyLink.next('.js-delete-field').removeClass('button--loading');

		// Show auto-hide alert bar
		var alertTpl = _.template($('#tpl-alert').html());
		$('body').append(alertTpl({ type: errorType }));
		setTimeout(function() {
			$('.js-alert-box').addClass('a-alert-box--out');
			setTimeout(function() {
				$('.js-alert-box').remove();
			}, 200);
		}, 3000);
	},

	/**
	 * @author Prsanjit Singh <prasanjit.singh@kayako.com>
	 * @description Connects facebook/twitter account to user profile
	 * @param {Object} e
	 */
	connect: function(e) {
		var $btn = $(e.target);

		// Show loading animation
		$btn
		  .addClass('is-disabled button--loading')
		  .prop('disabled', true)
		  .after(_.template($('#tpl-loader').html()));

		// Send API request
		var account = $btn.data('account');
		new LinkProfile({
		  'account': account,
			'query': 'connect'
		}).fetch({
		  data: {
		    callback: account === 'twitter' ? '/profile/linktwitter/' : '/facebook/connect/'
		  },
		  success: function(model, response) {
		    window.location.href = response.data.link;
		  },
		  error: function() {
				$btn.removeClass('is-disabled button--loading').prop('disabled', false);
		  }
		});
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Disconnects facebook/twitter account from user profile
	 * @param {Object} e
	 */
	disconnect: function(e) {
		var $link = $(e.target);
		$link.addClass('is-disabled');

		// Send API request
		var account = $link.data('account');
		new LinkProfile({
			'id': $link.data('id'),
			'account': account,
			'query': 'disconnect'
		}).destroy({
			success: function() {
				// Replace 'Disconnect' link with 'Connect' button
				$link.parent('.js-social-connection').html('<input class="button button--wide u-inlineblock js-social-connect" data-account="' + account + '" type="button" value="Connect">');
			},
			error: function() {
				$link.removeClass('is-disabled');
			}
		});
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Show alert if there are unsaved changes
	 */
	change: function() {
		var view = this;
		view.unsaved = true;

		window.onbeforeunload = function() {
			// Remove any animation from social connect buttons
		  $('.button--wide.button--loading')
				.attr('disabled', false)
				.removeClass('is-disabled button--loading');

		  if (view.unsaved) {
		    return 'You have some unsaved edits.';
		  }
		};
	},

	/**
	 * @author Prasanjit Singh <prasanjit.singh@kayako.com>
	 * @description Removes alert bar and focuses invalid field
	 * if form contains any error
	 */
	submit: function() {
		this.unsaved = false;
		$('#profile').find('.js-required-field').each(function() {
		  if (!$(this).val().trim().length) {
				$(this).focus();
				$('.alert-box--overlay').remove();
				return false;
		  }
		});
	}
});
};


modules['helpcenter/primary/models/customize/options'] = function(require, exports, module, modules) {
module.exports = Backbone.Model.extend({
    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @param  {Object}
     */
    initialize: function(options) {
        if (options.api) {
            this.idAttribute = 'id';
            this.api = options.api;
        }
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @description Override backbone's save method
     * @param  {Object} attributes
     * @param  {Object} options
     */
    save: function(attributes, options) {
        options = options || {};
        attributes = attributes || {};
        this.set(attributes);
        options.data = this.toJSON();

        return Backbone.Model.prototype.save.call(this, attributes, options);
    },

    /**
     * @author Prasanjit Singh <prasanjit.singh@kayako.com>
     * @return {Obejct}
     */
    url: function() {
    	return _Payload.portalpath + 'api/v1/' + this.api + '?include=*';
    }
});

};


modules['helpcenter/primary/plugins/froala/quote'] = function(require, exports, module, modules) {
/*!
 * froala_editor v2.3.4 (https://www.froala.com/wysiwyg-editor)
 * License https://froala.com/wysiwyg-editor/terms/
 * Copyright 2014-2016 Froala Labs
 */

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof module === 'object' && module.exports) {
        // Node/CommonJS
        module.exports = function( root, jQuery ) {
            if ( jQuery === undefined ) {
                // require('jQuery') returns a factory that requires window to
                // build a jQuery instance, we normalize how we use modules
                // that require this pattern but the window provided is a noop
                // if it's defined (how jquery works)
                if ( typeof window !== 'undefined' ) {
                    jQuery = require('jquery');
                }
                else {
                    jQuery = require('jquery')(root);
                }
            }
            factory(jQuery);
            return jQuery;
        };
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {

  'use strict';

  $.FE.PLUGINS.quote = function (editor) {
    function _deepestParent(node) {
      while (node.parentNode && node.parentNode != editor.$el.get(0)) {
        node = node.parentNode;
      }

      return node;
    }

    function _increase () {
      // Get blocks.
      var blocks = editor.selection.blocks();
      var i;

      // Normalize blocks.
      for (i = 0; i < blocks.length; i++) {
        blocks[i] = _deepestParent(blocks[i]);
      }

      // Save selection to restore it later.
      editor.selection.save();

      var $quote = $('<blockquote>');
      $quote.insertBefore(blocks[0]);
      for (i = 0; i < blocks.length; i++) {
        $quote.append(blocks[i]);
      }

      // Unwrap temp divs.
      editor.html.unwrap();

      editor.selection.restore();
    }

    function _decrease () {
      // Get blocks.
      var blocks = editor.selection.blocks();
      var i;

      for (i = 0; i < blocks.length; i++) {
        if (blocks[i].tagName != 'BLOCKQUOTE') {
          blocks[i] = $(blocks[i]).parentsUntil(editor.$el, 'BLOCKQUOTE').get(0);
        }
      }

      editor.selection.save();

      for (i = 0; i < blocks.length; i++) {
        if (blocks[i]) {
          $(blocks[i]).replaceWith(blocks[i].innerHTML);
        }
      }

      // Unwrap temp divs.
      editor.html.unwrap();

      editor.selection.restore();
    }

    function apply (val) {
      // Wrap.
      editor.selection.save();
      editor.html.wrap(true, true, true, true);
      editor.selection.restore();

      if (val == 'increase') {
        _increase();
      }
      else if (val == 'decrease') {
        _decrease();
      }


    }

    return {
      apply: apply
    }
  }

  // Register the quote command.
  $.FE.RegisterShortcut($.FE.KEYCODE.SINGLE_QUOTE, 'quote', 'increase', '\'');
  $.FE.RegisterShortcut($.FE.KEYCODE.SINGLE_QUOTE, 'quote', 'decrease', '\'', true);
  $.FE.RegisterCommand('quote', {
    title: 'Quote',
    type: 'dropdown',
    options: {
      increase: 'Increase',
      decrease: 'Decrease'
    },
    callback: function (cmd, val) {
      this.quote.apply(val);
    },
    plugin: 'quote'
  })

  // Add the quote icon.
  $.FE.DefineIcon('quote', {
    NAME: 'quote-left'
  });

}));

};


require('main');
require('helpcenter/primary/main');

}({}));
