(function webpackUniversalModuleDefinition(root, factory) {
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory();
	else if(typeof define === 'function' && define.amd)
		define([], factory);
	else if(typeof exports === 'object')
		exports["runestone"] = factory();
	else
		root["runestone"] = factory();
})(window, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ 		}
/******/ 	};
/******/
/******/ 	// define __esModule on exports
/******/ 	__webpack_require__.r = function(exports) {
/******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 		}
/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
/******/ 	};
/******/
/******/ 	// create a fake namespace object
/******/ 	// mode & 1: value is a module id, require it
/******/ 	// mode & 2: merge all properties of value into the ns
/******/ 	// mode & 4: return value when already ns object
/******/ 	// mode & 8|1: behave like require
/******/ 	__webpack_require__.t = function(value, mode) {
/******/ 		if(mode & 1) value = __webpack_require__(value);
/******/ 		if(mode & 8) return value;
/******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ 		var ns = Object.create(null);
/******/ 		__webpack_require__.r(ns);
/******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ 		return ns;
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ({

/***/ "./node_modules/codemirror/lib/codemirror.css":
/*!****************************************************!*\
  !*** ./node_modules/codemirror/lib/codemirror.css ***!
  \****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var api = __webpack_require__(/*! ../../style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
            var content = __webpack_require__(/*! !../../css-loader/dist/cjs.js!./codemirror.css */ "./node_modules/css-loader/dist/cjs.js!./node_modules/codemirror/lib/codemirror.css");

            content = content.__esModule ? content.default : content;

            if (typeof content === 'string') {
              content = [[module.i, content, '']];
            }

var options = {};

options.insert = "head";
options.singleton = false;

var update = api(content, options);



module.exports = content.locals || {};

/***/ }),

/***/ "./node_modules/codemirror/lib/codemirror.js":
/*!***************************************************!*\
  !*** ./node_modules/codemirror/lib/codemirror.js ***!
  \***************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE

// This is CodeMirror (https://codemirror.net), a code editor
// implemented in JavaScript on top of the browser's DOM.
//
// You can find some technical background for some of the code below
// at http://marijnhaverbeke.nl/blog/#cm-internals .

(function (global, factory) {
   true ? module.exports = factory() :
  undefined;
}(this, (function () { 'use strict';

  // Kludges for bugs and behavior differences that can't be feature
  // detected are enabled based on userAgent etc sniffing.
  var userAgent = navigator.userAgent;
  var platform = navigator.platform;

  var gecko = /gecko\/\d/i.test(userAgent);
  var ie_upto10 = /MSIE \d/.test(userAgent);
  var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
  var edge = /Edge\/(\d+)/.exec(userAgent);
  var ie = ie_upto10 || ie_11up || edge;
  var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]);
  var webkit = !edge && /WebKit\//.test(userAgent);
  var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
  var chrome = !edge && /Chrome\//.test(userAgent);
  var presto = /Opera\//.test(userAgent);
  var safari = /Apple Computer/.test(navigator.vendor);
  var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
  var phantom = /PhantomJS/.test(userAgent);

  var ios = safari && (/Mobile\/\w+/.test(userAgent) || navigator.maxTouchPoints > 2);
  var android = /Android/.test(userAgent);
  // This is woefully incomplete. Suggestions for alternative methods welcome.
  var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
  var mac = ios || /Mac/.test(platform);
  var chromeOS = /\bCrOS\b/.test(userAgent);
  var windows = /win/i.test(platform);

  var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
  if (presto_version) { presto_version = Number(presto_version[1]); }
  if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
  // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
  var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
  var captureRightClick = gecko || (ie && ie_version >= 9);

  function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }

  var rmClass = function(node, cls) {
    var current = node.className;
    var match = classTest(cls).exec(current);
    if (match) {
      var after = current.slice(match.index + match[0].length);
      node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
    }
  };

  function removeChildren(e) {
    for (var count = e.childNodes.length; count > 0; --count)
      { e.removeChild(e.firstChild); }
    return e
  }

  function removeChildrenAndAdd(parent, e) {
    return removeChildren(parent).appendChild(e)
  }

  function elt(tag, content, className, style) {
    var e = document.createElement(tag);
    if (className) { e.className = className; }
    if (style) { e.style.cssText = style; }
    if (typeof content == "string") { e.appendChild(document.createTextNode(content)); }
    else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } }
    return e
  }
  // wrapper for elt, which removes the elt from the accessibility tree
  function eltP(tag, content, className, style) {
    var e = elt(tag, content, className, style);
    e.setAttribute("role", "presentation");
    return e
  }

  var range;
  if (document.createRange) { range = function(node, start, end, endNode) {
    var r = document.createRange();
    r.setEnd(endNode || node, end);
    r.setStart(node, start);
    return r
  }; }
  else { range = function(node, start, end) {
    var r = document.body.createTextRange();
    try { r.moveToElementText(node.parentNode); }
    catch(e) { return r }
    r.collapse(true);
    r.moveEnd("character", end);
    r.moveStart("character", start);
    return r
  }; }

  function contains(parent, child) {
    if (child.nodeType == 3) // Android browser always returns false when child is a textnode
      { child = child.parentNode; }
    if (parent.contains)
      { return parent.contains(child) }
    do {
      if (child.nodeType == 11) { child = child.host; }
      if (child == parent) { return true }
    } while (child = child.parentNode)
  }

  function activeElt() {
    // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
    // IE < 10 will throw when accessed while the page is loading or in an iframe.
    // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
    var activeElement;
    try {
      activeElement = document.activeElement;
    } catch(e) {
      activeElement = document.body || null;
    }
    while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
      { activeElement = activeElement.shadowRoot.activeElement; }
    return activeElement
  }

  function addClass(node, cls) {
    var current = node.className;
    if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; }
  }
  function joinClasses(a, b) {
    var as = a.split(" ");
    for (var i = 0; i < as.length; i++)
      { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } }
    return b
  }

  var selectInput = function(node) { node.select(); };
  if (ios) // Mobile Safari apparently has a bug where select() is broken.
    { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; }
  else if (ie) // Suppress mysterious IE10 errors
    { selectInput = function(node) { try { node.select(); } catch(_e) {} }; }

  function bind(f) {
    var args = Array.prototype.slice.call(arguments, 1);
    return function(){return f.apply(null, args)}
  }

  function copyObj(obj, target, overwrite) {
    if (!target) { target = {}; }
    for (var prop in obj)
      { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
        { target[prop] = obj[prop]; } }
    return target
  }

  // Counts the column offset in a string, taking tabs into account.
  // Used mostly to find indentation.
  function countColumn(string, end, tabSize, startIndex, startValue) {
    if (end == null) {
      end = string.search(/[^\s\u00a0]/);
      if (end == -1) { end = string.length; }
    }
    for (var i = startIndex || 0, n = startValue || 0;;) {
      var nextTab = string.indexOf("\t", i);
      if (nextTab < 0 || nextTab >= end)
        { return n + (end - i) }
      n += nextTab - i;
      n += tabSize - (n % tabSize);
      i = nextTab + 1;
    }
  }

  var Delayed = function() {
    this.id = null;
    this.f = null;
    this.time = 0;
    this.handler = bind(this.onTimeout, this);
  };
  Delayed.prototype.onTimeout = function (self) {
    self.id = 0;
    if (self.time <= +new Date) {
      self.f();
    } else {
      setTimeout(self.handler, self.time - +new Date);
    }
  };
  Delayed.prototype.set = function (ms, f) {
    this.f = f;
    var time = +new Date + ms;
    if (!this.id || time < this.time) {
      clearTimeout(this.id);
      this.id = setTimeout(this.handler, ms);
      this.time = time;
    }
  };

  function indexOf(array, elt) {
    for (var i = 0; i < array.length; ++i)
      { if (array[i] == elt) { return i } }
    return -1
  }

  // Number of pixels added to scroller and sizer to hide scrollbar
  var scrollerGap = 50;

  // Returned or thrown by various protocols to signal 'I'm not
  // handling this'.
  var Pass = {toString: function(){return "CodeMirror.Pass"}};

  // Reused option objects for setSelection & friends
  var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};

  // The inverse of countColumn -- find the offset that corresponds to
  // a particular column.
  function findColumn(string, goal, tabSize) {
    for (var pos = 0, col = 0;;) {
      var nextTab = string.indexOf("\t", pos);
      if (nextTab == -1) { nextTab = string.length; }
      var skipped = nextTab - pos;
      if (nextTab == string.length || col + skipped >= goal)
        { return pos + Math.min(skipped, goal - col) }
      col += nextTab - pos;
      col += tabSize - (col % tabSize);
      pos = nextTab + 1;
      if (col >= goal) { return pos }
    }
  }

  var spaceStrs = [""];
  function spaceStr(n) {
    while (spaceStrs.length <= n)
      { spaceStrs.push(lst(spaceStrs) + " "); }
    return spaceStrs[n]
  }

  function lst(arr) { return arr[arr.length-1] }

  function map(array, f) {
    var out = [];
    for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); }
    return out
  }

  function insertSorted(array, value, score) {
    var pos = 0, priority = score(value);
    while (pos < array.length && score(array[pos]) <= priority) { pos++; }
    array.splice(pos, 0, value);
  }

  function nothing() {}

  function createObj(base, props) {
    var inst;
    if (Object.create) {
      inst = Object.create(base);
    } else {
      nothing.prototype = base;
      inst = new nothing();
    }
    if (props) { copyObj(props, inst); }
    return inst
  }

  var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
  function isWordCharBasic(ch) {
    return /\w/.test(ch) || ch > "\x80" &&
      (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
  }
  function isWordChar(ch, helper) {
    if (!helper) { return isWordCharBasic(ch) }
    if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
    return helper.test(ch)
  }

  function isEmpty(obj) {
    for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
    return true
  }

  // Extending unicode characters. A series of a non-extending char +
  // any number of extending chars is treated as a single unit as far
  // as editing and measuring is concerned. This is not fully correct,
  // since some scripts/fonts/browsers also treat other configurations
  // of code points as a group.
  var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;
  function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }

  // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
  function skipExtendingChars(str, pos, dir) {
    while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; }
    return pos
  }

  // Returns the value from the range [`from`; `to`] that satisfies
  // `pred` and is closest to `from`. Assumes that at least `to`
  // satisfies `pred`. Supports `from` being greater than `to`.
  function findFirst(pred, from, to) {
    // At any point we are certain `to` satisfies `pred`, don't know
    // whether `from` does.
    var dir = from > to ? -1 : 1;
    for (;;) {
      if (from == to) { return from }
      var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF);
      if (mid == from) { return pred(mid) ? from : to }
      if (pred(mid)) { to = mid; }
      else { from = mid + dir; }
    }
  }

  // BIDI HELPERS

  function iterateBidiSections(order, from, to, f) {
    if (!order) { return f(from, to, "ltr", 0) }
    var found = false;
    for (var i = 0; i < order.length; ++i) {
      var part = order[i];
      if (part.from < to && part.to > from || from == to && part.to == from) {
        f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i);
        found = true;
      }
    }
    if (!found) { f(from, to, "ltr"); }
  }

  var bidiOther = null;
  function getBidiPartAt(order, ch, sticky) {
    var found;
    bidiOther = null;
    for (var i = 0; i < order.length; ++i) {
      var cur = order[i];
      if (cur.from < ch && cur.to > ch) { return i }
      if (cur.to == ch) {
        if (cur.from != cur.to && sticky == "before") { found = i; }
        else { bidiOther = i; }
      }
      if (cur.from == ch) {
        if (cur.from != cur.to && sticky != "before") { found = i; }
        else { bidiOther = i; }
      }
    }
    return found != null ? found : bidiOther
  }

  // Bidirectional ordering algorithm
  // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
  // that this (partially) implements.

  // One-char codes used for character types:
  // L (L):   Left-to-Right
  // R (R):   Right-to-Left
  // r (AL):  Right-to-Left Arabic
  // 1 (EN):  European Number
  // + (ES):  European Number Separator
  // % (ET):  European Number Terminator
  // n (AN):  Arabic Number
  // , (CS):  Common Number Separator
  // m (NSM): Non-Spacing Mark
  // b (BN):  Boundary Neutral
  // s (B):   Paragraph Separator
  // t (S):   Segment Separator
  // w (WS):  Whitespace
  // N (ON):  Other Neutrals

  // Returns null if characters are ordered as they appear
  // (left-to-right), or an array of sections ({from, to, level}
  // objects) in the order in which they occur visually.
  var bidiOrdering = (function() {
    // Character types for codepoints 0 to 0xff
    var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
    // Character types for codepoints 0x600 to 0x6f9
    var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111";
    function charType(code) {
      if (code <= 0xf7) { return lowTypes.charAt(code) }
      else if (0x590 <= code && code <= 0x5f4) { return "R" }
      else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
      else if (0x6ee <= code && code <= 0x8ac) { return "r" }
      else if (0x2000 <= code && code <= 0x200b) { return "w" }
      else if (code == 0x200c) { return "b" }
      else { return "L" }
    }

    var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
    var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;

    function BidiSpan(level, from, to) {
      this.level = level;
      this.from = from; this.to = to;
    }

    return function(str, direction) {
      var outerType = direction == "ltr" ? "L" : "R";

      if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
      var len = str.length, types = [];
      for (var i = 0; i < len; ++i)
        { types.push(charType(str.charCodeAt(i))); }

      // W1. Examine each non-spacing mark (NSM) in the level run, and
      // change the type of the NSM to the type of the previous
      // character. If the NSM is at the start of the level run, it will
      // get the type of sor.
      for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
        var type = types[i$1];
        if (type == "m") { types[i$1] = prev; }
        else { prev = type; }
      }

      // W2. Search backwards from each instance of a European number
      // until the first strong type (R, L, AL, or sor) is found. If an
      // AL is found, change the type of the European number to Arabic
      // number.
      // W3. Change all ALs to R.
      for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
        var type$1 = types[i$2];
        if (type$1 == "1" && cur == "r") { types[i$2] = "n"; }
        else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } }
      }

      // W4. A single European separator between two European numbers
      // changes to a European number. A single common separator between
      // two numbers of the same type changes to that type.
      for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
        var type$2 = types[i$3];
        if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; }
        else if (type$2 == "," && prev$1 == types[i$3+1] &&
                 (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; }
        prev$1 = type$2;
      }

      // W5. A sequence of European terminators adjacent to European
      // numbers changes to all European numbers.
      // W6. Otherwise, separators and terminators change to Other
      // Neutral.
      for (var i$4 = 0; i$4 < len; ++i$4) {
        var type$3 = types[i$4];
        if (type$3 == ",") { types[i$4] = "N"; }
        else if (type$3 == "%") {
          var end = (void 0);
          for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
          var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
          for (var j = i$4; j < end; ++j) { types[j] = replace; }
          i$4 = end - 1;
        }
      }

      // W7. Search backwards from each instance of a European number
      // until the first strong type (R, L, or sor) is found. If an L is
      // found, then change the type of the European number to L.
      for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
        var type$4 = types[i$5];
        if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; }
        else if (isStrong.test(type$4)) { cur$1 = type$4; }
      }

      // N1. A sequence of neutrals takes the direction of the
      // surrounding strong text if the text on both sides has the same
      // direction. European and Arabic numbers act as if they were R in
      // terms of their influence on neutrals. Start-of-level-run (sor)
      // and end-of-level-run (eor) are used at level run boundaries.
      // N2. Any remaining neutrals take the embedding direction.
      for (var i$6 = 0; i$6 < len; ++i$6) {
        if (isNeutral.test(types[i$6])) {
          var end$1 = (void 0);
          for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
          var before = (i$6 ? types[i$6-1] : outerType) == "L";
          var after = (end$1 < len ? types[end$1] : outerType) == "L";
          var replace$1 = before == after ? (before ? "L" : "R") : outerType;
          for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; }
          i$6 = end$1 - 1;
        }
      }

      // Here we depart from the documented algorithm, in order to avoid
      // building up an actual levels array. Since there are only three
      // levels (0, 1, 2) in an implementation that doesn't take
      // explicit embedding into account, we can build up the order on
      // the fly, without following the level-based algorithm.
      var order = [], m;
      for (var i$7 = 0; i$7 < len;) {
        if (countsAsLeft.test(types[i$7])) {
          var start = i$7;
          for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
          order.push(new BidiSpan(0, start, i$7));
        } else {
          var pos = i$7, at = order.length, isRTL = direction == "rtl" ? 1 : 0;
          for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
          for (var j$2 = pos; j$2 < i$7;) {
            if (countsAsNum.test(types[j$2])) {
              if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); at += isRTL; }
              var nstart = j$2;
              for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
              order.splice(at, 0, new BidiSpan(2, nstart, j$2));
              at += isRTL;
              pos = j$2;
            } else { ++j$2; }
          }
          if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); }
        }
      }
      if (direction == "ltr") {
        if (order[0].level == 1 && (m = str.match(/^\s+/))) {
          order[0].from = m[0].length;
          order.unshift(new BidiSpan(0, 0, m[0].length));
        }
        if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
          lst(order).to -= m[0].length;
          order.push(new BidiSpan(0, len - m[0].length, len));
        }
      }

      return direction == "rtl" ? order.reverse() : order
    }
  })();

  // Get the bidi ordering for the given line (and cache it). Returns
  // false for lines that are fully left-to-right, and an array of
  // BidiSpan objects otherwise.
  function getOrder(line, direction) {
    var order = line.order;
    if (order == null) { order = line.order = bidiOrdering(line.text, direction); }
    return order
  }

  // EVENT HANDLING

  // Lightweight event framework. on/off also work on DOM nodes,
  // registering native DOM handlers.

  var noHandlers = [];

  var on = function(emitter, type, f) {
    if (emitter.addEventListener) {
      emitter.addEventListener(type, f, false);
    } else if (emitter.attachEvent) {
      emitter.attachEvent("on" + type, f);
    } else {
      var map = emitter._handlers || (emitter._handlers = {});
      map[type] = (map[type] || noHandlers).concat(f);
    }
  };

  function getHandlers(emitter, type) {
    return emitter._handlers && emitter._handlers[type] || noHandlers
  }

  function off(emitter, type, f) {
    if (emitter.removeEventListener) {
      emitter.removeEventListener(type, f, false);
    } else if (emitter.detachEvent) {
      emitter.detachEvent("on" + type, f);
    } else {
      var map = emitter._handlers, arr = map && map[type];
      if (arr) {
        var index = indexOf(arr, f);
        if (index > -1)
          { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)); }
      }
    }
  }

  function signal(emitter, type /*, values...*/) {
    var handlers = getHandlers(emitter, type);
    if (!handlers.length) { return }
    var args = Array.prototype.slice.call(arguments, 2);
    for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); }
  }

  // The DOM events that CodeMirror handles can be overridden by
  // registering a (non-DOM) handler on the editor for the event name,
  // and preventDefault-ing the event in that handler.
  function signalDOMEvent(cm, e, override) {
    if (typeof e == "string")
      { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; }
    signal(cm, override || e.type, cm, e);
    return e_defaultPrevented(e) || e.codemirrorIgnore
  }

  function signalCursorActivity(cm) {
    var arr = cm._handlers && cm._handlers.cursorActivity;
    if (!arr) { return }
    var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
    for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
      { set.push(arr[i]); } }
  }

  function hasHandler(emitter, type) {
    return getHandlers(emitter, type).length > 0
  }

  // Add on and off methods to a constructor's prototype, to make
  // registering events on such objects more convenient.
  function eventMixin(ctor) {
    ctor.prototype.on = function(type, f) {on(this, type, f);};
    ctor.prototype.off = function(type, f) {off(this, type, f);};
  }

  // Due to the fact that we still support jurassic IE versions, some
  // compatibility wrappers are needed.

  function e_preventDefault(e) {
    if (e.preventDefault) { e.preventDefault(); }
    else { e.returnValue = false; }
  }
  function e_stopPropagation(e) {
    if (e.stopPropagation) { e.stopPropagation(); }
    else { e.cancelBubble = true; }
  }
  function e_defaultPrevented(e) {
    return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
  }
  function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}

  function e_target(e) {return e.target || e.srcElement}
  function e_button(e) {
    var b = e.which;
    if (b == null) {
      if (e.button & 1) { b = 1; }
      else if (e.button & 2) { b = 3; }
      else if (e.button & 4) { b = 2; }
    }
    if (mac && e.ctrlKey && b == 1) { b = 3; }
    return b
  }

  // Detect drag-and-drop
  var dragAndDrop = function() {
    // There is *some* kind of drag-and-drop support in IE6-8, but I
    // couldn't get it to work yet.
    if (ie && ie_version < 9) { return false }
    var div = elt('div');
    return "draggable" in div || "dragDrop" in div
  }();

  var zwspSupported;
  function zeroWidthElement(measure) {
    if (zwspSupported == null) {
      var test = elt("span", "\u200b");
      removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
      if (measure.firstChild.offsetHeight != 0)
        { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); }
    }
    var node = zwspSupported ? elt("span", "\u200b") :
      elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
    node.setAttribute("cm-text", "");
    return node
  }

  // Feature-detect IE's crummy client rect reporting for bidi text
  var badBidiRects;
  function hasBadBidiRects(measure) {
    if (badBidiRects != null) { return badBidiRects }
    var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
    var r0 = range(txt, 0, 1).getBoundingClientRect();
    var r1 = range(txt, 1, 2).getBoundingClientRect();
    removeChildren(measure);
    if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
    return badBidiRects = (r1.right - r0.right < 3)
  }

  // See if "".split is the broken IE version, if so, provide an
  // alternative way to split lines.
  var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
    var pos = 0, result = [], l = string.length;
    while (pos <= l) {
      var nl = string.indexOf("\n", pos);
      if (nl == -1) { nl = string.length; }
      var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
      var rt = line.indexOf("\r");
      if (rt != -1) {
        result.push(line.slice(0, rt));
        pos += rt + 1;
      } else {
        result.push(line);
        pos = nl + 1;
      }
    }
    return result
  } : function (string) { return string.split(/\r\n?|\n/); };

  var hasSelection = window.getSelection ? function (te) {
    try { return te.selectionStart != te.selectionEnd }
    catch(e) { return false }
  } : function (te) {
    var range;
    try {range = te.ownerDocument.selection.createRange();}
    catch(e) {}
    if (!range || range.parentElement() != te) { return false }
    return range.compareEndPoints("StartToEnd", range) != 0
  };

  var hasCopyEvent = (function () {
    var e = elt("div");
    if ("oncopy" in e) { return true }
    e.setAttribute("oncopy", "return;");
    return typeof e.oncopy == "function"
  })();

  var badZoomedRects = null;
  function hasBadZoomedRects(measure) {
    if (badZoomedRects != null) { return badZoomedRects }
    var node = removeChildrenAndAdd(measure, elt("span", "x"));
    var normal = node.getBoundingClientRect();
    var fromRange = range(node, 0, 1).getBoundingClientRect();
    return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
  }

  // Known modes, by name and by MIME
  var modes = {}, mimeModes = {};

  // Extra arguments are stored as the mode's dependencies, which is
  // used by (legacy) mechanisms like loadmode.js to automatically
  // load a mode. (Preferred mechanism is the require/define calls.)
  function defineMode(name, mode) {
    if (arguments.length > 2)
      { mode.dependencies = Array.prototype.slice.call(arguments, 2); }
    modes[name] = mode;
  }

  function defineMIME(mime, spec) {
    mimeModes[mime] = spec;
  }

  // Given a MIME type, a {name, ...options} config object, or a name
  // string, return a mode config object.
  function resolveMode(spec) {
    if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
      spec = mimeModes[spec];
    } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
      var found = mimeModes[spec.name];
      if (typeof found == "string") { found = {name: found}; }
      spec = createObj(found, spec);
      spec.name = found.name;
    } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
      return resolveMode("application/xml")
    } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
      return resolveMode("application/json")
    }
    if (typeof spec == "string") { return {name: spec} }
    else { return spec || {name: "null"} }
  }

  // Given a mode spec (anything that resolveMode accepts), find and
  // initialize an actual mode object.
  function getMode(options, spec) {
    spec = resolveMode(spec);
    var mfactory = modes[spec.name];
    if (!mfactory) { return getMode(options, "text/plain") }
    var modeObj = mfactory(options, spec);
    if (modeExtensions.hasOwnProperty(spec.name)) {
      var exts = modeExtensions[spec.name];
      for (var prop in exts) {
        if (!exts.hasOwnProperty(prop)) { continue }
        if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
        modeObj[prop] = exts[prop];
      }
    }
    modeObj.name = spec.name;
    if (spec.helperType) { modeObj.helperType = spec.helperType; }
    if (spec.modeProps) { for (var prop$1 in spec.modeProps)
      { modeObj[prop$1] = spec.modeProps[prop$1]; } }

    return modeObj
  }

  // This can be used to attach properties to mode objects from
  // outside the actual mode definition.
  var modeExtensions = {};
  function extendMode(mode, properties) {
    var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
    copyObj(properties, exts);
  }

  function copyState(mode, state) {
    if (state === true) { return state }
    if (mode.copyState) { return mode.copyState(state) }
    var nstate = {};
    for (var n in state) {
      var val = state[n];
      if (val instanceof Array) { val = val.concat([]); }
      nstate[n] = val;
    }
    return nstate
  }

  // Given a mode and a state (for that mode), find the inner mode and
  // state at the position that the state refers to.
  function innerMode(mode, state) {
    var info;
    while (mode.innerMode) {
      info = mode.innerMode(state);
      if (!info || info.mode == mode) { break }
      state = info.state;
      mode = info.mode;
    }
    return info || {mode: mode, state: state}
  }

  function startState(mode, a1, a2) {
    return mode.startState ? mode.startState(a1, a2) : true
  }

  // STRING STREAM

  // Fed to the mode parsers, provides helper functions to make
  // parsers more succinct.

  var StringStream = function(string, tabSize, lineOracle) {
    this.pos = this.start = 0;
    this.string = string;
    this.tabSize = tabSize || 8;
    this.lastColumnPos = this.lastColumnValue = 0;
    this.lineStart = 0;
    this.lineOracle = lineOracle;
  };

  StringStream.prototype.eol = function () {return this.pos >= this.string.length};
  StringStream.prototype.sol = function () {return this.pos == this.lineStart};
  StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
  StringStream.prototype.next = function () {
    if (this.pos < this.string.length)
      { return this.string.charAt(this.pos++) }
  };
  StringStream.prototype.eat = function (match) {
    var ch = this.string.charAt(this.pos);
    var ok;
    if (typeof match == "string") { ok = ch == match; }
    else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
    if (ok) {++this.pos; return ch}
  };
  StringStream.prototype.eatWhile = function (match) {
    var start = this.pos;
    while (this.eat(match)){}
    return this.pos > start
  };
  StringStream.prototype.eatSpace = function () {
    var start = this.pos;
    while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; }
    return this.pos > start
  };
  StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
  StringStream.prototype.skipTo = function (ch) {
    var found = this.string.indexOf(ch, this.pos);
    if (found > -1) {this.pos = found; return true}
  };
  StringStream.prototype.backUp = function (n) {this.pos -= n;};
  StringStream.prototype.column = function () {
    if (this.lastColumnPos < this.start) {
      this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
      this.lastColumnPos = this.start;
    }
    return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
  };
  StringStream.prototype.indentation = function () {
    return countColumn(this.string, null, this.tabSize) -
      (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
  };
  StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
    if (typeof pattern == "string") {
      var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
      var substr = this.string.substr(this.pos, pattern.length);
      if (cased(substr) == cased(pattern)) {
        if (consume !== false) { this.pos += pattern.length; }
        return true
      }
    } else {
      var match = this.string.slice(this.pos).match(pattern);
      if (match && match.index > 0) { return null }
      if (match && consume !== false) { this.pos += match[0].length; }
      return match
    }
  };
  StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
  StringStream.prototype.hideFirstChars = function (n, inner) {
    this.lineStart += n;
    try { return inner() }
    finally { this.lineStart -= n; }
  };
  StringStream.prototype.lookAhead = function (n) {
    var oracle = this.lineOracle;
    return oracle && oracle.lookAhead(n)
  };
  StringStream.prototype.baseToken = function () {
    var oracle = this.lineOracle;
    return oracle && oracle.baseToken(this.pos)
  };

  // Find the line object corresponding to the given line number.
  function getLine(doc, n) {
    n -= doc.first;
    if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
    var chunk = doc;
    while (!chunk.lines) {
      for (var i = 0;; ++i) {
        var child = chunk.children[i], sz = child.chunkSize();
        if (n < sz) { chunk = child; break }
        n -= sz;
      }
    }
    return chunk.lines[n]
  }

  // Get the part of a document between two positions, as an array of
  // strings.
  function getBetween(doc, start, end) {
    var out = [], n = start.line;
    doc.iter(start.line, end.line + 1, function (line) {
      var text = line.text;
      if (n == end.line) { text = text.slice(0, end.ch); }
      if (n == start.line) { text = text.slice(start.ch); }
      out.push(text);
      ++n;
    });
    return out
  }
  // Get the lines between from and to, as array of strings.
  function getLines(doc, from, to) {
    var out = [];
    doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value
    return out
  }

  // Update the height of a line, propagating the height change
  // upwards to parent nodes.
  function updateLineHeight(line, height) {
    var diff = height - line.height;
    if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } }
  }

  // Given a line object, find its line number by walking up through
  // its parent links.
  function lineNo(line) {
    if (line.parent == null) { return null }
    var cur = line.parent, no = indexOf(cur.lines, line);
    for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
      for (var i = 0;; ++i) {
        if (chunk.children[i] == cur) { break }
        no += chunk.children[i].chunkSize();
      }
    }
    return no + cur.first
  }

  // Find the line at the given vertical position, using the height
  // information in the document tree.
  function lineAtHeight(chunk, h) {
    var n = chunk.first;
    outer: do {
      for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
        var child = chunk.children[i$1], ch = child.height;
        if (h < ch) { chunk = child; continue outer }
        h -= ch;
        n += child.chunkSize();
      }
      return n
    } while (!chunk.lines)
    var i = 0;
    for (; i < chunk.lines.length; ++i) {
      var line = chunk.lines[i], lh = line.height;
      if (h < lh) { break }
      h -= lh;
    }
    return n + i
  }

  function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}

  function lineNumberFor(options, i) {
    return String(options.lineNumberFormatter(i + options.firstLineNumber))
  }

  // A Pos instance represents a position within the text.
  function Pos(line, ch, sticky) {
    if ( sticky === void 0 ) sticky = null;

    if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
    this.line = line;
    this.ch = ch;
    this.sticky = sticky;
  }

  // Compare two positions, return 0 if they are the same, a negative
  // number when a is less, and a positive number otherwise.
  function cmp(a, b) { return a.line - b.line || a.ch - b.ch }

  function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }

  function copyPos(x) {return Pos(x.line, x.ch)}
  function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
  function minPos(a, b) { return cmp(a, b) < 0 ? a : b }

  // Most of the external API clips given positions to make sure they
  // actually exist within the document.
  function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
  function clipPos(doc, pos) {
    if (pos.line < doc.first) { return Pos(doc.first, 0) }
    var last = doc.first + doc.size - 1;
    if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
    return clipToLen(pos, getLine(doc, pos.line).text.length)
  }
  function clipToLen(pos, linelen) {
    var ch = pos.ch;
    if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
    else if (ch < 0) { return Pos(pos.line, 0) }
    else { return pos }
  }
  function clipPosArray(doc, array) {
    var out = [];
    for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); }
    return out
  }

  var SavedContext = function(state, lookAhead) {
    this.state = state;
    this.lookAhead = lookAhead;
  };

  var Context = function(doc, state, line, lookAhead) {
    this.state = state;
    this.doc = doc;
    this.line = line;
    this.maxLookAhead = lookAhead || 0;
    this.baseTokens = null;
    this.baseTokenPos = 1;
  };

  Context.prototype.lookAhead = function (n) {
    var line = this.doc.getLine(this.line + n);
    if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; }
    return line
  };

  Context.prototype.baseToken = function (n) {
    if (!this.baseTokens) { return null }
    while (this.baseTokens[this.baseTokenPos] <= n)
      { this.baseTokenPos += 2; }
    var type = this.baseTokens[this.baseTokenPos + 1];
    return {type: type && type.replace(/( |^)overlay .*/, ""),
            size: this.baseTokens[this.baseTokenPos] - n}
  };

  Context.prototype.nextLine = function () {
    this.line++;
    if (this.maxLookAhead > 0) { this.maxLookAhead--; }
  };

  Context.fromSaved = function (doc, saved, line) {
    if (saved instanceof SavedContext)
      { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }
    else
      { return new Context(doc, copyState(doc.mode, saved), line) }
  };

  Context.prototype.save = function (copy) {
    var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state;
    return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
  };


  // Compute a style array (an array starting with a mode generation
  // -- for invalidation -- followed by pairs of end positions and
  // style strings), which is used to highlight the tokens on the
  // line.
  function highlightLine(cm, line, context, forceToEnd) {
    // A styles array always starts with a number identifying the
    // mode/overlays that it is based on (for easy invalidation).
    var st = [cm.state.modeGen], lineClasses = {};
    // Compute the base array of styles
    runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },
            lineClasses, forceToEnd);
    var state = context.state;

    // Run overlays, adjust style array.
    var loop = function ( o ) {
      context.baseTokens = st;
      var overlay = cm.state.overlays[o], i = 1, at = 0;
      context.state = true;
      runMode(cm, line.text, overlay.mode, context, function (end, style) {
        var start = i;
        // Ensure there's a token end at the current position, and that i points at it
        while (at < end) {
          var i_end = st[i];
          if (i_end > end)
            { st.splice(i, 1, end, st[i+1], i_end); }
          i += 2;
          at = Math.min(end, i_end);
        }
        if (!style) { return }
        if (overlay.opaque) {
          st.splice(start, i - start, end, "overlay " + style);
          i = start + 2;
        } else {
          for (; start < i; start += 2) {
            var cur = st[start+1];
            st[start+1] = (cur ? cur + " " : "") + "overlay " + style;
          }
        }
      }, lineClasses);
      context.state = state;
      context.baseTokens = null;
      context.baseTokenPos = 1;
    };

    for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );

    return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
  }

  function getLineStyles(cm, line, updateFrontier) {
    if (!line.styles || line.styles[0] != cm.state.modeGen) {
      var context = getContextBefore(cm, lineNo(line));
      var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state);
      var result = highlightLine(cm, line, context);
      if (resetState) { context.state = resetState; }
      line.stateAfter = context.save(!resetState);
      line.styles = result.styles;
      if (result.classes) { line.styleClasses = result.classes; }
      else if (line.styleClasses) { line.styleClasses = null; }
      if (updateFrontier === cm.doc.highlightFrontier)
        { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); }
    }
    return line.styles
  }

  function getContextBefore(cm, n, precise) {
    var doc = cm.doc, display = cm.display;
    if (!doc.mode.startState) { return new Context(doc, true, n) }
    var start = findStartLine(cm, n, precise);
    var saved = start > doc.first && getLine(doc, start - 1).stateAfter;
    var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start);

    doc.iter(start, n, function (line) {
      processLine(cm, line.text, context);
      var pos = context.line;
      line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null;
      context.nextLine();
    });
    if (precise) { doc.modeFrontier = context.line; }
    return context
  }

  // Lightweight form of highlight -- proceed over this line and
  // update state, but don't save a style array. Used for lines that
  // aren't currently visible.
  function processLine(cm, text, context, startAt) {
    var mode = cm.doc.mode;
    var stream = new StringStream(text, cm.options.tabSize, context);
    stream.start = stream.pos = startAt || 0;
    if (text == "") { callBlankLine(mode, context.state); }
    while (!stream.eol()) {
      readToken(mode, stream, context.state);
      stream.start = stream.pos;
    }
  }

  function callBlankLine(mode, state) {
    if (mode.blankLine) { return mode.blankLine(state) }
    if (!mode.innerMode) { return }
    var inner = innerMode(mode, state);
    if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
  }

  function readToken(mode, stream, state, inner) {
    for (var i = 0; i < 10; i++) {
      if (inner) { inner[0] = innerMode(mode, state).mode; }
      var style = mode.token(stream, state);
      if (stream.pos > stream.start) { return style }
    }
    throw new Error("Mode " + mode.name + " failed to advance stream.")
  }

  var Token = function(stream, type, state) {
    this.start = stream.start; this.end = stream.pos;
    this.string = stream.current();
    this.type = type || null;
    this.state = state;
  };

  // Utility for getTokenAt and getLineTokens
  function takeToken(cm, pos, precise, asArray) {
    var doc = cm.doc, mode = doc.mode, style;
    pos = clipPos(doc, pos);
    var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);
    var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;
    if (asArray) { tokens = []; }
    while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
      stream.start = stream.pos;
      style = readToken(mode, stream, context.state);
      if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); }
    }
    return asArray ? tokens : new Token(stream, style, context.state)
  }

  function extractLineClasses(type, output) {
    if (type) { for (;;) {
      var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
      if (!lineClass) { break }
      type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
      var prop = lineClass[1] ? "bgClass" : "textClass";
      if (output[prop] == null)
        { output[prop] = lineClass[2]; }
      else if (!(new RegExp("(?:^|\\s)" + lineClass[2] + "(?:$|\\s)")).test(output[prop]))
        { output[prop] += " " + lineClass[2]; }
    } }
    return type
  }

  // Run the given mode's parser over a line, calling f for each token.
  function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
    var flattenSpans = mode.flattenSpans;
    if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; }
    var curStart = 0, curStyle = null;
    var stream = new StringStream(text, cm.options.tabSize, context), style;
    var inner = cm.options.addModeClass && [null];
    if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); }
    while (!stream.eol()) {
      if (stream.pos > cm.options.maxHighlightLength) {
        flattenSpans = false;
        if (forceToEnd) { processLine(cm, text, context, stream.pos); }
        stream.pos = text.length;
        style = null;
      } else {
        style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses);
      }
      if (inner) {
        var mName = inner[0].name;
        if (mName) { style = "m-" + (style ? mName + " " + style : mName); }
      }
      if (!flattenSpans || curStyle != style) {
        while (curStart < stream.start) {
          curStart = Math.min(stream.start, curStart + 5000);
          f(curStart, curStyle);
        }
        curStyle = style;
      }
      stream.start = stream.pos;
    }
    while (curStart < stream.pos) {
      // Webkit seems to refuse to render text nodes longer than 57444
      // characters, and returns inaccurate measurements in nodes
      // starting around 5000 chars.
      var pos = Math.min(stream.pos, curStart + 5000);
      f(pos, curStyle);
      curStart = pos;
    }
  }

  // Finds the line to start with when starting a parse. Tries to
  // find a line with a stateAfter, so that it can start with a
  // valid state. If that fails, it returns the line with the
  // smallest indentation, which tends to need the least context to
  // parse correctly.
  function findStartLine(cm, n, precise) {
    var minindent, minline, doc = cm.doc;
    var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
    for (var search = n; search > lim; --search) {
      if (search <= doc.first) { return doc.first }
      var line = getLine(doc, search - 1), after = line.stateAfter;
      if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))
        { return search }
      var indented = countColumn(line.text, null, cm.options.tabSize);
      if (minline == null || minindent > indented) {
        minline = search - 1;
        minindent = indented;
      }
    }
    return minline
  }

  function retreatFrontier(doc, n) {
    doc.modeFrontier = Math.min(doc.modeFrontier, n);
    if (doc.highlightFrontier < n - 10) { return }
    var start = doc.first;
    for (var line = n - 1; line > start; line--) {
      var saved = getLine(doc, line).stateAfter;
      // change is on 3
      // state on line 1 looked ahead 2 -- so saw 3
      // test 1 + 2 < 3 should cover this
      if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
        start = line + 1;
        break
      }
    }
    doc.highlightFrontier = Math.min(doc.highlightFrontier, start);
  }

  // Optimize some code when these features are not used.
  var sawReadOnlySpans = false, sawCollapsedSpans = false;

  function seeReadOnlySpans() {
    sawReadOnlySpans = true;
  }

  function seeCollapsedSpans() {
    sawCollapsedSpans = true;
  }

  // TEXTMARKER SPANS

  function MarkedSpan(marker, from, to) {
    this.marker = marker;
    this.from = from; this.to = to;
  }

  // Search an array of spans for a span matching the given marker.
  function getMarkedSpanFor(spans, marker) {
    if (spans) { for (var i = 0; i < spans.length; ++i) {
      var span = spans[i];
      if (span.marker == marker) { return span }
    } }
  }
  // Remove a span from an array, returning undefined if no spans are
  // left (we don't store arrays for lines without spans).
  function removeMarkedSpan(spans, span) {
    var r;
    for (var i = 0; i < spans.length; ++i)
      { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } }
    return r
  }
  // Add a span to a line.
  function addMarkedSpan(line, span) {
    line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
    span.marker.attachLine(line);
  }

  // Used for the algorithm that adjusts markers for a change in the
  // document. These functions cut an array of spans at a given
  // character position, returning an array of remaining chunks (or
  // undefined if nothing remains).
  function markedSpansBefore(old, startCh, isInsert) {
    var nw;
    if (old) { for (var i = 0; i < old.length; ++i) {
      var span = old[i], marker = span.marker;
      var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
      if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
        var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)
        ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
      }
    } }
    return nw
  }
  function markedSpansAfter(old, endCh, isInsert) {
    var nw;
    if (old) { for (var i = 0; i < old.length; ++i) {
      var span = old[i], marker = span.marker;
      var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
      if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
        var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)
        ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
                                              span.to == null ? null : span.to - endCh));
      }
    } }
    return nw
  }

  // Given a change object, compute the new set of marker spans that
  // cover the line in which the change took place. Removes spans
  // entirely within the change, reconnects spans belonging to the
  // same marker that appear on both sides of the change, and cuts off
  // spans partially within the change. Returns an array of span
  // arrays with one element for each line in (after) the change.
  function stretchSpansOverChange(doc, change) {
    if (change.full) { return null }
    var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
    var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
    if (!oldFirst && !oldLast) { return null }

    var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
    // Get the spans that 'stick out' on both sides
    var first = markedSpansBefore(oldFirst, startCh, isInsert);
    var last = markedSpansAfter(oldLast, endCh, isInsert);

    // Next, merge those two ends
    var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
    if (first) {
      // Fix up .to properties of first
      for (var i = 0; i < first.length; ++i) {
        var span = first[i];
        if (span.to == null) {
          var found = getMarkedSpanFor(last, span.marker);
          if (!found) { span.to = startCh; }
          else if (sameLine) { span.to = found.to == null ? null : found.to + offset; }
        }
      }
    }
    if (last) {
      // Fix up .from in last (or move them into first in case of sameLine)
      for (var i$1 = 0; i$1 < last.length; ++i$1) {
        var span$1 = last[i$1];
        if (span$1.to != null) { span$1.to += offset; }
        if (span$1.from == null) {
          var found$1 = getMarkedSpanFor(first, span$1.marker);
          if (!found$1) {
            span$1.from = offset;
            if (sameLine) { (first || (first = [])).push(span$1); }
          }
        } else {
          span$1.from += offset;
          if (sameLine) { (first || (first = [])).push(span$1); }
        }
      }
    }
    // Make sure we didn't create any zero-length spans
    if (first) { first = clearEmptySpans(first); }
    if (last && last != first) { last = clearEmptySpans(last); }

    var newMarkers = [first];
    if (!sameLine) {
      // Fill gap with whole-line-spans
      var gap = change.text.length - 2, gapMarkers;
      if (gap > 0 && first)
        { for (var i$2 = 0; i$2 < first.length; ++i$2)
          { if (first[i$2].to == null)
            { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } }
      for (var i$3 = 0; i$3 < gap; ++i$3)
        { newMarkers.push(gapMarkers); }
      newMarkers.push(last);
    }
    return newMarkers
  }

  // Remove spans that are empty and don't have a clearWhenEmpty
  // option of false.
  function clearEmptySpans(spans) {
    for (var i = 0; i < spans.length; ++i) {
      var span = spans[i];
      if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
        { spans.splice(i--, 1); }
    }
    if (!spans.length) { return null }
    return spans
  }

  // Used to 'clip' out readOnly ranges when making a change.
  function removeReadOnlyRanges(doc, from, to) {
    var markers = null;
    doc.iter(from.line, to.line + 1, function (line) {
      if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
        var mark = line.markedSpans[i].marker;
        if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
          { (markers || (markers = [])).push(mark); }
      } }
    });
    if (!markers) { return null }
    var parts = [{from: from, to: to}];
    for (var i = 0; i < markers.length; ++i) {
      var mk = markers[i], m = mk.find(0);
      for (var j = 0; j < parts.length; ++j) {
        var p = parts[j];
        if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
        var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
        if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
          { newParts.push({from: p.from, to: m.from}); }
        if (dto > 0 || !mk.inclusiveRight && !dto)
          { newParts.push({from: m.to, to: p.to}); }
        parts.splice.apply(parts, newParts);
        j += newParts.length - 3;
      }
    }
    return parts
  }

  // Connect or disconnect spans from a line.
  function detachMarkedSpans(line) {
    var spans = line.markedSpans;
    if (!spans) { return }
    for (var i = 0; i < spans.length; ++i)
      { spans[i].marker.detachLine(line); }
    line.markedSpans = null;
  }
  function attachMarkedSpans(line, spans) {
    if (!spans) { return }
    for (var i = 0; i < spans.length; ++i)
      { spans[i].marker.attachLine(line); }
    line.markedSpans = spans;
  }

  // Helpers used when computing which overlapping collapsed span
  // counts as the larger one.
  function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
  function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }

  // Returns a number indicating which of two overlapping collapsed
  // spans is larger (and thus includes the other). Falls back to
  // comparing ids when the spans cover exactly the same range.
  function compareCollapsedMarkers(a, b) {
    var lenDiff = a.lines.length - b.lines.length;
    if (lenDiff != 0) { return lenDiff }
    var aPos = a.find(), bPos = b.find();
    var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
    if (fromCmp) { return -fromCmp }
    var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
    if (toCmp) { return toCmp }
    return b.id - a.id
  }

  // Find out whether a line ends or starts in a collapsed span. If
  // so, return the marker for that span.
  function collapsedSpanAtSide(line, start) {
    var sps = sawCollapsedSpans && line.markedSpans, found;
    if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
      sp = sps[i];
      if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
          (!found || compareCollapsedMarkers(found, sp.marker) < 0))
        { found = sp.marker; }
    } }
    return found
  }
  function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
  function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }

  function collapsedSpanAround(line, ch) {
    var sps = sawCollapsedSpans && line.markedSpans, found;
    if (sps) { for (var i = 0; i < sps.length; ++i) {
      var sp = sps[i];
      if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) &&
          (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; }
    } }
    return found
  }

  // Test whether there exists a collapsed span that partially
  // overlaps (covers the start or end, but not both) of a new span.
  // Such overlap is not allowed.
  function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
    var line = getLine(doc, lineNo);
    var sps = sawCollapsedSpans && line.markedSpans;
    if (sps) { for (var i = 0; i < sps.length; ++i) {
      var sp = sps[i];
      if (!sp.marker.collapsed) { continue }
      var found = sp.marker.find(0);
      var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
      var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
      if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
      if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
          fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
        { return true }
    } }
  }

  // A visual line is a line as drawn on the screen. Folding, for
  // example, can cause multiple logical lines to appear on the same
  // visual line. This finds the start of the visual line that the
  // given line is part of (usually that is the line itself).
  function visualLine(line) {
    var merged;
    while (merged = collapsedSpanAtStart(line))
      { line = merged.find(-1, true).line; }
    return line
  }

  function visualLineEnd(line) {
    var merged;
    while (merged = collapsedSpanAtEnd(line))
      { line = merged.find(1, true).line; }
    return line
  }

  // Returns an array of logical lines that continue the visual line
  // started by the argument, or undefined if there are no such lines.
  function visualLineContinued(line) {
    var merged, lines;
    while (merged = collapsedSpanAtEnd(line)) {
      line = merged.find(1, true).line
      ;(lines || (lines = [])).push(line);
    }
    return lines
  }

  // Get the line number of the start of the visual line that the
  // given line number is part of.
  function visualLineNo(doc, lineN) {
    var line = getLine(doc, lineN), vis = visualLine(line);
    if (line == vis) { return lineN }
    return lineNo(vis)
  }

  // Get the line number of the start of the next visual line after
  // the given line.
  function visualLineEndNo(doc, lineN) {
    if (lineN > doc.lastLine()) { return lineN }
    var line = getLine(doc, lineN), merged;
    if (!lineIsHidden(doc, line)) { return lineN }
    while (merged = collapsedSpanAtEnd(line))
      { line = merged.find(1, true).line; }
    return lineNo(line) + 1
  }

  // Compute whether a line is hidden. Lines count as hidden when they
  // are part of a visual line that starts with another line, or when
  // they are entirely covered by collapsed, non-widget span.
  function lineIsHidden(doc, line) {
    var sps = sawCollapsedSpans && line.markedSpans;
    if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
      sp = sps[i];
      if (!sp.marker.collapsed) { continue }
      if (sp.from == null) { return true }
      if (sp.marker.widgetNode) { continue }
      if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
        { return true }
    } }
  }
  function lineIsHiddenInner(doc, line, span) {
    if (span.to == null) {
      var end = span.marker.find(1, true);
      return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
    }
    if (span.marker.inclusiveRight && span.to == line.text.length)
      { return true }
    for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
      sp = line.markedSpans[i];
      if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
          (sp.to == null || sp.to != span.from) &&
          (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
          lineIsHiddenInner(doc, line, sp)) { return true }
    }
  }

  // Find the height above the given line.
  function heightAtLine(lineObj) {
    lineObj = visualLine(lineObj);

    var h = 0, chunk = lineObj.parent;
    for (var i = 0; i < chunk.lines.length; ++i) {
      var line = chunk.lines[i];
      if (line == lineObj) { break }
      else { h += line.height; }
    }
    for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
      for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
        var cur = p.children[i$1];
        if (cur == chunk) { break }
        else { h += cur.height; }
      }
    }
    return h
  }

  // Compute the character length of a line, taking into account
  // collapsed ranges (see markText) that might hide parts, and join
  // other lines onto it.
  function lineLength(line) {
    if (line.height == 0) { return 0 }
    var len = line.text.length, merged, cur = line;
    while (merged = collapsedSpanAtStart(cur)) {
      var found = merged.find(0, true);
      cur = found.from.line;
      len += found.from.ch - found.to.ch;
    }
    cur = line;
    while (merged = collapsedSpanAtEnd(cur)) {
      var found$1 = merged.find(0, true);
      len -= cur.text.length - found$1.from.ch;
      cur = found$1.to.line;
      len += cur.text.length - found$1.to.ch;
    }
    return len
  }

  // Find the longest line in the document.
  function findMaxLine(cm) {
    var d = cm.display, doc = cm.doc;
    d.maxLine = getLine(doc, doc.first);
    d.maxLineLength = lineLength(d.maxLine);
    d.maxLineChanged = true;
    doc.iter(function (line) {
      var len = lineLength(line);
      if (len > d.maxLineLength) {
        d.maxLineLength = len;
        d.maxLine = line;
      }
    });
  }

  // LINE DATA STRUCTURE

  // Line objects. These hold state related to a line, including
  // highlighting info (the styles array).
  var Line = function(text, markedSpans, estimateHeight) {
    this.text = text;
    attachMarkedSpans(this, markedSpans);
    this.height = estimateHeight ? estimateHeight(this) : 1;
  };

  Line.prototype.lineNo = function () { return lineNo(this) };
  eventMixin(Line);

  // Change the content (text, markers) of a line. Automatically
  // invalidates cached information and tries to re-estimate the
  // line's height.
  function updateLine(line, text, markedSpans, estimateHeight) {
    line.text = text;
    if (line.stateAfter) { line.stateAfter = null; }
    if (line.styles) { line.styles = null; }
    if (line.order != null) { line.order = null; }
    detachMarkedSpans(line);
    attachMarkedSpans(line, markedSpans);
    var estHeight = estimateHeight ? estimateHeight(line) : 1;
    if (estHeight != line.height) { updateLineHeight(line, estHeight); }
  }

  // Detach a line from the document tree and its markers.
  function cleanUpLine(line) {
    line.parent = null;
    detachMarkedSpans(line);
  }

  // Convert a style as returned by a mode (either null, or a string
  // containing one or more styles) to a CSS style. This is cached,
  // and also looks for line-wide styles.
  var styleToClassCache = {}, styleToClassCacheWithMode = {};
  function interpretTokenStyle(style, options) {
    if (!style || /^\s*$/.test(style)) { return null }
    var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
    return cache[style] ||
      (cache[style] = style.replace(/\S+/g, "cm-$&"))
  }

  // Render the DOM representation of the text of a line. Also builds
  // up a 'line map', which points at the DOM nodes that represent
  // specific stretches of text, and is used by the measuring code.
  // The returned object contains the DOM node, this map, and
  // information about line-wide styles that were set by the mode.
  function buildLineContent(cm, lineView) {
    // The padding-right forces the element to have a 'border', which
    // is needed on Webkit to be able to get line-level bounding
    // rectangles for it (in measureChar).
    var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null);
    var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
                   col: 0, pos: 0, cm: cm,
                   trailingSpace: false,
                   splitSpaces: cm.getOption("lineWrapping")};
    lineView.measure = {};

    // Iterate over the logical lines that make up this visual line.
    for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
      var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0);
      builder.pos = 0;
      builder.addToken = buildToken;
      // Optionally wire in some hacks into the token-rendering
      // algorithm, to deal with browser quirks.
      if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
        { builder.addToken = buildTokenBadBidi(builder.addToken, order); }
      builder.map = [];
      var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
      insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
      if (line.styleClasses) {
        if (line.styleClasses.bgClass)
          { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); }
        if (line.styleClasses.textClass)
          { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); }
      }

      // Ensure at least a single node is present, for measuring.
      if (builder.map.length == 0)
        { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); }

      // Store the map and a cache object for the current logical line
      if (i == 0) {
        lineView.measure.map = builder.map;
        lineView.measure.cache = {};
      } else {
  (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
        ;(lineView.measure.caches || (lineView.measure.caches = [])).push({});
      }
    }

    // See issue #2901
    if (webkit) {
      var last = builder.content.lastChild;
      if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
        { builder.content.className = "cm-tab-wrap-hack"; }
    }

    signal(cm, "renderLine", cm, lineView.line, builder.pre);
    if (builder.pre.className)
      { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); }

    return builder
  }

  function defaultSpecialCharPlaceholder(ch) {
    var token = elt("span", "\u2022", "cm-invalidchar");
    token.title = "\\u" + ch.charCodeAt(0).toString(16);
    token.setAttribute("aria-label", token.title);
    return token
  }

  // Build up the DOM representation for a single token, and add it to
  // the line map. Takes care to render special characters separately.
  function buildToken(builder, text, style, startStyle, endStyle, css, attributes) {
    if (!text) { return }
    var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text;
    var special = builder.cm.state.specialChars, mustWrap = false;
    var content;
    if (!special.test(text)) {
      builder.col += text.length;
      content = document.createTextNode(displayText);
      builder.map.push(builder.pos, builder.pos + text.length, content);
      if (ie && ie_version < 9) { mustWrap = true; }
      builder.pos += text.length;
    } else {
      content = document.createDocumentFragment();
      var pos = 0;
      while (true) {
        special.lastIndex = pos;
        var m = special.exec(text);
        var skipped = m ? m.index - pos : text.length - pos;
        if (skipped) {
          var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
          if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); }
          else { content.appendChild(txt); }
          builder.map.push(builder.pos, builder.pos + skipped, txt);
          builder.col += skipped;
          builder.pos += skipped;
        }
        if (!m) { break }
        pos += skipped + 1;
        var txt$1 = (void 0);
        if (m[0] == "\t") {
          var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
          txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
          txt$1.setAttribute("role", "presentation");
          txt$1.setAttribute("cm-text", "\t");
          builder.col += tabWidth;
        } else if (m[0] == "\r" || m[0] == "\n") {
          txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
          txt$1.setAttribute("cm-text", m[0]);
          builder.col += 1;
        } else {
          txt$1 = builder.cm.options.specialCharPlaceholder(m[0]);
          txt$1.setAttribute("cm-text", m[0]);
          if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); }
          else { content.appendChild(txt$1); }
          builder.col += 1;
        }
        builder.map.push(builder.pos, builder.pos + 1, txt$1);
        builder.pos++;
      }
    }
    builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32;
    if (style || startStyle || endStyle || mustWrap || css || attributes) {
      var fullStyle = style || "";
      if (startStyle) { fullStyle += startStyle; }
      if (endStyle) { fullStyle += endStyle; }
      var token = elt("span", [content], fullStyle, css);
      if (attributes) {
        for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class")
          { token.setAttribute(attr, attributes[attr]); } }
      }
      return builder.content.appendChild(token)
    }
    builder.content.appendChild(content);
  }

  // Change some spaces to NBSP to prevent the browser from collapsing
  // trailing spaces at the end of a line when rendering text (issue #1362).
  function splitSpaces(text, trailingBefore) {
    if (text.length > 1 && !/  /.test(text)) { return text }
    var spaceBefore = trailingBefore, result = "";
    for (var i = 0; i < text.length; i++) {
      var ch = text.charAt(i);
      if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
        { ch = "\u00a0"; }
      result += ch;
      spaceBefore = ch == " ";
    }
    return result
  }

  // Work around nonsense dimensions being reported for stretches of
  // right-to-left text.
  function buildTokenBadBidi(inner, order) {
    return function (builder, text, style, startStyle, endStyle, css, attributes) {
      style = style ? style + " cm-force-border" : "cm-force-border";
      var start = builder.pos, end = start + text.length;
      for (;;) {
        // Find the part that overlaps with the start of this text
        var part = (void 0);
        for (var i = 0; i < order.length; i++) {
          part = order[i];
          if (part.to > start && part.from <= start) { break }
        }
        if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) }
        inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes);
        startStyle = null;
        text = text.slice(part.to - start);
        start = part.to;
      }
    }
  }

  function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
    var widget = !ignoreWidget && marker.widgetNode;
    if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); }
    if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
      if (!widget)
        { widget = builder.content.appendChild(document.createElement("span")); }
      widget.setAttribute("cm-marker", marker.id);
    }
    if (widget) {
      builder.cm.display.input.setUneditable(widget);
      builder.content.appendChild(widget);
    }
    builder.pos += size;
    builder.trailingSpace = false;
  }

  // Outputs a number of spans to make up a line, taking highlighting
  // and marked text into account.
  function insertLineContent(line, builder, styles) {
    var spans = line.markedSpans, allText = line.text, at = 0;
    if (!spans) {
      for (var i$1 = 1; i$1 < styles.length; i$1+=2)
        { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); }
      return
    }

    var len = allText.length, pos = 0, i = 1, text = "", style, css;
    var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes;
    for (;;) {
      if (nextChange == pos) { // Update current marker set
        spanStyle = spanEndStyle = spanStartStyle = css = "";
        attributes = null;
        collapsed = null; nextChange = Infinity;
        var foundBookmarks = [], endStyles = (void 0);
        for (var j = 0; j < spans.length; ++j) {
          var sp = spans[j], m = sp.marker;
          if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
            foundBookmarks.push(m);
          } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
            if (sp.to != null && sp.to != pos && nextChange > sp.to) {
              nextChange = sp.to;
              spanEndStyle = "";
            }
            if (m.className) { spanStyle += " " + m.className; }
            if (m.css) { css = (css ? css + ";" : "") + m.css; }
            if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; }
            if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); }
            // support for the old title property
            // https://github.com/codemirror/CodeMirror/pull/5673
            if (m.title) { (attributes || (attributes = {})).title = m.title; }
            if (m.attributes) {
              for (var attr in m.attributes)
                { (attributes || (attributes = {}))[attr] = m.attributes[attr]; }
            }
            if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
              { collapsed = sp; }
          } else if (sp.from > pos && nextChange > sp.from) {
            nextChange = sp.from;
          }
        }
        if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
          { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } }

        if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
          { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } }
        if (collapsed && (collapsed.from || 0) == pos) {
          buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
                             collapsed.marker, collapsed.from == null);
          if (collapsed.to == null) { return }
          if (collapsed.to == pos) { collapsed = false; }
        }
      }
      if (pos >= len) { break }

      var upto = Math.min(len, nextChange);
      while (true) {
        if (text) {
          var end = pos + text.length;
          if (!collapsed) {
            var tokenText = end > upto ? text.slice(0, upto - pos) : text;
            builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
                             spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes);
          }
          if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
          pos = end;
          spanStartStyle = "";
        }
        text = allText.slice(at, at = styles[i++]);
        style = interpretTokenStyle(styles[i++], builder.cm.options);
      }
    }
  }


  // These objects are used to represent the visible (currently drawn)
  // part of the document. A LineView may correspond to multiple
  // logical lines, if those are connected by collapsed ranges.
  function LineView(doc, line, lineN) {
    // The starting line
    this.line = line;
    // Continuing lines, if any
    this.rest = visualLineContinued(line);
    // Number of logical lines in this visual line
    this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
    this.node = this.text = null;
    this.hidden = lineIsHidden(doc, line);
  }

  // Create a range of LineView objects for the given lines.
  function buildViewArray(cm, from, to) {
    var array = [], nextPos;
    for (var pos = from; pos < to; pos = nextPos) {
      var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
      nextPos = pos + view.size;
      array.push(view);
    }
    return array
  }

  var operationGroup = null;

  function pushOperation(op) {
    if (operationGroup) {
      operationGroup.ops.push(op);
    } else {
      op.ownsGroup = operationGroup = {
        ops: [op],
        delayedCallbacks: []
      };
    }
  }

  function fireCallbacksForOps(group) {
    // Calls delayed callbacks and cursorActivity handlers until no
    // new ones appear
    var callbacks = group.delayedCallbacks, i = 0;
    do {
      for (; i < callbacks.length; i++)
        { callbacks[i].call(null); }
      for (var j = 0; j < group.ops.length; j++) {
        var op = group.ops[j];
        if (op.cursorActivityHandlers)
          { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
            { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } }
      }
    } while (i < callbacks.length)
  }

  function finishOperation(op, endCb) {
    var group = op.ownsGroup;
    if (!group) { return }

    try { fireCallbacksForOps(group); }
    finally {
      operationGroup = null;
      endCb(group);
    }
  }

  var orphanDelayedCallbacks = null;

  // Often, we want to signal events at a point where we are in the
  // middle of some work, but don't want the handler to start calling
  // other methods on the editor, which might be in an inconsistent
  // state or simply not expect any other events to happen.
  // signalLater looks whether there are any handlers, and schedules
  // them to be executed when the last operation ends, or, if no
  // operation is active, when a timeout fires.
  function signalLater(emitter, type /*, values...*/) {
    var arr = getHandlers(emitter, type);
    if (!arr.length) { return }
    var args = Array.prototype.slice.call(arguments, 2), list;
    if (operationGroup) {
      list = operationGroup.delayedCallbacks;
    } else if (orphanDelayedCallbacks) {
      list = orphanDelayedCallbacks;
    } else {
      list = orphanDelayedCallbacks = [];
      setTimeout(fireOrphanDelayed, 0);
    }
    var loop = function ( i ) {
      list.push(function () { return arr[i].apply(null, args); });
    };

    for (var i = 0; i < arr.length; ++i)
      loop( i );
  }

  function fireOrphanDelayed() {
    var delayed = orphanDelayedCallbacks;
    orphanDelayedCallbacks = null;
    for (var i = 0; i < delayed.length; ++i) { delayed[i](); }
  }

  // When an aspect of a line changes, a string is added to
  // lineView.changes. This updates the relevant part of the line's
  // DOM structure.
  function updateLineForChanges(cm, lineView, lineN, dims) {
    for (var j = 0; j < lineView.changes.length; j++) {
      var type = lineView.changes[j];
      if (type == "text") { updateLineText(cm, lineView); }
      else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); }
      else if (type == "class") { updateLineClasses(cm, lineView); }
      else if (type == "widget") { updateLineWidgets(cm, lineView, dims); }
    }
    lineView.changes = null;
  }

  // Lines with gutter elements, widgets or a background class need to
  // be wrapped, and have the extra elements added to the wrapper div
  function ensureLineWrapped(lineView) {
    if (lineView.node == lineView.text) {
      lineView.node = elt("div", null, null, "position: relative");
      if (lineView.text.parentNode)
        { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); }
      lineView.node.appendChild(lineView.text);
      if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; }
    }
    return lineView.node
  }

  function updateLineBackground(cm, lineView) {
    var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
    if (cls) { cls += " CodeMirror-linebackground"; }
    if (lineView.background) {
      if (cls) { lineView.background.className = cls; }
      else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
    } else if (cls) {
      var wrap = ensureLineWrapped(lineView);
      lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
      cm.display.input.setUneditable(lineView.background);
    }
  }

  // Wrapper around buildLineContent which will reuse the structure
  // in display.externalMeasured when possible.
  function getLineContent(cm, lineView) {
    var ext = cm.display.externalMeasured;
    if (ext && ext.line == lineView.line) {
      cm.display.externalMeasured = null;
      lineView.measure = ext.measure;
      return ext.built
    }
    return buildLineContent(cm, lineView)
  }

  // Redraw the line's text. Interacts with the background and text
  // classes because the mode may output tokens that influence these
  // classes.
  function updateLineText(cm, lineView) {
    var cls = lineView.text.className;
    var built = getLineContent(cm, lineView);
    if (lineView.text == lineView.node) { lineView.node = built.pre; }
    lineView.text.parentNode.replaceChild(built.pre, lineView.text);
    lineView.text = built.pre;
    if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
      lineView.bgClass = built.bgClass;
      lineView.textClass = built.textClass;
      updateLineClasses(cm, lineView);
    } else if (cls) {
      lineView.text.className = cls;
    }
  }

  function updateLineClasses(cm, lineView) {
    updateLineBackground(cm, lineView);
    if (lineView.line.wrapClass)
      { ensureLineWrapped(lineView).className = lineView.line.wrapClass; }
    else if (lineView.node != lineView.text)
      { lineView.node.className = ""; }
    var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
    lineView.text.className = textClass || "";
  }

  function updateLineGutter(cm, lineView, lineN, dims) {
    if (lineView.gutter) {
      lineView.node.removeChild(lineView.gutter);
      lineView.gutter = null;
    }
    if (lineView.gutterBackground) {
      lineView.node.removeChild(lineView.gutterBackground);
      lineView.gutterBackground = null;
    }
    if (lineView.line.gutterClass) {
      var wrap = ensureLineWrapped(lineView);
      lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
                                      ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"));
      cm.display.input.setUneditable(lineView.gutterBackground);
      wrap.insertBefore(lineView.gutterBackground, lineView.text);
    }
    var markers = lineView.line.gutterMarkers;
    if (cm.options.lineNumbers || markers) {
      var wrap$1 = ensureLineWrapped(lineView);
      var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
      cm.display.input.setUneditable(gutterWrap);
      wrap$1.insertBefore(gutterWrap, lineView.text);
      if (lineView.line.gutterClass)
        { gutterWrap.className += " " + lineView.line.gutterClass; }
      if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
        { lineView.lineNumber = gutterWrap.appendChild(
          elt("div", lineNumberFor(cm.options, lineN),
              "CodeMirror-linenumber CodeMirror-gutter-elt",
              ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); }
      if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) {
        var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id];
        if (found)
          { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
                                     ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); }
      } }
    }
  }

  function updateLineWidgets(cm, lineView, dims) {
    if (lineView.alignable) { lineView.alignable = null; }
    var isWidget = classTest("CodeMirror-linewidget");
    for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
      next = node.nextSibling;
      if (isWidget.test(node.className)) { lineView.node.removeChild(node); }
    }
    insertLineWidgets(cm, lineView, dims);
  }

  // Build a line's DOM representation from scratch
  function buildLineElement(cm, lineView, lineN, dims) {
    var built = getLineContent(cm, lineView);
    lineView.text = lineView.node = built.pre;
    if (built.bgClass) { lineView.bgClass = built.bgClass; }
    if (built.textClass) { lineView.textClass = built.textClass; }

    updateLineClasses(cm, lineView);
    updateLineGutter(cm, lineView, lineN, dims);
    insertLineWidgets(cm, lineView, dims);
    return lineView.node
  }

  // A lineView may contain multiple logical lines (when merged by
  // collapsed spans). The widgets for all of them need to be drawn.
  function insertLineWidgets(cm, lineView, dims) {
    insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
    if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
      { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } }
  }

  function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
    if (!line.widgets) { return }
    var wrap = ensureLineWrapped(lineView);
    for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
      var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : ""));
      if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); }
      positionLineWidget(widget, node, lineView, dims);
      cm.display.input.setUneditable(node);
      if (allowAbove && widget.above)
        { wrap.insertBefore(node, lineView.gutter || lineView.text); }
      else
        { wrap.appendChild(node); }
      signalLater(widget, "redraw");
    }
  }

  function positionLineWidget(widget, node, lineView, dims) {
    if (widget.noHScroll) {
  (lineView.alignable || (lineView.alignable = [])).push(node);
      var width = dims.wrapperWidth;
      node.style.left = dims.fixedPos + "px";
      if (!widget.coverGutter) {
        width -= dims.gutterTotalWidth;
        node.style.paddingLeft = dims.gutterTotalWidth + "px";
      }
      node.style.width = width + "px";
    }
    if (widget.coverGutter) {
      node.style.zIndex = 5;
      node.style.position = "relative";
      if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; }
    }
  }

  function widgetHeight(widget) {
    if (widget.height != null) { return widget.height }
    var cm = widget.doc.cm;
    if (!cm) { return 0 }
    if (!contains(document.body, widget.node)) {
      var parentStyle = "position: relative;";
      if (widget.coverGutter)
        { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; }
      if (widget.noHScroll)
        { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; }
      removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
    }
    return widget.height = widget.node.parentNode.offsetHeight
  }

  // Return true when the given mouse event happened in a widget
  function eventInWidget(display, e) {
    for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
      if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
          (n.parentNode == display.sizer && n != display.mover))
        { return true }
    }
  }

  // POSITION MEASUREMENT

  function paddingTop(display) {return display.lineSpace.offsetTop}
  function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
  function paddingH(display) {
    if (display.cachedPaddingH) { return display.cachedPaddingH }
    var e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like"));
    var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
    var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
    if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; }
    return data
  }

  function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
  function displayWidth(cm) {
    return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
  }
  function displayHeight(cm) {
    return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
  }

  // Ensure the lineView.wrapping.heights array is populated. This is
  // an array of bottom offsets for the lines that make up a drawn
  // line. When lineWrapping is on, there might be more than one
  // height.
  function ensureLineHeights(cm, lineView, rect) {
    var wrapping = cm.options.lineWrapping;
    var curWidth = wrapping && displayWidth(cm);
    if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
      var heights = lineView.measure.heights = [];
      if (wrapping) {
        lineView.measure.width = curWidth;
        var rects = lineView.text.firstChild.getClientRects();
        for (var i = 0; i < rects.length - 1; i++) {
          var cur = rects[i], next = rects[i + 1];
          if (Math.abs(cur.bottom - next.bottom) > 2)
            { heights.push((cur.bottom + next.top) / 2 - rect.top); }
        }
      }
      heights.push(rect.bottom - rect.top);
    }
  }

  // Find a line map (mapping character offsets to text nodes) and a
  // measurement cache for the given line number. (A line view might
  // contain multiple lines when collapsed ranges are present.)
  function mapFromLineView(lineView, line, lineN) {
    if (lineView.line == line)
      { return {map: lineView.measure.map, cache: lineView.measure.cache} }
    for (var i = 0; i < lineView.rest.length; i++)
      { if (lineView.rest[i] == line)
        { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
    for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
      { if (lineNo(lineView.rest[i$1]) > lineN)
        { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
  }

  // Render a line into the hidden node display.externalMeasured. Used
  // when measurement is needed for a line that's not in the viewport.
  function updateExternalMeasurement(cm, line) {
    line = visualLine(line);
    var lineN = lineNo(line);
    var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
    view.lineN = lineN;
    var built = view.built = buildLineContent(cm, view);
    view.text = built.pre;
    removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
    return view
  }

  // Get a {top, bottom, left, right} box (in line-local coordinates)
  // for a given character.
  function measureChar(cm, line, ch, bias) {
    return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
  }

  // Find a line view that corresponds to the given line number.
  function findViewForLine(cm, lineN) {
    if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
      { return cm.display.view[findViewIndex(cm, lineN)] }
    var ext = cm.display.externalMeasured;
    if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
      { return ext }
  }

  // Measurement can be split in two steps, the set-up work that
  // applies to the whole line, and the measurement of the actual
  // character. Functions like coordsChar, that need to do a lot of
  // measurements in a row, can thus ensure that the set-up work is
  // only done once.
  function prepareMeasureForLine(cm, line) {
    var lineN = lineNo(line);
    var view = findViewForLine(cm, lineN);
    if (view && !view.text) {
      view = null;
    } else if (view && view.changes) {
      updateLineForChanges(cm, view, lineN, getDimensions(cm));
      cm.curOp.forceUpdate = true;
    }
    if (!view)
      { view = updateExternalMeasurement(cm, line); }

    var info = mapFromLineView(view, line, lineN);
    return {
      line: line, view: view, rect: null,
      map: info.map, cache: info.cache, before: info.before,
      hasHeights: false
    }
  }

  // Given a prepared measurement object, measures the position of an
  // actual character (or fetches it from the cache).
  function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
    if (prepared.before) { ch = -1; }
    var key = ch + (bias || ""), found;
    if (prepared.cache.hasOwnProperty(key)) {
      found = prepared.cache[key];
    } else {
      if (!prepared.rect)
        { prepared.rect = prepared.view.text.getBoundingClientRect(); }
      if (!prepared.hasHeights) {
        ensureLineHeights(cm, prepared.view, prepared.rect);
        prepared.hasHeights = true;
      }
      found = measureCharInner(cm, prepared, ch, bias);
      if (!found.bogus) { prepared.cache[key] = found; }
    }
    return {left: found.left, right: found.right,
            top: varHeight ? found.rtop : found.top,
            bottom: varHeight ? found.rbottom : found.bottom}
  }

  var nullRect = {left: 0, right: 0, top: 0, bottom: 0};

  function nodeAndOffsetInLineMap(map, ch, bias) {
    var node, start, end, collapse, mStart, mEnd;
    // First, search the line map for the text node corresponding to,
    // or closest to, the target character.
    for (var i = 0; i < map.length; i += 3) {
      mStart = map[i];
      mEnd = map[i + 1];
      if (ch < mStart) {
        start = 0; end = 1;
        collapse = "left";
      } else if (ch < mEnd) {
        start = ch - mStart;
        end = start + 1;
      } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
        end = mEnd - mStart;
        start = end - 1;
        if (ch >= mEnd) { collapse = "right"; }
      }
      if (start != null) {
        node = map[i + 2];
        if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
          { collapse = bias; }
        if (bias == "left" && start == 0)
          { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
            node = map[(i -= 3) + 2];
            collapse = "left";
          } }
        if (bias == "right" && start == mEnd - mStart)
          { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {
            node = map[(i += 3) + 2];
            collapse = "right";
          } }
        break
      }
    }
    return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
  }

  function getUsefulRect(rects, bias) {
    var rect = nullRect;
    if (bias == "left") { for (var i = 0; i < rects.length; i++) {
      if ((rect = rects[i]).left != rect.right) { break }
    } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
      if ((rect = rects[i$1]).left != rect.right) { break }
    } }
    return rect
  }

  function measureCharInner(cm, prepared, ch, bias) {
    var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
    var node = place.node, start = place.start, end = place.end, collapse = place.collapse;

    var rect;
    if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
      for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
        while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; }
        while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; }
        if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
          { rect = node.parentNode.getBoundingClientRect(); }
        else
          { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); }
        if (rect.left || rect.right || start == 0) { break }
        end = start;
        start = start - 1;
        collapse = "right";
      }
      if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); }
    } else { // If it is a widget, simply get the box for the whole widget.
      if (start > 0) { collapse = bias = "right"; }
      var rects;
      if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
        { rect = rects[bias == "right" ? rects.length - 1 : 0]; }
      else
        { rect = node.getBoundingClientRect(); }
    }
    if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
      var rSpan = node.parentNode.getClientRects()[0];
      if (rSpan)
        { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; }
      else
        { rect = nullRect; }
    }

    var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
    var mid = (rtop + rbot) / 2;
    var heights = prepared.view.measure.heights;
    var i = 0;
    for (; i < heights.length - 1; i++)
      { if (mid < heights[i]) { break } }
    var top = i ? heights[i - 1] : 0, bot = heights[i];
    var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
                  right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
                  top: top, bottom: bot};
    if (!rect.left && !rect.right) { result.bogus = true; }
    if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }

    return result
  }

  // Work around problem with bounding client rects on ranges being
  // returned incorrectly when zoomed on IE10 and below.
  function maybeUpdateRectForZooming(measure, rect) {
    if (!window.screen || screen.logicalXDPI == null ||
        screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
      { return rect }
    var scaleX = screen.logicalXDPI / screen.deviceXDPI;
    var scaleY = screen.logicalYDPI / screen.deviceYDPI;
    return {left: rect.left * scaleX, right: rect.right * scaleX,
            top: rect.top * scaleY, bottom: rect.bottom * scaleY}
  }

  function clearLineMeasurementCacheFor(lineView) {
    if (lineView.measure) {
      lineView.measure.cache = {};
      lineView.measure.heights = null;
      if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
        { lineView.measure.caches[i] = {}; } }
    }
  }

  function clearLineMeasurementCache(cm) {
    cm.display.externalMeasure = null;
    removeChildren(cm.display.lineMeasure);
    for (var i = 0; i < cm.display.view.length; i++)
      { clearLineMeasurementCacheFor(cm.display.view[i]); }
  }

  function clearCaches(cm) {
    clearLineMeasurementCache(cm);
    cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
    if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; }
    cm.display.lineNumChars = null;
  }

  function pageScrollX() {
    // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
    // which causes page_Offset and bounding client rects to use
    // different reference viewports and invalidate our calculations.
    if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }
    return window.pageXOffset || (document.documentElement || document.body).scrollLeft
  }
  function pageScrollY() {
    if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }
    return window.pageYOffset || (document.documentElement || document.body).scrollTop
  }

  function widgetTopHeight(lineObj) {
    var height = 0;
    if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
      { height += widgetHeight(lineObj.widgets[i]); } } }
    return height
  }

  // Converts a {top, bottom, left, right} box from line-local
  // coordinates into another coordinate system. Context may be one of
  // "line", "div" (display.lineDiv), "local"./null (editor), "window",
  // or "page".
  function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
    if (!includeWidgets) {
      var height = widgetTopHeight(lineObj);
      rect.top += height; rect.bottom += height;
    }
    if (context == "line") { return rect }
    if (!context) { context = "local"; }
    var yOff = heightAtLine(lineObj);
    if (context == "local") { yOff += paddingTop(cm.display); }
    else { yOff -= cm.display.viewOffset; }
    if (context == "page" || context == "window") {
      var lOff = cm.display.lineSpace.getBoundingClientRect();
      yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
      var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
      rect.left += xOff; rect.right += xOff;
    }
    rect.top += yOff; rect.bottom += yOff;
    return rect
  }

  // Coverts a box from "div" coords to another coordinate system.
  // Context may be "window", "page", "div", or "local"./null.
  function fromCoordSystem(cm, coords, context) {
    if (context == "div") { return coords }
    var left = coords.left, top = coords.top;
    // First move into "page" coordinate system
    if (context == "page") {
      left -= pageScrollX();
      top -= pageScrollY();
    } else if (context == "local" || !context) {
      var localBox = cm.display.sizer.getBoundingClientRect();
      left += localBox.left;
      top += localBox.top;
    }

    var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
    return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
  }

  function charCoords(cm, pos, context, lineObj, bias) {
    if (!lineObj) { lineObj = getLine(cm.doc, pos.line); }
    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
  }

  // Returns a box for a given cursor position, which may have an
  // 'other' property containing the position of the secondary cursor
  // on a bidi boundary.
  // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
  // and after `char - 1` in writing order of `char - 1`
  // A cursor Pos(line, char, "after") is on the same visual line as `char`
  // and before `char` in writing order of `char`
  // Examples (upper-case letters are RTL, lower-case are LTR):
  //     Pos(0, 1, ...)
  //     before   after
  // ab     a|b     a|b
  // aB     a|B     aB|
  // Ab     |Ab     A|b
  // AB     B|A     B|A
  // Every position after the last character on a line is considered to stick
  // to the last character on the line.
  function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
    lineObj = lineObj || getLine(cm.doc, pos.line);
    if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
    function get(ch, right) {
      var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
      if (right) { m.left = m.right; } else { m.right = m.left; }
      return intoCoordSystem(cm, lineObj, m, context)
    }
    var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky;
    if (ch >= lineObj.text.length) {
      ch = lineObj.text.length;
      sticky = "before";
    } else if (ch <= 0) {
      ch = 0;
      sticky = "after";
    }
    if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }

    function getBidi(ch, partPos, invert) {
      var part = order[partPos], right = part.level == 1;
      return get(invert ? ch - 1 : ch, right != invert)
    }
    var partPos = getBidiPartAt(order, ch, sticky);
    var other = bidiOther;
    var val = getBidi(ch, partPos, sticky == "before");
    if (other != null) { val.other = getBidi(ch, other, sticky != "before"); }
    return val
  }

  // Used to cheaply estimate the coordinates for a position. Used for
  // intermediate scroll updates.
  function estimateCoords(cm, pos) {
    var left = 0;
    pos = clipPos(cm.doc, pos);
    if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; }
    var lineObj = getLine(cm.doc, pos.line);
    var top = heightAtLine(lineObj) + paddingTop(cm.display);
    return {left: left, right: left, top: top, bottom: top + lineObj.height}
  }

  // Positions returned by coordsChar contain some extra information.
  // xRel is the relative x position of the input coordinates compared
  // to the found position (so xRel > 0 means the coordinates are to
  // the right of the character position, for example). When outside
  // is true, that means the coordinates lie outside the line's
  // vertical range.
  function PosWithInfo(line, ch, sticky, outside, xRel) {
    var pos = Pos(line, ch, sticky);
    pos.xRel = xRel;
    if (outside) { pos.outside = outside; }
    return pos
  }

  // Compute the character position closest to the given coordinates.
  // Input must be lineSpace-local ("div" coordinate system).
  function coordsChar(cm, x, y) {
    var doc = cm.doc;
    y += cm.display.viewOffset;
    if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) }
    var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
    if (lineN > last)
      { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) }
    if (x < 0) { x = 0; }

    var lineObj = getLine(doc, lineN);
    for (;;) {
      var found = coordsCharInner(cm, lineObj, lineN, x, y);
      var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0));
      if (!collapsed) { return found }
      var rangeEnd = collapsed.find(1);
      if (rangeEnd.line == lineN) { return rangeEnd }
      lineObj = getLine(doc, lineN = rangeEnd.line);
    }
  }

  function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
    y -= widgetTopHeight(lineObj);
    var end = lineObj.text.length;
    var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0);
    end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end);
    return {begin: begin, end: end}
  }

  function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
    if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
    var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top;
    return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
  }

  // Returns true if the given side of a box is after the given
  // coordinates, in top-to-bottom, left-to-right order.
  function boxIsAfter(box, x, y, left) {
    return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
  }

  function coordsCharInner(cm, lineObj, lineNo, x, y) {
    // Move y into line-local coordinate space
    y -= heightAtLine(lineObj);
    var preparedMeasure = prepareMeasureForLine(cm, lineObj);
    // When directly calling `measureCharPrepared`, we have to adjust
    // for the widgets at this line.
    var widgetHeight = widgetTopHeight(lineObj);
    var begin = 0, end = lineObj.text.length, ltr = true;

    var order = getOrder(lineObj, cm.doc.direction);
    // If the line isn't plain left-to-right text, first figure out
    // which bidi section the coordinates fall into.
    if (order) {
      var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
                   (cm, lineObj, lineNo, preparedMeasure, order, x, y);
      ltr = part.level != 1;
      // The awkward -1 offsets are needed because findFirst (called
      // on these below) will treat its first bound as inclusive,
      // second as exclusive, but we want to actually address the
      // characters in the part's range
      begin = ltr ? part.from : part.to - 1;
      end = ltr ? part.to : part.from - 1;
    }

    // A binary search to find the first character whose bounding box
    // starts after the coordinates. If we run across any whose box wrap
    // the coordinates, store that.
    var chAround = null, boxAround = null;
    var ch = findFirst(function (ch) {
      var box = measureCharPrepared(cm, preparedMeasure, ch);
      box.top += widgetHeight; box.bottom += widgetHeight;
      if (!boxIsAfter(box, x, y, false)) { return false }
      if (box.top <= y && box.left <= x) {
        chAround = ch;
        boxAround = box;
      }
      return true
    }, begin, end);

    var baseX, sticky, outside = false;
    // If a box around the coordinates was found, use that
    if (boxAround) {
      // Distinguish coordinates nearer to the left or right side of the box
      var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr;
      ch = chAround + (atStart ? 0 : 1);
      sticky = atStart ? "after" : "before";
      baseX = atLeft ? boxAround.left : boxAround.right;
    } else {
      // (Adjust for extended bound, if necessary.)
      if (!ltr && (ch == end || ch == begin)) { ch++; }
      // To determine which side to associate with, get the box to the
      // left of the character and compare it's vertical position to the
      // coordinates
      sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
        (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ?
        "after" : "before";
      // Now get accurate coordinates for this place, in order to get a
      // base X position
      var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure);
      baseX = coords.left;
      outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0;
    }

    ch = skipExtendingChars(lineObj.text, ch, 1);
    return PosWithInfo(lineNo, ch, sticky, outside, x - baseX)
  }

  function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) {
    // Bidi parts are sorted left-to-right, and in a non-line-wrapping
    // situation, we can take this ordering to correspond to the visual
    // ordering. This finds the first part whose end is after the given
    // coordinates.
    var index = findFirst(function (i) {
      var part = order[i], ltr = part.level != 1;
      return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"),
                                     "line", lineObj, preparedMeasure), x, y, true)
    }, 0, order.length - 1);
    var part = order[index];
    // If this isn't the first part, the part's start is also after
    // the coordinates, and the coordinates aren't on the same line as
    // that start, move one part back.
    if (index > 0) {
      var ltr = part.level != 1;
      var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"),
                               "line", lineObj, preparedMeasure);
      if (boxIsAfter(start, x, y, true) && start.top > y)
        { part = order[index - 1]; }
    }
    return part
  }

  function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
    // In a wrapped line, rtl text on wrapping boundaries can do things
    // that don't correspond to the ordering in our `order` array at
    // all, so a binary search doesn't work, and we want to return a
    // part that only spans one line so that the binary search in
    // coordsCharInner is safe. As such, we first find the extent of the
    // wrapped line, and then do a flat search in which we discard any
    // spans that aren't on the line.
    var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
    var begin = ref.begin;
    var end = ref.end;
    if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; }
    var part = null, closestDist = null;
    for (var i = 0; i < order.length; i++) {
      var p = order[i];
      if (p.from >= end || p.to <= begin) { continue }
      var ltr = p.level != 1;
      var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right;
      // Weigh against spans ending before this, so that they are only
      // picked if nothing ends after
      var dist = endX < x ? x - endX + 1e9 : endX - x;
      if (!part || closestDist > dist) {
        part = p;
        closestDist = dist;
      }
    }
    if (!part) { part = order[order.length - 1]; }
    // Clip the part to the wrapped line.
    if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; }
    if (part.to > end) { part = {from: part.from, to: end, level: part.level}; }
    return part
  }

  var measureText;
  // Compute the default text height.
  function textHeight(display) {
    if (display.cachedTextHeight != null) { return display.cachedTextHeight }
    if (measureText == null) {
      measureText = elt("pre", null, "CodeMirror-line-like");
      // Measure a bunch of lines, for browsers that compute
      // fractional heights.
      for (var i = 0; i < 49; ++i) {
        measureText.appendChild(document.createTextNode("x"));
        measureText.appendChild(elt("br"));
      }
      measureText.appendChild(document.createTextNode("x"));
    }
    removeChildrenAndAdd(display.measure, measureText);
    var height = measureText.offsetHeight / 50;
    if (height > 3) { display.cachedTextHeight = height; }
    removeChildren(display.measure);
    return height || 1
  }

  // Compute the default character width.
  function charWidth(display) {
    if (display.cachedCharWidth != null) { return display.cachedCharWidth }
    var anchor = elt("span", "xxxxxxxxxx");
    var pre = elt("pre", [anchor], "CodeMirror-line-like");
    removeChildrenAndAdd(display.measure, pre);
    var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
    if (width > 2) { display.cachedCharWidth = width; }
    return width || 10
  }

  // Do a bulk-read of the DOM positions and sizes needed to draw the
  // view, so that we don't interleave reading and writing to the DOM.
  function getDimensions(cm) {
    var d = cm.display, left = {}, width = {};
    var gutterLeft = d.gutters.clientLeft;
    for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
      var id = cm.display.gutterSpecs[i].className;
      left[id] = n.offsetLeft + n.clientLeft + gutterLeft;
      width[id] = n.clientWidth;
    }
    return {fixedPos: compensateForHScroll(d),
            gutterTotalWidth: d.gutters.offsetWidth,
            gutterLeft: left,
            gutterWidth: width,
            wrapperWidth: d.wrapper.clientWidth}
  }

  // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
  // but using getBoundingClientRect to get a sub-pixel-accurate
  // result.
  function compensateForHScroll(display) {
    return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
  }

  // Returns a function that estimates the height of a line, to use as
  // first approximation until the line becomes visible (and is thus
  // properly measurable).
  function estimateHeight(cm) {
    var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
    var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
    return function (line) {
      if (lineIsHidden(cm.doc, line)) { return 0 }

      var widgetsHeight = 0;
      if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
        if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; }
      } }

      if (wrapping)
        { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
      else
        { return widgetsHeight + th }
    }
  }

  function estimateLineHeights(cm) {
    var doc = cm.doc, est = estimateHeight(cm);
    doc.iter(function (line) {
      var estHeight = est(line);
      if (estHeight != line.height) { updateLineHeight(line, estHeight); }
    });
  }

  // Given a mouse event, find the corresponding position. If liberal
  // is false, it checks whether a gutter or scrollbar was clicked,
  // and returns null if it was. forRect is used by rectangular
  // selections, and tries to estimate a character position even for
  // coordinates beyond the right of the text.
  function posFromMouse(cm, e, liberal, forRect) {
    var display = cm.display;
    if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }

    var x, y, space = display.lineSpace.getBoundingClientRect();
    // Fails unpredictably on IE[67] when mouse is dragged around quickly.
    try { x = e.clientX - space.left; y = e.clientY - space.top; }
    catch (e$1) { return null }
    var coords = coordsChar(cm, x, y), line;
    if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
      var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
      coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
    }
    return coords
  }

  // Find the view element corresponding to a given line. Return null
  // when the line isn't visible.
  function findViewIndex(cm, n) {
    if (n >= cm.display.viewTo) { return null }
    n -= cm.display.viewFrom;
    if (n < 0) { return null }
    var view = cm.display.view;
    for (var i = 0; i < view.length; i++) {
      n -= view[i].size;
      if (n < 0) { return i }
    }
  }

  // Updates the display.view data structure for a given change to the
  // document. From and to are in pre-change coordinates. Lendiff is
  // the amount of lines added or subtracted by the change. This is
  // used for changes that span multiple lines, or change the way
  // lines are divided into visual lines. regLineChange (below)
  // registers single-line changes.
  function regChange(cm, from, to, lendiff) {
    if (from == null) { from = cm.doc.first; }
    if (to == null) { to = cm.doc.first + cm.doc.size; }
    if (!lendiff) { lendiff = 0; }

    var display = cm.display;
    if (lendiff && to < display.viewTo &&
        (display.updateLineNumbers == null || display.updateLineNumbers > from))
      { display.updateLineNumbers = from; }

    cm.curOp.viewChanged = true;

    if (from >= display.viewTo) { // Change after
      if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
        { resetView(cm); }
    } else if (to <= display.viewFrom) { // Change before
      if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
        resetView(cm);
      } else {
        display.viewFrom += lendiff;
        display.viewTo += lendiff;
      }
    } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
      resetView(cm);
    } else if (from <= display.viewFrom) { // Top overlap
      var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
      if (cut) {
        display.view = display.view.slice(cut.index);
        display.viewFrom = cut.lineN;
        display.viewTo += lendiff;
      } else {
        resetView(cm);
      }
    } else if (to >= display.viewTo) { // Bottom overlap
      var cut$1 = viewCuttingPoint(cm, from, from, -1);
      if (cut$1) {
        display.view = display.view.slice(0, cut$1.index);
        display.viewTo = cut$1.lineN;
      } else {
        resetView(cm);
      }
    } else { // Gap in the middle
      var cutTop = viewCuttingPoint(cm, from, from, -1);
      var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
      if (cutTop && cutBot) {
        display.view = display.view.slice(0, cutTop.index)
          .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
          .concat(display.view.slice(cutBot.index));
        display.viewTo += lendiff;
      } else {
        resetView(cm);
      }
    }

    var ext = display.externalMeasured;
    if (ext) {
      if (to < ext.lineN)
        { ext.lineN += lendiff; }
      else if (from < ext.lineN + ext.size)
        { display.externalMeasured = null; }
    }
  }

  // Register a change to a single line. Type must be one of "text",
  // "gutter", "class", "widget"
  function regLineChange(cm, line, type) {
    cm.curOp.viewChanged = true;
    var display = cm.display, ext = cm.display.externalMeasured;
    if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
      { display.externalMeasured = null; }

    if (line < display.viewFrom || line >= display.viewTo) { return }
    var lineView = display.view[findViewIndex(cm, line)];
    if (lineView.node == null) { return }
    var arr = lineView.changes || (lineView.changes = []);
    if (indexOf(arr, type) == -1) { arr.push(type); }
  }

  // Clear the view.
  function resetView(cm) {
    cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
    cm.display.view = [];
    cm.display.viewOffset = 0;
  }

  function viewCuttingPoint(cm, oldN, newN, dir) {
    var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
    if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
      { return {index: index, lineN: newN} }
    var n = cm.display.viewFrom;
    for (var i = 0; i < index; i++)
      { n += view[i].size; }
    if (n != oldN) {
      if (dir > 0) {
        if (index == view.length - 1) { return null }
        diff = (n + view[index].size) - oldN;
        index++;
      } else {
        diff = n - oldN;
      }
      oldN += diff; newN += diff;
    }
    while (visualLineNo(cm.doc, newN) != newN) {
      if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
      newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
      index += dir;
    }
    return {index: index, lineN: newN}
  }

  // Force the view to cover a given range, adding empty view element
  // or clipping off existing ones as needed.
  function adjustView(cm, from, to) {
    var display = cm.display, view = display.view;
    if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
      display.view = buildViewArray(cm, from, to);
      display.viewFrom = from;
    } else {
      if (display.viewFrom > from)
        { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); }
      else if (display.viewFrom < from)
        { display.view = display.view.slice(findViewIndex(cm, from)); }
      display.viewFrom = from;
      if (display.viewTo < to)
        { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); }
      else if (display.viewTo > to)
        { display.view = display.view.slice(0, findViewIndex(cm, to)); }
    }
    display.viewTo = to;
  }

  // Count the number of lines in the view whose DOM representation is
  // out of date (or nonexistent).
  function countDirtyView(cm) {
    var view = cm.display.view, dirty = 0;
    for (var i = 0; i < view.length; i++) {
      var lineView = view[i];
      if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; }
    }
    return dirty
  }

  function updateSelection(cm) {
    cm.display.input.showSelection(cm.display.input.prepareSelection());
  }

  function prepareSelection(cm, primary) {
    if ( primary === void 0 ) primary = true;

    var doc = cm.doc, result = {};
    var curFragment = result.cursors = document.createDocumentFragment();
    var selFragment = result.selection = document.createDocumentFragment();

    for (var i = 0; i < doc.sel.ranges.length; i++) {
      if (!primary && i == doc.sel.primIndex) { continue }
      var range = doc.sel.ranges[i];
      if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue }
      var collapsed = range.empty();
      if (collapsed || cm.options.showCursorWhenSelecting)
        { drawSelectionCursor(cm, range.head, curFragment); }
      if (!collapsed)
        { drawSelectionRange(cm, range, selFragment); }
    }
    return result
  }

  // Draws a cursor for the given range
  function drawSelectionCursor(cm, head, output) {
    var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);

    var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
    cursor.style.left = pos.left + "px";
    cursor.style.top = pos.top + "px";
    cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";

    if (pos.other) {
      // Secondary cursor, shown when on a 'jump' in bi-directional text
      var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
      otherCursor.style.display = "";
      otherCursor.style.left = pos.other.left + "px";
      otherCursor.style.top = pos.other.top + "px";
      otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
    }
  }

  function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }

  // Draws the given range as a highlighted selection
  function drawSelectionRange(cm, range, output) {
    var display = cm.display, doc = cm.doc;
    var fragment = document.createDocumentFragment();
    var padding = paddingH(cm.display), leftSide = padding.left;
    var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
    var docLTR = doc.direction == "ltr";

    function add(left, top, width, bottom) {
      if (top < 0) { top = 0; }
      top = Math.round(top);
      bottom = Math.round(bottom);
      fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n                             top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n                             height: " + (bottom - top) + "px")));
    }

    function drawForLine(line, fromArg, toArg) {
      var lineObj = getLine(doc, line);
      var lineLen = lineObj.text.length;
      var start, end;
      function coords(ch, bias) {
        return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
      }

      function wrapX(pos, dir, side) {
        var extent = wrappedLineExtentChar(cm, lineObj, null, pos);
        var prop = (dir == "ltr") == (side == "after") ? "left" : "right";
        var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1);
        return coords(ch, prop)[prop]
      }

      var order = getOrder(lineObj, doc.direction);
      iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
        var ltr = dir == "ltr";
        var fromPos = coords(from, ltr ? "left" : "right");
        var toPos = coords(to - 1, ltr ? "right" : "left");

        var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen;
        var first = i == 0, last = !order || i == order.length - 1;
        if (toPos.top - fromPos.top <= 3) { // Single line
          var openLeft = (docLTR ? openStart : openEnd) && first;
          var openRight = (docLTR ? openEnd : openStart) && last;
          var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left;
          var right = openRight ? rightSide : (ltr ? toPos : fromPos).right;
          add(left, fromPos.top, right - left, fromPos.bottom);
        } else { // Multiple lines
          var topLeft, topRight, botLeft, botRight;
          if (ltr) {
            topLeft = docLTR && openStart && first ? leftSide : fromPos.left;
            topRight = docLTR ? rightSide : wrapX(from, dir, "before");
            botLeft = docLTR ? leftSide : wrapX(to, dir, "after");
            botRight = docLTR && openEnd && last ? rightSide : toPos.right;
          } else {
            topLeft = !docLTR ? leftSide : wrapX(from, dir, "before");
            topRight = !docLTR && openStart && first ? rightSide : fromPos.right;
            botLeft = !docLTR && openEnd && last ? leftSide : toPos.left;
            botRight = !docLTR ? rightSide : wrapX(to, dir, "after");
          }
          add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom);
          if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); }
          add(botLeft, toPos.top, botRight - botLeft, toPos.bottom);
        }

        if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; }
        if (cmpCoords(toPos, start) < 0) { start = toPos; }
        if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; }
        if (cmpCoords(toPos, end) < 0) { end = toPos; }
      });
      return {start: start, end: end}
    }

    var sFrom = range.from(), sTo = range.to();
    if (sFrom.line == sTo.line) {
      drawForLine(sFrom.line, sFrom.ch, sTo.ch);
    } else {
      var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
      var singleVLine = visualLine(fromLine) == visualLine(toLine);
      var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
      var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
      if (singleVLine) {
        if (leftEnd.top < rightStart.top - 2) {
          add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
          add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
        } else {
          add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
        }
      }
      if (leftEnd.bottom < rightStart.top)
        { add(leftSide, leftEnd.bottom, null, rightStart.top); }
    }

    output.appendChild(fragment);
  }

  // Cursor-blinking
  function restartBlink(cm) {
    if (!cm.state.focused) { return }
    var display = cm.display;
    clearInterval(display.blinker);
    var on = true;
    display.cursorDiv.style.visibility = "";
    if (cm.options.cursorBlinkRate > 0)
      { display.blinker = setInterval(function () {
        if (!cm.hasFocus()) { onBlur(cm); }
        display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden";
      }, cm.options.cursorBlinkRate); }
    else if (cm.options.cursorBlinkRate < 0)
      { display.cursorDiv.style.visibility = "hidden"; }
  }

  function ensureFocus(cm) {
    if (!cm.hasFocus()) {
      cm.display.input.focus();
      if (!cm.state.focused) { onFocus(cm); }
    }
  }

  function delayBlurEvent(cm) {
    cm.state.delayingBlurEvent = true;
    setTimeout(function () { if (cm.state.delayingBlurEvent) {
      cm.state.delayingBlurEvent = false;
      if (cm.state.focused) { onBlur(cm); }
    } }, 100);
  }

  function onFocus(cm, e) {
    if (cm.state.delayingBlurEvent && !cm.state.draggingText) { cm.state.delayingBlurEvent = false; }

    if (cm.options.readOnly == "nocursor") { return }
    if (!cm.state.focused) {
      signal(cm, "focus", cm, e);
      cm.state.focused = true;
      addClass(cm.display.wrapper, "CodeMirror-focused");
      // This test prevents this from firing when a context
      // menu is closed (since the input reset would kill the
      // select-all detection hack)
      if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
        cm.display.input.reset();
        if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730
      }
      cm.display.input.receivedFocus();
    }
    restartBlink(cm);
  }
  function onBlur(cm, e) {
    if (cm.state.delayingBlurEvent) { return }

    if (cm.state.focused) {
      signal(cm, "blur", cm, e);
      cm.state.focused = false;
      rmClass(cm.display.wrapper, "CodeMirror-focused");
    }
    clearInterval(cm.display.blinker);
    setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150);
  }

  // Read the actual heights of the rendered lines, and update their
  // stored heights to match.
  function updateHeightsInViewport(cm) {
    var display = cm.display;
    var prevBottom = display.lineDiv.offsetTop;
    for (var i = 0; i < display.view.length; i++) {
      var cur = display.view[i], wrapping = cm.options.lineWrapping;
      var height = (void 0), width = 0;
      if (cur.hidden) { continue }
      if (ie && ie_version < 8) {
        var bot = cur.node.offsetTop + cur.node.offsetHeight;
        height = bot - prevBottom;
        prevBottom = bot;
      } else {
        var box = cur.node.getBoundingClientRect();
        height = box.bottom - box.top;
        // Check that lines don't extend past the right of the current
        // editor width
        if (!wrapping && cur.text.firstChild)
          { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; }
      }
      var diff = cur.line.height - height;
      if (diff > .005 || diff < -.005) {
        updateLineHeight(cur.line, height);
        updateWidgetHeight(cur.line);
        if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
          { updateWidgetHeight(cur.rest[j]); } }
      }
      if (width > cm.display.sizerWidth) {
        var chWidth = Math.ceil(width / charWidth(cm.display));
        if (chWidth > cm.display.maxLineLength) {
          cm.display.maxLineLength = chWidth;
          cm.display.maxLine = cur.line;
          cm.display.maxLineChanged = true;
        }
      }
    }
  }

  // Read and store the height of line widgets associated with the
  // given line.
  function updateWidgetHeight(line) {
    if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {
      var w = line.widgets[i], parent = w.node.parentNode;
      if (parent) { w.height = parent.offsetHeight; }
    } }
  }

  // Compute the lines that are visible in a given viewport (defaults
  // the the current scroll position). viewport may contain top,
  // height, and ensure (see op.scrollToPos) properties.
  function visibleLines(display, doc, viewport) {
    var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
    top = Math.floor(top - paddingTop(display));
    var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;

    var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
    // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
    // forces those lines into the viewport (if possible).
    if (viewport && viewport.ensure) {
      var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
      if (ensureFrom < from) {
        from = ensureFrom;
        to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
      } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
        from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
        to = ensureTo;
      }
    }
    return {from: from, to: Math.max(to, from + 1)}
  }

  // SCROLLING THINGS INTO VIEW

  // If an editor sits on the top or bottom of the window, partially
  // scrolled out of view, this ensures that the cursor is visible.
  function maybeScrollWindow(cm, rect) {
    if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }

    var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
    if (rect.top + box.top < 0) { doScroll = true; }
    else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; }
    if (doScroll != null && !phantom) {
      var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n                         top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n                         height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n                         left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;"));
      cm.display.lineSpace.appendChild(scrollNode);
      scrollNode.scrollIntoView(doScroll);
      cm.display.lineSpace.removeChild(scrollNode);
    }
  }

  // Scroll a given position into view (immediately), verifying that
  // it actually became visible (as line heights are accurately
  // measured, the position of something may 'drift' during drawing).
  function scrollPosIntoView(cm, pos, end, margin) {
    if (margin == null) { margin = 0; }
    var rect;
    if (!cm.options.lineWrapping && pos == end) {
      // Set pos and end to the cursor positions around the character pos sticks to
      // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
      // If pos == Pos(_, 0, "before"), pos and end are unchanged
      pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos;
      end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos;
    }
    for (var limit = 0; limit < 5; limit++) {
      var changed = false;
      var coords = cursorCoords(cm, pos);
      var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
      rect = {left: Math.min(coords.left, endCoords.left),
              top: Math.min(coords.top, endCoords.top) - margin,
              right: Math.max(coords.left, endCoords.left),
              bottom: Math.max(coords.bottom, endCoords.bottom) + margin};
      var scrollPos = calculateScrollPos(cm, rect);
      var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
      if (scrollPos.scrollTop != null) {
        updateScrollTop(cm, scrollPos.scrollTop);
        if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; }
      }
      if (scrollPos.scrollLeft != null) {
        setScrollLeft(cm, scrollPos.scrollLeft);
        if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; }
      }
      if (!changed) { break }
    }
    return rect
  }

  // Scroll a given set of coordinates into view (immediately).
  function scrollIntoView(cm, rect) {
    var scrollPos = calculateScrollPos(cm, rect);
    if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); }
    if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); }
  }

  // Calculate a new scroll position needed to scroll the given
  // rectangle into view. Returns an object with scrollTop and
  // scrollLeft properties. When these are undefined, the
  // vertical/horizontal position does not need to be adjusted.
  function calculateScrollPos(cm, rect) {
    var display = cm.display, snapMargin = textHeight(cm.display);
    if (rect.top < 0) { rect.top = 0; }
    var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
    var screen = displayHeight(cm), result = {};
    if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; }
    var docBottom = cm.doc.height + paddingVert(display);
    var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin;
    if (rect.top < screentop) {
      result.scrollTop = atTop ? 0 : rect.top;
    } else if (rect.bottom > screentop + screen) {
      var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen);
      if (newTop != screentop) { result.scrollTop = newTop; }
    }

    var gutterSpace = cm.options.fixedGutter ? 0 : display.gutters.offsetWidth;
    var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft - gutterSpace;
    var screenw = displayWidth(cm) - display.gutters.offsetWidth;
    var tooWide = rect.right - rect.left > screenw;
    if (tooWide) { rect.right = rect.left + screenw; }
    if (rect.left < 10)
      { result.scrollLeft = 0; }
    else if (rect.left < screenleft)
      { result.scrollLeft = Math.max(0, rect.left + gutterSpace - (tooWide ? 0 : 10)); }
    else if (rect.right > screenw + screenleft - 3)
      { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; }
    return result
  }

  // Store a relative adjustment to the scroll position in the current
  // operation (to be applied when the operation finishes).
  function addToScrollTop(cm, top) {
    if (top == null) { return }
    resolveScrollToPos(cm);
    cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
  }

  // Make sure that at the end of the operation the current cursor is
  // shown.
  function ensureCursorVisible(cm) {
    resolveScrollToPos(cm);
    var cur = cm.getCursor();
    cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin};
  }

  function scrollToCoords(cm, x, y) {
    if (x != null || y != null) { resolveScrollToPos(cm); }
    if (x != null) { cm.curOp.scrollLeft = x; }
    if (y != null) { cm.curOp.scrollTop = y; }
  }

  function scrollToRange(cm, range) {
    resolveScrollToPos(cm);
    cm.curOp.scrollToPos = range;
  }

  // When an operation has its scrollToPos property set, and another
  // scroll action is applied before the end of the operation, this
  // 'simulates' scrolling that position into view in a cheap way, so
  // that the effect of intermediate scroll commands is not ignored.
  function resolveScrollToPos(cm) {
    var range = cm.curOp.scrollToPos;
    if (range) {
      cm.curOp.scrollToPos = null;
      var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to);
      scrollToCoordsRange(cm, from, to, range.margin);
    }
  }

  function scrollToCoordsRange(cm, from, to, margin) {
    var sPos = calculateScrollPos(cm, {
      left: Math.min(from.left, to.left),
      top: Math.min(from.top, to.top) - margin,
      right: Math.max(from.right, to.right),
      bottom: Math.max(from.bottom, to.bottom) + margin
    });
    scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop);
  }

  // Sync the scrollable area and scrollbars, ensure the viewport
  // covers the visible area.
  function updateScrollTop(cm, val) {
    if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
    if (!gecko) { updateDisplaySimple(cm, {top: val}); }
    setScrollTop(cm, val, true);
    if (gecko) { updateDisplaySimple(cm); }
    startWorker(cm, 100);
  }

  function setScrollTop(cm, val, forceScroll) {
    val = Math.max(0, Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val));
    if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
    cm.doc.scrollTop = val;
    cm.display.scrollbars.setScrollTop(val);
    if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; }
  }

  // Sync scroller and scrollbar, ensure the gutter elements are
  // aligned.
  function setScrollLeft(cm, val, isScroller, forceScroll) {
    val = Math.max(0, Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth));
    if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
    cm.doc.scrollLeft = val;
    alignHorizontally(cm);
    if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; }
    cm.display.scrollbars.setScrollLeft(val);
  }

  // SCROLLBARS

  // Prepare DOM reads needed to update the scrollbars. Done in one
  // shot to minimize update/measure roundtrips.
  function measureForScrollbars(cm) {
    var d = cm.display, gutterW = d.gutters.offsetWidth;
    var docH = Math.round(cm.doc.height + paddingVert(cm.display));
    return {
      clientHeight: d.scroller.clientHeight,
      viewHeight: d.wrapper.clientHeight,
      scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
      viewWidth: d.wrapper.clientWidth,
      barLeft: cm.options.fixedGutter ? gutterW : 0,
      docHeight: docH,
      scrollHeight: docH + scrollGap(cm) + d.barHeight,
      nativeBarWidth: d.nativeBarWidth,
      gutterWidth: gutterW
    }
  }

  var NativeScrollbars = function(place, scroll, cm) {
    this.cm = cm;
    var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
    var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
    vert.tabIndex = horiz.tabIndex = -1;
    place(vert); place(horiz);

    on(vert, "scroll", function () {
      if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); }
    });
    on(horiz, "scroll", function () {
      if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); }
    });

    this.checkedZeroWidth = false;
    // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
    if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; }
  };

  NativeScrollbars.prototype.update = function (measure) {
    var needsH = measure.scrollWidth > measure.clientWidth + 1;
    var needsV = measure.scrollHeight > measure.clientHeight + 1;
    var sWidth = measure.nativeBarWidth;

    if (needsV) {
      this.vert.style.display = "block";
      this.vert.style.bottom = needsH ? sWidth + "px" : "0";
      var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
      // A bug in IE8 can cause this value to be negative, so guard it.
      this.vert.firstChild.style.height =
        Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
    } else {
      this.vert.style.display = "";
      this.vert.firstChild.style.height = "0";
    }

    if (needsH) {
      this.horiz.style.display = "block";
      this.horiz.style.right = needsV ? sWidth + "px" : "0";
      this.horiz.style.left = measure.barLeft + "px";
      var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
      this.horiz.firstChild.style.width =
        Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
    } else {
      this.horiz.style.display = "";
      this.horiz.firstChild.style.width = "0";
    }

    if (!this.checkedZeroWidth && measure.clientHeight > 0) {
      if (sWidth == 0) { this.zeroWidthHack(); }
      this.checkedZeroWidth = true;
    }

    return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
  };

  NativeScrollbars.prototype.setScrollLeft = function (pos) {
    if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; }
    if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); }
  };

  NativeScrollbars.prototype.setScrollTop = function (pos) {
    if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; }
    if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); }
  };

  NativeScrollbars.prototype.zeroWidthHack = function () {
    var w = mac && !mac_geMountainLion ? "12px" : "18px";
    this.horiz.style.height = this.vert.style.width = w;
    this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
    this.disableHoriz = new Delayed;
    this.disableVert = new Delayed;
  };

  NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
    bar.style.pointerEvents = "auto";
    function maybeDisable() {
      // To find out whether the scrollbar is still visible, we
      // check whether the element under the pixel in the bottom
      // right corner of the scrollbar box is the scrollbar box
      // itself (when the bar is still visible) or its filler child
      // (when the bar is hidden). If it is still visible, we keep
      // it enabled, if it's hidden, we disable pointer events.
      var box = bar.getBoundingClientRect();
      var elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
          : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1);
      if (elt != bar) { bar.style.pointerEvents = "none"; }
      else { delay.set(1000, maybeDisable); }
    }
    delay.set(1000, maybeDisable);
  };

  NativeScrollbars.prototype.clear = function () {
    var parent = this.horiz.parentNode;
    parent.removeChild(this.horiz);
    parent.removeChild(this.vert);
  };

  var NullScrollbars = function () {};

  NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
  NullScrollbars.prototype.setScrollLeft = function () {};
  NullScrollbars.prototype.setScrollTop = function () {};
  NullScrollbars.prototype.clear = function () {};

  function updateScrollbars(cm, measure) {
    if (!measure) { measure = measureForScrollbars(cm); }
    var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
    updateScrollbarsInner(cm, measure);
    for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
      if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
        { updateHeightsInViewport(cm); }
      updateScrollbarsInner(cm, measureForScrollbars(cm));
      startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
    }
  }

  // Re-synchronize the fake scrollbars with the actual size of the
  // content.
  function updateScrollbarsInner(cm, measure) {
    var d = cm.display;
    var sizes = d.scrollbars.update(measure);

    d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
    d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
    d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent";

    if (sizes.right && sizes.bottom) {
      d.scrollbarFiller.style.display = "block";
      d.scrollbarFiller.style.height = sizes.bottom + "px";
      d.scrollbarFiller.style.width = sizes.right + "px";
    } else { d.scrollbarFiller.style.display = ""; }
    if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
      d.gutterFiller.style.display = "block";
      d.gutterFiller.style.height = sizes.bottom + "px";
      d.gutterFiller.style.width = measure.gutterWidth + "px";
    } else { d.gutterFiller.style.display = ""; }
  }

  var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};

  function initScrollbars(cm) {
    if (cm.display.scrollbars) {
      cm.display.scrollbars.clear();
      if (cm.display.scrollbars.addClass)
        { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
    }

    cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
      cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
      // Prevent clicks in the scrollbars from killing focus
      on(node, "mousedown", function () {
        if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); }
      });
      node.setAttribute("cm-not-content", "true");
    }, function (pos, axis) {
      if (axis == "horizontal") { setScrollLeft(cm, pos); }
      else { updateScrollTop(cm, pos); }
    }, cm);
    if (cm.display.scrollbars.addClass)
      { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
  }

  // Operations are used to wrap a series of changes to the editor
  // state in such a way that each change won't have to update the
  // cursor and display (which would be awkward, slow, and
  // error-prone). Instead, display updates are batched and then all
  // combined and executed at once.

  var nextOpId = 0;
  // Start a new operation.
  function startOperation(cm) {
    cm.curOp = {
      cm: cm,
      viewChanged: false,      // Flag that indicates that lines might need to be redrawn
      startHeight: cm.doc.height, // Used to detect need to update scrollbar
      forceUpdate: false,      // Used to force a redraw
      updateInput: 0,       // Whether to reset the input textarea
      typing: false,           // Whether this reset should be careful to leave existing text (for compositing)
      changeObjs: null,        // Accumulated changes, for firing change events
      cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
      cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
      selectionChanged: false, // Whether the selection needs to be redrawn
      updateMaxLine: false,    // Set when the widest line needs to be determined anew
      scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
      scrollToPos: null,       // Used to scroll to a specific position
      focus: false,
      id: ++nextOpId           // Unique ID
    };
    pushOperation(cm.curOp);
  }

  // Finish an operation, updating the display and signalling delayed events
  function endOperation(cm) {
    var op = cm.curOp;
    if (op) { finishOperation(op, function (group) {
      for (var i = 0; i < group.ops.length; i++)
        { group.ops[i].cm.curOp = null; }
      endOperations(group);
    }); }
  }

  // The DOM updates done when an operation finishes are batched so
  // that the minimum number of relayouts are required.
  function endOperations(group) {
    var ops = group.ops;
    for (var i = 0; i < ops.length; i++) // Read DOM
      { endOperation_R1(ops[i]); }
    for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
      { endOperation_W1(ops[i$1]); }
    for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
      { endOperation_R2(ops[i$2]); }
    for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
      { endOperation_W2(ops[i$3]); }
    for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
      { endOperation_finish(ops[i$4]); }
  }

  function endOperation_R1(op) {
    var cm = op.cm, display = cm.display;
    maybeClipScrollbars(cm);
    if (op.updateMaxLine) { findMaxLine(cm); }

    op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
      op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
                         op.scrollToPos.to.line >= display.viewTo) ||
      display.maxLineChanged && cm.options.lineWrapping;
    op.update = op.mustUpdate &&
      new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
  }

  function endOperation_W1(op) {
    op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
  }

  function endOperation_R2(op) {
    var cm = op.cm, display = cm.display;
    if (op.updatedDisplay) { updateHeightsInViewport(cm); }

    op.barMeasure = measureForScrollbars(cm);

    // If the max line changed since it was last measured, measure it,
    // and ensure the document's width matches it.
    // updateDisplay_W2 will use these properties to do the actual resizing
    if (display.maxLineChanged && !cm.options.lineWrapping) {
      op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
      cm.display.sizerWidth = op.adjustWidthTo;
      op.barMeasure.scrollWidth =
        Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
      op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
    }

    if (op.updatedDisplay || op.selectionChanged)
      { op.preparedSelection = display.input.prepareSelection(); }
  }

  function endOperation_W2(op) {
    var cm = op.cm;

    if (op.adjustWidthTo != null) {
      cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
      if (op.maxScrollLeft < cm.doc.scrollLeft)
        { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); }
      cm.display.maxLineChanged = false;
    }

    var takeFocus = op.focus && op.focus == activeElt();
    if (op.preparedSelection)
      { cm.display.input.showSelection(op.preparedSelection, takeFocus); }
    if (op.updatedDisplay || op.startHeight != cm.doc.height)
      { updateScrollbars(cm, op.barMeasure); }
    if (op.updatedDisplay)
      { setDocumentHeight(cm, op.barMeasure); }

    if (op.selectionChanged) { restartBlink(cm); }

    if (cm.state.focused && op.updateInput)
      { cm.display.input.reset(op.typing); }
    if (takeFocus) { ensureFocus(op.cm); }
  }

  function endOperation_finish(op) {
    var cm = op.cm, display = cm.display, doc = cm.doc;

    if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); }

    // Abort mouse wheel delta measurement, when scrolling explicitly
    if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
      { display.wheelStartX = display.wheelStartY = null; }

    // Propagate the scroll position to the actual DOM scroller
    if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); }

    if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); }
    // If we need to scroll a specific position into view, do so.
    if (op.scrollToPos) {
      var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
                                   clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
      maybeScrollWindow(cm, rect);
    }

    // Fire events for markers that are hidden/unidden by editing or
    // undoing
    var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
    if (hidden) { for (var i = 0; i < hidden.length; ++i)
      { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } }
    if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
      { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } }

    if (display.wrapper.offsetHeight)
      { doc.scrollTop = cm.display.scroller.scrollTop; }

    // Fire change events, and delayed event handlers
    if (op.changeObjs)
      { signal(cm, "changes", cm, op.changeObjs); }
    if (op.update)
      { op.update.finish(); }
  }

  // Run the given function in an operation
  function runInOp(cm, f) {
    if (cm.curOp) { return f() }
    startOperation(cm);
    try { return f() }
    finally { endOperation(cm); }
  }
  // Wraps a function in an operation. Returns the wrapped function.
  function operation(cm, f) {
    return function() {
      if (cm.curOp) { return f.apply(cm, arguments) }
      startOperation(cm);
      try { return f.apply(cm, arguments) }
      finally { endOperation(cm); }
    }
  }
  // Used to add methods to editor and doc instances, wrapping them in
  // operations.
  function methodOp(f) {
    return function() {
      if (this.curOp) { return f.apply(this, arguments) }
      startOperation(this);
      try { return f.apply(this, arguments) }
      finally { endOperation(this); }
    }
  }
  function docMethodOp(f) {
    return function() {
      var cm = this.cm;
      if (!cm || cm.curOp) { return f.apply(this, arguments) }
      startOperation(cm);
      try { return f.apply(this, arguments) }
      finally { endOperation(cm); }
    }
  }

  // HIGHLIGHT WORKER

  function startWorker(cm, time) {
    if (cm.doc.highlightFrontier < cm.display.viewTo)
      { cm.state.highlight.set(time, bind(highlightWorker, cm)); }
  }

  function highlightWorker(cm) {
    var doc = cm.doc;
    if (doc.highlightFrontier >= cm.display.viewTo) { return }
    var end = +new Date + cm.options.workTime;
    var context = getContextBefore(cm, doc.highlightFrontier);
    var changedLines = [];

    doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
      if (context.line >= cm.display.viewFrom) { // Visible
        var oldStyles = line.styles;
        var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null;
        var highlighted = highlightLine(cm, line, context, true);
        if (resetState) { context.state = resetState; }
        line.styles = highlighted.styles;
        var oldCls = line.styleClasses, newCls = highlighted.classes;
        if (newCls) { line.styleClasses = newCls; }
        else if (oldCls) { line.styleClasses = null; }
        var ischange = !oldStyles || oldStyles.length != line.styles.length ||
          oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
        for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; }
        if (ischange) { changedLines.push(context.line); }
        line.stateAfter = context.save();
        context.nextLine();
      } else {
        if (line.text.length <= cm.options.maxHighlightLength)
          { processLine(cm, line.text, context); }
        line.stateAfter = context.line % 5 == 0 ? context.save() : null;
        context.nextLine();
      }
      if (+new Date > end) {
        startWorker(cm, cm.options.workDelay);
        return true
      }
    });
    doc.highlightFrontier = context.line;
    doc.modeFrontier = Math.max(doc.modeFrontier, context.line);
    if (changedLines.length) { runInOp(cm, function () {
      for (var i = 0; i < changedLines.length; i++)
        { regLineChange(cm, changedLines[i], "text"); }
    }); }
  }

  // DISPLAY DRAWING

  var DisplayUpdate = function(cm, viewport, force) {
    var display = cm.display;

    this.viewport = viewport;
    // Store some values that we'll need later (but don't want to force a relayout for)
    this.visible = visibleLines(display, cm.doc, viewport);
    this.editorIsHidden = !display.wrapper.offsetWidth;
    this.wrapperHeight = display.wrapper.clientHeight;
    this.wrapperWidth = display.wrapper.clientWidth;
    this.oldDisplayWidth = displayWidth(cm);
    this.force = force;
    this.dims = getDimensions(cm);
    this.events = [];
  };

  DisplayUpdate.prototype.signal = function (emitter, type) {
    if (hasHandler(emitter, type))
      { this.events.push(arguments); }
  };
  DisplayUpdate.prototype.finish = function () {
    for (var i = 0; i < this.events.length; i++)
      { signal.apply(null, this.events[i]); }
  };

  function maybeClipScrollbars(cm) {
    var display = cm.display;
    if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
      display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
      display.heightForcer.style.height = scrollGap(cm) + "px";
      display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
      display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
      display.scrollbarsClipped = true;
    }
  }

  function selectionSnapshot(cm) {
    if (cm.hasFocus()) { return null }
    var active = activeElt();
    if (!active || !contains(cm.display.lineDiv, active)) { return null }
    var result = {activeElt: active};
    if (window.getSelection) {
      var sel = window.getSelection();
      if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
        result.anchorNode = sel.anchorNode;
        result.anchorOffset = sel.anchorOffset;
        result.focusNode = sel.focusNode;
        result.focusOffset = sel.focusOffset;
      }
    }
    return result
  }

  function restoreSelection(snapshot) {
    if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }
    snapshot.activeElt.focus();
    if (!/^(INPUT|TEXTAREA)$/.test(snapshot.activeElt.nodeName) &&
        snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
      var sel = window.getSelection(), range = document.createRange();
      range.setEnd(snapshot.anchorNode, snapshot.anchorOffset);
      range.collapse(false);
      sel.removeAllRanges();
      sel.addRange(range);
      sel.extend(snapshot.focusNode, snapshot.focusOffset);
    }
  }

  // Does the actual updating of the line display. Bails out
  // (returning false) when there is nothing to be done and forced is
  // false.
  function updateDisplayIfNeeded(cm, update) {
    var display = cm.display, doc = cm.doc;

    if (update.editorIsHidden) {
      resetView(cm);
      return false
    }

    // Bail out if the visible area is already rendered and nothing changed.
    if (!update.force &&
        update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
        display.renderedView == display.view && countDirtyView(cm) == 0)
      { return false }

    if (maybeUpdateLineNumberWidth(cm)) {
      resetView(cm);
      update.dims = getDimensions(cm);
    }

    // Compute a suitable new viewport (from & to)
    var end = doc.first + doc.size;
    var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
    var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
    if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); }
    if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); }
    if (sawCollapsedSpans) {
      from = visualLineNo(cm.doc, from);
      to = visualLineEndNo(cm.doc, to);
    }

    var different = from != display.viewFrom || to != display.viewTo ||
      display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
    adjustView(cm, from, to);

    display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
    // Position the mover div to align with the current scroll position
    cm.display.mover.style.top = display.viewOffset + "px";

    var toUpdate = countDirtyView(cm);
    if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
      { return false }

    // For big changes, we hide the enclosing element during the
    // update, since that speeds up the operations on most browsers.
    var selSnapshot = selectionSnapshot(cm);
    if (toUpdate > 4) { display.lineDiv.style.display = "none"; }
    patchDisplay(cm, display.updateLineNumbers, update.dims);
    if (toUpdate > 4) { display.lineDiv.style.display = ""; }
    display.renderedView = display.view;
    // There might have been a widget with a focused element that got
    // hidden or updated, if so re-focus it.
    restoreSelection(selSnapshot);

    // Prevent selection and cursors from interfering with the scroll
    // width and height.
    removeChildren(display.cursorDiv);
    removeChildren(display.selectionDiv);
    display.gutters.style.height = display.sizer.style.minHeight = 0;

    if (different) {
      display.lastWrapHeight = update.wrapperHeight;
      display.lastWrapWidth = update.wrapperWidth;
      startWorker(cm, 400);
    }

    display.updateLineNumbers = null;

    return true
  }

  function postUpdateDisplay(cm, update) {
    var viewport = update.viewport;

    for (var first = true;; first = false) {
      if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
        // Clip forced viewport to actual scrollable area.
        if (viewport && viewport.top != null)
          { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; }
        // Updated line heights might result in the drawn area not
        // actually covering the viewport. Keep looping until it does.
        update.visible = visibleLines(cm.display, cm.doc, viewport);
        if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
          { break }
      } else if (first) {
        update.visible = visibleLines(cm.display, cm.doc, viewport);
      }
      if (!updateDisplayIfNeeded(cm, update)) { break }
      updateHeightsInViewport(cm);
      var barMeasure = measureForScrollbars(cm);
      updateSelection(cm);
      updateScrollbars(cm, barMeasure);
      setDocumentHeight(cm, barMeasure);
      update.force = false;
    }

    update.signal(cm, "update", cm);
    if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
      update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
      cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
    }
  }

  function updateDisplaySimple(cm, viewport) {
    var update = new DisplayUpdate(cm, viewport);
    if (updateDisplayIfNeeded(cm, update)) {
      updateHeightsInViewport(cm);
      postUpdateDisplay(cm, update);
      var barMeasure = measureForScrollbars(cm);
      updateSelection(cm);
      updateScrollbars(cm, barMeasure);
      setDocumentHeight(cm, barMeasure);
      update.finish();
    }
  }

  // Sync the actual display DOM structure with display.view, removing
  // nodes for lines that are no longer in view, and creating the ones
  // that are not there yet, and updating the ones that are out of
  // date.
  function patchDisplay(cm, updateNumbersFrom, dims) {
    var display = cm.display, lineNumbers = cm.options.lineNumbers;
    var container = display.lineDiv, cur = container.firstChild;

    function rm(node) {
      var next = node.nextSibling;
      // Works around a throw-scroll bug in OS X Webkit
      if (webkit && mac && cm.display.currentWheelTarget == node)
        { node.style.display = "none"; }
      else
        { node.parentNode.removeChild(node); }
      return next
    }

    var view = display.view, lineN = display.viewFrom;
    // Loop over the elements in the view, syncing cur (the DOM nodes
    // in display.lineDiv) with the view as we go.
    for (var i = 0; i < view.length; i++) {
      var lineView = view[i];
      if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
        var node = buildLineElement(cm, lineView, lineN, dims);
        container.insertBefore(node, cur);
      } else { // Already drawn
        while (cur != lineView.node) { cur = rm(cur); }
        var updateNumber = lineNumbers && updateNumbersFrom != null &&
          updateNumbersFrom <= lineN && lineView.lineNumber;
        if (lineView.changes) {
          if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; }
          updateLineForChanges(cm, lineView, lineN, dims);
        }
        if (updateNumber) {
          removeChildren(lineView.lineNumber);
          lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
        }
        cur = lineView.node.nextSibling;
      }
      lineN += lineView.size;
    }
    while (cur) { cur = rm(cur); }
  }

  function updateGutterSpace(display) {
    var width = display.gutters.offsetWidth;
    display.sizer.style.marginLeft = width + "px";
  }

  function setDocumentHeight(cm, measure) {
    cm.display.sizer.style.minHeight = measure.docHeight + "px";
    cm.display.heightForcer.style.top = measure.docHeight + "px";
    cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px";
  }

  // Re-align line numbers and gutter marks to compensate for
  // horizontal scrolling.
  function alignHorizontally(cm) {
    var display = cm.display, view = display.view;
    if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
    var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
    var gutterW = display.gutters.offsetWidth, left = comp + "px";
    for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
      if (cm.options.fixedGutter) {
        if (view[i].gutter)
          { view[i].gutter.style.left = left; }
        if (view[i].gutterBackground)
          { view[i].gutterBackground.style.left = left; }
      }
      var align = view[i].alignable;
      if (align) { for (var j = 0; j < align.length; j++)
        { align[j].style.left = left; } }
    } }
    if (cm.options.fixedGutter)
      { display.gutters.style.left = (comp + gutterW) + "px"; }
  }

  // Used to ensure that the line number gutter is still the right
  // size for the current document size. Returns true when an update
  // is needed.
  function maybeUpdateLineNumberWidth(cm) {
    if (!cm.options.lineNumbers) { return false }
    var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
    if (last.length != display.lineNumChars) {
      var test = display.measure.appendChild(elt("div", [elt("div", last)],
                                                 "CodeMirror-linenumber CodeMirror-gutter-elt"));
      var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
      display.lineGutter.style.width = "";
      display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
      display.lineNumWidth = display.lineNumInnerWidth + padding;
      display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
      display.lineGutter.style.width = display.lineNumWidth + "px";
      updateGutterSpace(cm.display);
      return true
    }
    return false
  }

  function getGutters(gutters, lineNumbers) {
    var result = [], sawLineNumbers = false;
    for (var i = 0; i < gutters.length; i++) {
      var name = gutters[i], style = null;
      if (typeof name != "string") { style = name.style; name = name.className; }
      if (name == "CodeMirror-linenumbers") {
        if (!lineNumbers) { continue }
        else { sawLineNumbers = true; }
      }
      result.push({className: name, style: style});
    }
    if (lineNumbers && !sawLineNumbers) { result.push({className: "CodeMirror-linenumbers", style: null}); }
    return result
  }

  // Rebuild the gutter elements, ensure the margin to the left of the
  // code matches their width.
  function renderGutters(display) {
    var gutters = display.gutters, specs = display.gutterSpecs;
    removeChildren(gutters);
    display.lineGutter = null;
    for (var i = 0; i < specs.length; ++i) {
      var ref = specs[i];
      var className = ref.className;
      var style = ref.style;
      var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className));
      if (style) { gElt.style.cssText = style; }
      if (className == "CodeMirror-linenumbers") {
        display.lineGutter = gElt;
        gElt.style.width = (display.lineNumWidth || 1) + "px";
      }
    }
    gutters.style.display = specs.length ? "" : "none";
    updateGutterSpace(display);
  }

  function updateGutters(cm) {
    renderGutters(cm.display);
    regChange(cm);
    alignHorizontally(cm);
  }

  // The display handles the DOM integration, both for input reading
  // and content drawing. It holds references to DOM nodes and
  // display-related state.

  function Display(place, doc, input, options) {
    var d = this;
    this.input = input;

    // Covers bottom-right square when both scrollbars are present.
    d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
    d.scrollbarFiller.setAttribute("cm-not-content", "true");
    // Covers bottom of gutter when coverGutterNextToScrollbar is on
    // and h scrollbar is present.
    d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
    d.gutterFiller.setAttribute("cm-not-content", "true");
    // Will contain the actual code, positioned to cover the viewport.
    d.lineDiv = eltP("div", null, "CodeMirror-code");
    // Elements are added to these to represent selection and cursors.
    d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
    d.cursorDiv = elt("div", null, "CodeMirror-cursors");
    // A visibility: hidden element used to find the size of things.
    d.measure = elt("div", null, "CodeMirror-measure");
    // When lines outside of the viewport are measured, they are drawn in this.
    d.lineMeasure = elt("div", null, "CodeMirror-measure");
    // Wraps everything that needs to exist inside the vertically-padded coordinate system
    d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
                      null, "position: relative; outline: none");
    var lines = eltP("div", [d.lineSpace], "CodeMirror-lines");
    // Moved around its parent to cover visible view.
    d.mover = elt("div", [lines], null, "position: relative");
    // Set to the height of the document, allowing scrolling.
    d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
    d.sizerWidth = null;
    // Behavior of elts with overflow: auto and padding is
    // inconsistent across browsers. This is used to ensure the
    // scrollable area is big enough.
    d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
    // Will contain the gutters, if any.
    d.gutters = elt("div", null, "CodeMirror-gutters");
    d.lineGutter = null;
    // Actual scrollable element.
    d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
    d.scroller.setAttribute("tabIndex", "-1");
    // The element in which the editor lives.
    d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");

    // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
    if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
    if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; }

    if (place) {
      if (place.appendChild) { place.appendChild(d.wrapper); }
      else { place(d.wrapper); }
    }

    // Current rendered range (may be bigger than the view window).
    d.viewFrom = d.viewTo = doc.first;
    d.reportedViewFrom = d.reportedViewTo = doc.first;
    // Information about the rendered lines.
    d.view = [];
    d.renderedView = null;
    // Holds info about a single rendered line when it was rendered
    // for measurement, while not in view.
    d.externalMeasured = null;
    // Empty space (in pixels) above the view
    d.viewOffset = 0;
    d.lastWrapHeight = d.lastWrapWidth = 0;
    d.updateLineNumbers = null;

    d.nativeBarWidth = d.barHeight = d.barWidth = 0;
    d.scrollbarsClipped = false;

    // Used to only resize the line number gutter when necessary (when
    // the amount of lines crosses a boundary that makes its width change)
    d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
    // Set to true when a non-horizontal-scrolling line widget is
    // added. As an optimization, line widget aligning is skipped when
    // this is false.
    d.alignWidgets = false;

    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;

    // Tracks the maximum line length so that the horizontal scrollbar
    // can be kept static when scrolling.
    d.maxLine = null;
    d.maxLineLength = 0;
    d.maxLineChanged = false;

    // Used for measuring wheel scrolling granularity
    d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;

    // True when shift is held down.
    d.shift = false;

    // Used to track whether anything happened since the context menu
    // was opened.
    d.selForContextMenu = null;

    d.activeTouch = null;

    d.gutterSpecs = getGutters(options.gutters, options.lineNumbers);
    renderGutters(d);

    input.init(d);
  }

  // Since the delta values reported on mouse wheel events are
  // unstandardized between browsers and even browser versions, and
  // generally horribly unpredictable, this code starts by measuring
  // the scroll effect that the first few mouse wheel events have,
  // and, from that, detects the way it can convert deltas to pixel
  // offsets afterwards.
  //
  // The reason we want to know the amount a wheel event will scroll
  // is that it gives us a chance to update the display before the
  // actual scrolling happens, reducing flickering.

  var wheelSamples = 0, wheelPixelsPerUnit = null;
  // Fill in a browser-detected starting value on browsers where we
  // know one. These don't have to be accurate -- the result of them
  // being wrong would just be a slight flicker on the first wheel
  // scroll (if it is large enough).
  if (ie) { wheelPixelsPerUnit = -.53; }
  else if (gecko) { wheelPixelsPerUnit = 15; }
  else if (chrome) { wheelPixelsPerUnit = -.7; }
  else if (safari) { wheelPixelsPerUnit = -1/3; }

  function wheelEventDelta(e) {
    var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
    if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; }
    if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; }
    else if (dy == null) { dy = e.wheelDelta; }
    return {x: dx, y: dy}
  }
  function wheelEventPixels(e) {
    var delta = wheelEventDelta(e);
    delta.x *= wheelPixelsPerUnit;
    delta.y *= wheelPixelsPerUnit;
    return delta
  }

  function onScrollWheel(cm, e) {
    var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;

    var display = cm.display, scroll = display.scroller;
    // Quit if there's nothing to scroll here
    var canScrollX = scroll.scrollWidth > scroll.clientWidth;
    var canScrollY = scroll.scrollHeight > scroll.clientHeight;
    if (!(dx && canScrollX || dy && canScrollY)) { return }

    // Webkit browsers on OS X abort momentum scrolls when the target
    // of the scroll event is removed from the scrollable element.
    // This hack (see related code in patchDisplay) makes sure the
    // element is kept around.
    if (dy && mac && webkit) {
      outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
        for (var i = 0; i < view.length; i++) {
          if (view[i].node == cur) {
            cm.display.currentWheelTarget = cur;
            break outer
          }
        }
      }
    }

    // On some browsers, horizontal scrolling will cause redraws to
    // happen before the gutter has been realigned, causing it to
    // wriggle around in a most unseemly way. When we have an
    // estimated pixels/delta value, we just handle horizontal
    // scrolling entirely here. It'll be slightly off from native, but
    // better than glitching out.
    if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
      if (dy && canScrollY)
        { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); }
      setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit));
      // Only prevent default scrolling if vertical scrolling is
      // actually possible. Otherwise, it causes vertical scroll
      // jitter on OSX trackpads when deltaX is small and deltaY
      // is large (issue #3579)
      if (!dy || (dy && canScrollY))
        { e_preventDefault(e); }
      display.wheelStartX = null; // Abort measurement, if in progress
      return
    }

    // 'Project' the visible viewport to cover the area that is being
    // scrolled into view (if we know enough to estimate it).
    if (dy && wheelPixelsPerUnit != null) {
      var pixels = dy * wheelPixelsPerUnit;
      var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
      if (pixels < 0) { top = Math.max(0, top + pixels - 50); }
      else { bot = Math.min(cm.doc.height, bot + pixels + 50); }
      updateDisplaySimple(cm, {top: top, bottom: bot});
    }

    if (wheelSamples < 20) {
      if (display.wheelStartX == null) {
        display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
        display.wheelDX = dx; display.wheelDY = dy;
        setTimeout(function () {
          if (display.wheelStartX == null) { return }
          var movedX = scroll.scrollLeft - display.wheelStartX;
          var movedY = scroll.scrollTop - display.wheelStartY;
          var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
            (movedX && display.wheelDX && movedX / display.wheelDX);
          display.wheelStartX = display.wheelStartY = null;
          if (!sample) { return }
          wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
          ++wheelSamples;
        }, 200);
      } else {
        display.wheelDX += dx; display.wheelDY += dy;
      }
    }
  }

  // Selection objects are immutable. A new one is created every time
  // the selection changes. A selection is one or more non-overlapping
  // (and non-touching) ranges, sorted, and an integer that indicates
  // which one is the primary selection (the one that's scrolled into
  // view, that getCursor returns, etc).
  var Selection = function(ranges, primIndex) {
    this.ranges = ranges;
    this.primIndex = primIndex;
  };

  Selection.prototype.primary = function () { return this.ranges[this.primIndex] };

  Selection.prototype.equals = function (other) {
    if (other == this) { return true }
    if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
    for (var i = 0; i < this.ranges.length; i++) {
      var here = this.ranges[i], there = other.ranges[i];
      if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
    }
    return true
  };

  Selection.prototype.deepCopy = function () {
    var out = [];
    for (var i = 0; i < this.ranges.length; i++)
      { out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); }
    return new Selection(out, this.primIndex)
  };

  Selection.prototype.somethingSelected = function () {
    for (var i = 0; i < this.ranges.length; i++)
      { if (!this.ranges[i].empty()) { return true } }
    return false
  };

  Selection.prototype.contains = function (pos, end) {
    if (!end) { end = pos; }
    for (var i = 0; i < this.ranges.length; i++) {
      var range = this.ranges[i];
      if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
        { return i }
    }
    return -1
  };

  var Range = function(anchor, head) {
    this.anchor = anchor; this.head = head;
  };

  Range.prototype.from = function () { return minPos(this.anchor, this.head) };
  Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
  Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };

  // Take an unsorted, potentially overlapping set of ranges, and
  // build a selection out of it. 'Consumes' ranges array (modifying
  // it).
  function normalizeSelection(cm, ranges, primIndex) {
    var mayTouch = cm && cm.options.selectionsMayTouch;
    var prim = ranges[primIndex];
    ranges.sort(function (a, b) { return cmp(a.from(), b.from()); });
    primIndex = indexOf(ranges, prim);
    for (var i = 1; i < ranges.length; i++) {
      var cur = ranges[i], prev = ranges[i - 1];
      var diff = cmp(prev.to(), cur.from());
      if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) {
        var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
        var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
        if (i <= primIndex) { --primIndex; }
        ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
      }
    }
    return new Selection(ranges, primIndex)
  }

  function simpleSelection(anchor, head) {
    return new Selection([new Range(anchor, head || anchor)], 0)
  }

  // Compute the position of the end of a change (its 'to' property
  // refers to the pre-change end).
  function changeEnd(change) {
    if (!change.text) { return change.to }
    return Pos(change.from.line + change.text.length - 1,
               lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
  }

  // Adjust a position to refer to the post-change position of the
  // same text, or the end of the change if the change covers it.
  function adjustForChange(pos, change) {
    if (cmp(pos, change.from) < 0) { return pos }
    if (cmp(pos, change.to) <= 0) { return changeEnd(change) }

    var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
    if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; }
    return Pos(line, ch)
  }

  function computeSelAfterChange(doc, change) {
    var out = [];
    for (var i = 0; i < doc.sel.ranges.length; i++) {
      var range = doc.sel.ranges[i];
      out.push(new Range(adjustForChange(range.anchor, change),
                         adjustForChange(range.head, change)));
    }
    return normalizeSelection(doc.cm, out, doc.sel.primIndex)
  }

  function offsetPos(pos, old, nw) {
    if (pos.line == old.line)
      { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
    else
      { return Pos(nw.line + (pos.line - old.line), pos.ch) }
  }

  // Used by replaceSelections to allow moving the selection to the
  // start or around the replaced test. Hint may be "start" or "around".
  function computeReplacedSel(doc, changes, hint) {
    var out = [];
    var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
    for (var i = 0; i < changes.length; i++) {
      var change = changes[i];
      var from = offsetPos(change.from, oldPrev, newPrev);
      var to = offsetPos(changeEnd(change), oldPrev, newPrev);
      oldPrev = change.to;
      newPrev = to;
      if (hint == "around") {
        var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
        out[i] = new Range(inv ? to : from, inv ? from : to);
      } else {
        out[i] = new Range(from, from);
      }
    }
    return new Selection(out, doc.sel.primIndex)
  }

  // Used to get the editor into a consistent state again when options change.

  function loadMode(cm) {
    cm.doc.mode = getMode(cm.options, cm.doc.modeOption);
    resetModeState(cm);
  }

  function resetModeState(cm) {
    cm.doc.iter(function (line) {
      if (line.stateAfter) { line.stateAfter = null; }
      if (line.styles) { line.styles = null; }
    });
    cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first;
    startWorker(cm, 100);
    cm.state.modeGen++;
    if (cm.curOp) { regChange(cm); }
  }

  // DOCUMENT DATA STRUCTURE

  // By default, updates that start and end at the beginning of a line
  // are treated specially, in order to make the association of line
  // widgets and marker elements with the text behave more intuitive.
  function isWholeLineUpdate(doc, change) {
    return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
      (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
  }

  // Perform a change on the document data structure.
  function updateDoc(doc, change, markedSpans, estimateHeight) {
    function spansFor(n) {return markedSpans ? markedSpans[n] : null}
    function update(line, text, spans) {
      updateLine(line, text, spans, estimateHeight);
      signalLater(line, "change", line, change);
    }
    function linesFor(start, end) {
      var result = [];
      for (var i = start; i < end; ++i)
        { result.push(new Line(text[i], spansFor(i), estimateHeight)); }
      return result
    }

    var from = change.from, to = change.to, text = change.text;
    var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
    var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;

    // Adjust the line structure
    if (change.full) {
      doc.insert(0, linesFor(0, text.length));
      doc.remove(text.length, doc.size - text.length);
    } else if (isWholeLineUpdate(doc, change)) {
      // This is a whole-line replace. Treated specially to make
      // sure line objects move the way they are supposed to.
      var added = linesFor(0, text.length - 1);
      update(lastLine, lastLine.text, lastSpans);
      if (nlines) { doc.remove(from.line, nlines); }
      if (added.length) { doc.insert(from.line, added); }
    } else if (firstLine == lastLine) {
      if (text.length == 1) {
        update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
      } else {
        var added$1 = linesFor(1, text.length - 1);
        added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
        update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
        doc.insert(from.line + 1, added$1);
      }
    } else if (text.length == 1) {
      update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
      doc.remove(from.line + 1, nlines);
    } else {
      update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
      update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
      var added$2 = linesFor(1, text.length - 1);
      if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); }
      doc.insert(from.line + 1, added$2);
    }

    signalLater(doc, "change", doc, change);
  }

  // Call f for all linked documents.
  function linkedDocs(doc, f, sharedHistOnly) {
    function propagate(doc, skip, sharedHist) {
      if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
        var rel = doc.linked[i];
        if (rel.doc == skip) { continue }
        var shared = sharedHist && rel.sharedHist;
        if (sharedHistOnly && !shared) { continue }
        f(rel.doc, shared);
        propagate(rel.doc, doc, shared);
      } }
    }
    propagate(doc, null, true);
  }

  // Attach a document to an editor.
  function attachDoc(cm, doc) {
    if (doc.cm) { throw new Error("This document is already in use.") }
    cm.doc = doc;
    doc.cm = cm;
    estimateLineHeights(cm);
    loadMode(cm);
    setDirectionClass(cm);
    if (!cm.options.lineWrapping) { findMaxLine(cm); }
    cm.options.mode = doc.modeOption;
    regChange(cm);
  }

  function setDirectionClass(cm) {
  (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl");
  }

  function directionChanged(cm) {
    runInOp(cm, function () {
      setDirectionClass(cm);
      regChange(cm);
    });
  }

  function History(startGen) {
    // Arrays of change events and selections. Doing something adds an
    // event to done and clears undo. Undoing moves events from done
    // to undone, redoing moves them in the other direction.
    this.done = []; this.undone = [];
    this.undoDepth = Infinity;
    // Used to track when changes can be merged into a single undo
    // event
    this.lastModTime = this.lastSelTime = 0;
    this.lastOp = this.lastSelOp = null;
    this.lastOrigin = this.lastSelOrigin = null;
    // Used by the isClean() method
    this.generation = this.maxGeneration = startGen || 1;
  }

  // Create a history change event from an updateDoc-style change
  // object.
  function historyChangeFromChange(doc, change) {
    var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
    attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
    linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true);
    return histChange
  }

  // Pop all selection events off the end of a history array. Stop at
  // a change event.
  function clearSelectionEvents(array) {
    while (array.length) {
      var last = lst(array);
      if (last.ranges) { array.pop(); }
      else { break }
    }
  }

  // Find the top change event in the history. Pop off selection
  // events that are in the way.
  function lastChangeEvent(hist, force) {
    if (force) {
      clearSelectionEvents(hist.done);
      return lst(hist.done)
    } else if (hist.done.length && !lst(hist.done).ranges) {
      return lst(hist.done)
    } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
      hist.done.pop();
      return lst(hist.done)
    }
  }

  // Register a change in the history. Merges changes that are within
  // a single operation, or are close together with an origin that
  // allows merging (starting with "+") into a single event.
  function addChangeToHistory(doc, change, selAfter, opId) {
    var hist = doc.history;
    hist.undone.length = 0;
    var time = +new Date, cur;
    var last;

    if ((hist.lastOp == opId ||
         hist.lastOrigin == change.origin && change.origin &&
         ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||
          change.origin.charAt(0) == "*")) &&
        (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
      // Merge this change into the last event
      last = lst(cur.changes);
      if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
        // Optimized case for simple insertion -- don't want to add
        // new changesets for every character typed
        last.to = changeEnd(change);
      } else {
        // Add new sub-event
        cur.changes.push(historyChangeFromChange(doc, change));
      }
    } else {
      // Can not be merged, start a new event.
      var before = lst(hist.done);
      if (!before || !before.ranges)
        { pushSelectionToHistory(doc.sel, hist.done); }
      cur = {changes: [historyChangeFromChange(doc, change)],
             generation: hist.generation};
      hist.done.push(cur);
      while (hist.done.length > hist.undoDepth) {
        hist.done.shift();
        if (!hist.done[0].ranges) { hist.done.shift(); }
      }
    }
    hist.done.push(selAfter);
    hist.generation = ++hist.maxGeneration;
    hist.lastModTime = hist.lastSelTime = time;
    hist.lastOp = hist.lastSelOp = opId;
    hist.lastOrigin = hist.lastSelOrigin = change.origin;

    if (!last) { signal(doc, "historyAdded"); }
  }

  function selectionEventCanBeMerged(doc, origin, prev, sel) {
    var ch = origin.charAt(0);
    return ch == "*" ||
      ch == "+" &&
      prev.ranges.length == sel.ranges.length &&
      prev.somethingSelected() == sel.somethingSelected() &&
      new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
  }

  // Called whenever the selection changes, sets the new selection as
  // the pending selection in the history, and pushes the old pending
  // selection into the 'done' array when it was significantly
  // different (in number of selected ranges, emptiness, or time).
  function addSelectionToHistory(doc, sel, opId, options) {
    var hist = doc.history, origin = options && options.origin;

    // A new event is started when the previous origin does not match
    // the current, or the origins don't allow matching. Origins
    // starting with * are always merged, those starting with + are
    // merged when similar and close together in time.
    if (opId == hist.lastSelOp ||
        (origin && hist.lastSelOrigin == origin &&
         (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
          selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
      { hist.done[hist.done.length - 1] = sel; }
    else
      { pushSelectionToHistory(sel, hist.done); }

    hist.lastSelTime = +new Date;
    hist.lastSelOrigin = origin;
    hist.lastSelOp = opId;
    if (options && options.clearRedo !== false)
      { clearSelectionEvents(hist.undone); }
  }

  function pushSelectionToHistory(sel, dest) {
    var top = lst(dest);
    if (!(top && top.ranges && top.equals(sel)))
      { dest.push(sel); }
  }

  // Used to store marked span information in the history.
  function attachLocalSpans(doc, change, from, to) {
    var existing = change["spans_" + doc.id], n = 0;
    doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
      if (line.markedSpans)
        { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; }
      ++n;
    });
  }

  // When un/re-doing restores text containing marked spans, those
  // that have been explicitly cleared should not be restored.
  function removeClearedSpans(spans) {
    if (!spans) { return null }
    var out;
    for (var i = 0; i < spans.length; ++i) {
      if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } }
      else if (out) { out.push(spans[i]); }
    }
    return !out ? spans : out.length ? out : null
  }

  // Retrieve and filter the old marked spans stored in a change event.
  function getOldSpans(doc, change) {
    var found = change["spans_" + doc.id];
    if (!found) { return null }
    var nw = [];
    for (var i = 0; i < change.text.length; ++i)
      { nw.push(removeClearedSpans(found[i])); }
    return nw
  }

  // Used for un/re-doing changes from the history. Combines the
  // result of computing the existing spans with the set of spans that
  // existed in the history (so that deleting around a span and then
  // undoing brings back the span).
  function mergeOldSpans(doc, change) {
    var old = getOldSpans(doc, change);
    var stretched = stretchSpansOverChange(doc, change);
    if (!old) { return stretched }
    if (!stretched) { return old }

    for (var i = 0; i < old.length; ++i) {
      var oldCur = old[i], stretchCur = stretched[i];
      if (oldCur && stretchCur) {
        spans: for (var j = 0; j < stretchCur.length; ++j) {
          var span = stretchCur[j];
          for (var k = 0; k < oldCur.length; ++k)
            { if (oldCur[k].marker == span.marker) { continue spans } }
          oldCur.push(span);
        }
      } else if (stretchCur) {
        old[i] = stretchCur;
      }
    }
    return old
  }

  // Used both to provide a JSON-safe object in .getHistory, and, when
  // detaching a document, to split the history in two
  function copyHistoryArray(events, newGroup, instantiateSel) {
    var copy = [];
    for (var i = 0; i < events.length; ++i) {
      var event = events[i];
      if (event.ranges) {
        copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
        continue
      }
      var changes = event.changes, newChanges = [];
      copy.push({changes: newChanges});
      for (var j = 0; j < changes.length; ++j) {
        var change = changes[j], m = (void 0);
        newChanges.push({from: change.from, to: change.to, text: change.text});
        if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
          if (indexOf(newGroup, Number(m[1])) > -1) {
            lst(newChanges)[prop] = change[prop];
            delete change[prop];
          }
        } } }
      }
    }
    return copy
  }

  // The 'scroll' parameter given to many of these indicated whether
  // the new cursor position should be scrolled into view after
  // modifying the selection.

  // If shift is held or the extend flag is set, extends a range to
  // include a given position (and optionally a second position).
  // Otherwise, simply returns the range between the given positions.
  // Used for cursor motion and such.
  function extendRange(range, head, other, extend) {
    if (extend) {
      var anchor = range.anchor;
      if (other) {
        var posBefore = cmp(head, anchor) < 0;
        if (posBefore != (cmp(other, anchor) < 0)) {
          anchor = head;
          head = other;
        } else if (posBefore != (cmp(head, other) < 0)) {
          head = other;
        }
      }
      return new Range(anchor, head)
    } else {
      return new Range(other || head, head)
    }
  }

  // Extend the primary selection range, discard the rest.
  function extendSelection(doc, head, other, options, extend) {
    if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); }
    setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options);
  }

  // Extend all selections (pos is an array of selections with length
  // equal the number of selections)
  function extendSelections(doc, heads, options) {
    var out = [];
    var extend = doc.cm && (doc.cm.display.shift || doc.extend);
    for (var i = 0; i < doc.sel.ranges.length; i++)
      { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); }
    var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex);
    setSelection(doc, newSel, options);
  }

  // Updates a single range in the selection.
  function replaceOneSelection(doc, i, range, options) {
    var ranges = doc.sel.ranges.slice(0);
    ranges[i] = range;
    setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options);
  }

  // Reset the selection to a single range.
  function setSimpleSelection(doc, anchor, head, options) {
    setSelection(doc, simpleSelection(anchor, head), options);
  }

  // Give beforeSelectionChange handlers a change to influence a
  // selection update.
  function filterSelectionChange(doc, sel, options) {
    var obj = {
      ranges: sel.ranges,
      update: function(ranges) {
        this.ranges = [];
        for (var i = 0; i < ranges.length; i++)
          { this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
                                     clipPos(doc, ranges[i].head)); }
      },
      origin: options && options.origin
    };
    signal(doc, "beforeSelectionChange", doc, obj);
    if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); }
    if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) }
    else { return sel }
  }

  function setSelectionReplaceHistory(doc, sel, options) {
    var done = doc.history.done, last = lst(done);
    if (last && last.ranges) {
      done[done.length - 1] = sel;
      setSelectionNoUndo(doc, sel, options);
    } else {
      setSelection(doc, sel, options);
    }
  }

  // Set a new selection.
  function setSelection(doc, sel, options) {
    setSelectionNoUndo(doc, sel, options);
    addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
  }

  function setSelectionNoUndo(doc, sel, options) {
    if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
      { sel = filterSelectionChange(doc, sel, options); }

    var bias = options && options.bias ||
      (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
    setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));

    if (!(options && options.scroll === false) && doc.cm && doc.cm.getOption("readOnly") != "nocursor")
      { ensureCursorVisible(doc.cm); }
  }

  function setSelectionInner(doc, sel) {
    if (sel.equals(doc.sel)) { return }

    doc.sel = sel;

    if (doc.cm) {
      doc.cm.curOp.updateInput = 1;
      doc.cm.curOp.selectionChanged = true;
      signalCursorActivity(doc.cm);
    }
    signalLater(doc, "cursorActivity", doc);
  }

  // Verify that the selection does not partially select any atomic
  // marked ranges.
  function reCheckSelection(doc) {
    setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false));
  }

  // Return a selection that does not partially select any atomic
  // ranges.
  function skipAtomicInSelection(doc, sel, bias, mayClear) {
    var out;
    for (var i = 0; i < sel.ranges.length; i++) {
      var range = sel.ranges[i];
      var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
      var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);
      var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);
      if (out || newAnchor != range.anchor || newHead != range.head) {
        if (!out) { out = sel.ranges.slice(0, i); }
        out[i] = new Range(newAnchor, newHead);
      }
    }
    return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel
  }

  function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
    var line = getLine(doc, pos.line);
    if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
      var sp = line.markedSpans[i], m = sp.marker;

      // Determine if we should prevent the cursor being placed to the left/right of an atomic marker
      // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it
      // is with selectLeft/Right
      var preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft;
      var preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight;

      if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
          (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
        if (mayClear) {
          signal(m, "beforeCursorEnter");
          if (m.explicitlyCleared) {
            if (!line.markedSpans) { break }
            else {--i; continue}
          }
        }
        if (!m.atomic) { continue }

        if (oldPos) {
          var near = m.find(dir < 0 ? 1 : -1), diff = (void 0);
          if (dir < 0 ? preventCursorRight : preventCursorLeft)
            { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); }
          if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
            { return skipAtomicInner(doc, near, pos, dir, mayClear) }
        }

        var far = m.find(dir < 0 ? -1 : 1);
        if (dir < 0 ? preventCursorLeft : preventCursorRight)
          { far = movePos(doc, far, dir, far.line == pos.line ? line : null); }
        return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
      }
    } }
    return pos
  }

  // Ensure a given position is not inside an atomic range.
  function skipAtomic(doc, pos, oldPos, bias, mayClear) {
    var dir = bias || 1;
    var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
        (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
        skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
        (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));
    if (!found) {
      doc.cantEdit = true;
      return Pos(doc.first, 0)
    }
    return found
  }

  function movePos(doc, pos, dir, line) {
    if (dir < 0 && pos.ch == 0) {
      if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
      else { return null }
    } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
      if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
      else { return null }
    } else {
      return new Pos(pos.line, pos.ch + dir)
    }
  }

  function selectAll(cm) {
    cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);
  }

  // UPDATING

  // Allow "beforeChange" event handlers to influence a change
  function filterChange(doc, change, update) {
    var obj = {
      canceled: false,
      from: change.from,
      to: change.to,
      text: change.text,
      origin: change.origin,
      cancel: function () { return obj.canceled = true; }
    };
    if (update) { obj.update = function (from, to, text, origin) {
      if (from) { obj.from = clipPos(doc, from); }
      if (to) { obj.to = clipPos(doc, to); }
      if (text) { obj.text = text; }
      if (origin !== undefined) { obj.origin = origin; }
    }; }
    signal(doc, "beforeChange", doc, obj);
    if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); }

    if (obj.canceled) {
      if (doc.cm) { doc.cm.curOp.updateInput = 2; }
      return null
    }
    return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
  }

  // Apply a change to a document, and add it to the document's
  // history, and propagating it to all linked documents.
  function makeChange(doc, change, ignoreReadOnly) {
    if (doc.cm) {
      if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
      if (doc.cm.state.suppressEdits) { return }
    }

    if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
      change = filterChange(doc, change, true);
      if (!change) { return }
    }

    // Possibly split or suppress the update based on the presence
    // of read-only spans in its range.
    var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
    if (split) {
      for (var i = split.length - 1; i >= 0; --i)
        { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); }
    } else {
      makeChangeInner(doc, change);
    }
  }

  function makeChangeInner(doc, change) {
    if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
    var selAfter = computeSelAfterChange(doc, change);
    addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);

    makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
    var rebased = [];

    linkedDocs(doc, function (doc, sharedHist) {
      if (!sharedHist && indexOf(rebased, doc.history) == -1) {
        rebaseHist(doc.history, change);
        rebased.push(doc.history);
      }
      makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
    });
  }

  // Revert a change stored in a document's history.
  function makeChangeFromHistory(doc, type, allowSelectionOnly) {
    var suppress = doc.cm && doc.cm.state.suppressEdits;
    if (suppress && !allowSelectionOnly) { return }

    var hist = doc.history, event, selAfter = doc.sel;
    var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;

    // Verify that there is a useable event (so that ctrl-z won't
    // needlessly clear selection events)
    var i = 0;
    for (; i < source.length; i++) {
      event = source[i];
      if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
        { break }
    }
    if (i == source.length) { return }
    hist.lastOrigin = hist.lastSelOrigin = null;

    for (;;) {
      event = source.pop();
      if (event.ranges) {
        pushSelectionToHistory(event, dest);
        if (allowSelectionOnly && !event.equals(doc.sel)) {
          setSelection(doc, event, {clearRedo: false});
          return
        }
        selAfter = event;
      } else if (suppress) {
        source.push(event);
        return
      } else { break }
    }

    // Build up a reverse change object to add to the opposite history
    // stack (redo when undoing, and vice versa).
    var antiChanges = [];
    pushSelectionToHistory(selAfter, dest);
    dest.push({changes: antiChanges, generation: hist.generation});
    hist.generation = event.generation || ++hist.maxGeneration;

    var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");

    var loop = function ( i ) {
      var change = event.changes[i];
      change.origin = type;
      if (filter && !filterChange(doc, change, false)) {
        source.length = 0;
        return {}
      }

      antiChanges.push(historyChangeFromChange(doc, change));

      var after = i ? computeSelAfterChange(doc, change) : lst(source);
      makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
      if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); }
      var rebased = [];

      // Propagate to the linked documents
      linkedDocs(doc, function (doc, sharedHist) {
        if (!sharedHist && indexOf(rebased, doc.history) == -1) {
          rebaseHist(doc.history, change);
          rebased.push(doc.history);
        }
        makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
      });
    };

    for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
      var returned = loop( i$1 );

      if ( returned ) return returned.v;
    }
  }

  // Sub-views need their line numbers shifted when text is added
  // above or below them in the parent document.
  function shiftDoc(doc, distance) {
    if (distance == 0) { return }
    doc.first += distance;
    doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
      Pos(range.anchor.line + distance, range.anchor.ch),
      Pos(range.head.line + distance, range.head.ch)
    ); }), doc.sel.primIndex);
    if (doc.cm) {
      regChange(doc.cm, doc.first, doc.first - distance, distance);
      for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
        { regLineChange(doc.cm, l, "gutter"); }
    }
  }

  // More lower-level change function, handling only a single document
  // (not linked ones).
  function makeChangeSingleDoc(doc, change, selAfter, spans) {
    if (doc.cm && !doc.cm.curOp)
      { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }

    if (change.to.line < doc.first) {
      shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
      return
    }
    if (change.from.line > doc.lastLine()) { return }

    // Clip the change to the size of this doc
    if (change.from.line < doc.first) {
      var shift = change.text.length - 1 - (doc.first - change.from.line);
      shiftDoc(doc, shift);
      change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
                text: [lst(change.text)], origin: change.origin};
    }
    var last = doc.lastLine();
    if (change.to.line > last) {
      change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
                text: [change.text[0]], origin: change.origin};
    }

    change.removed = getBetween(doc, change.from, change.to);

    if (!selAfter) { selAfter = computeSelAfterChange(doc, change); }
    if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); }
    else { updateDoc(doc, change, spans); }
    setSelectionNoUndo(doc, selAfter, sel_dontScroll);

    if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0)))
      { doc.cantEdit = false; }
  }

  // Handle the interaction of a change to a document with the editor
  // that this document is part of.
  function makeChangeSingleDocInEditor(cm, change, spans) {
    var doc = cm.doc, display = cm.display, from = change.from, to = change.to;

    var recomputeMaxLength = false, checkWidthStart = from.line;
    if (!cm.options.lineWrapping) {
      checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
      doc.iter(checkWidthStart, to.line + 1, function (line) {
        if (line == display.maxLine) {
          recomputeMaxLength = true;
          return true
        }
      });
    }

    if (doc.sel.contains(change.from, change.to) > -1)
      { signalCursorActivity(cm); }

    updateDoc(doc, change, spans, estimateHeight(cm));

    if (!cm.options.lineWrapping) {
      doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
        var len = lineLength(line);
        if (len > display.maxLineLength) {
          display.maxLine = line;
          display.maxLineLength = len;
          display.maxLineChanged = true;
          recomputeMaxLength = false;
        }
      });
      if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; }
    }

    retreatFrontier(doc, from.line);
    startWorker(cm, 400);

    var lendiff = change.text.length - (to.line - from.line) - 1;
    // Remember that these lines changed, for updating the display
    if (change.full)
      { regChange(cm); }
    else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
      { regLineChange(cm, from.line, "text"); }
    else
      { regChange(cm, from.line, to.line + 1, lendiff); }

    var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
    if (changeHandler || changesHandler) {
      var obj = {
        from: from, to: to,
        text: change.text,
        removed: change.removed,
        origin: change.origin
      };
      if (changeHandler) { signalLater(cm, "change", cm, obj); }
      if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); }
    }
    cm.display.selForContextMenu = null;
  }

  function replaceRange(doc, code, from, to, origin) {
    var assign;

    if (!to) { to = from; }
    if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); }
    if (typeof code == "string") { code = doc.splitLines(code); }
    makeChange(doc, {from: from, to: to, text: code, origin: origin});
  }

  // Rebasing/resetting history to deal with externally-sourced changes

  function rebaseHistSelSingle(pos, from, to, diff) {
    if (to < pos.line) {
      pos.line += diff;
    } else if (from < pos.line) {
      pos.line = from;
      pos.ch = 0;
    }
  }

  // Tries to rebase an array of history events given a change in the
  // document. If the change touches the same lines as the event, the
  // event, and everything 'behind' it, is discarded. If the change is
  // before the event, the event's positions are updated. Uses a
  // copy-on-write scheme for the positions, to avoid having to
  // reallocate them all on every rebase, but also avoid problems with
  // shared position objects being unsafely updated.
  function rebaseHistArray(array, from, to, diff) {
    for (var i = 0; i < array.length; ++i) {
      var sub = array[i], ok = true;
      if (sub.ranges) {
        if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
        for (var j = 0; j < sub.ranges.length; j++) {
          rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
          rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
        }
        continue
      }
      for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
        var cur = sub.changes[j$1];
        if (to < cur.from.line) {
          cur.from = Pos(cur.from.line + diff, cur.from.ch);
          cur.to = Pos(cur.to.line + diff, cur.to.ch);
        } else if (from <= cur.to.line) {
          ok = false;
          break
        }
      }
      if (!ok) {
        array.splice(0, i + 1);
        i = 0;
      }
    }
  }

  function rebaseHist(hist, change) {
    var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
    rebaseHistArray(hist.done, from, to, diff);
    rebaseHistArray(hist.undone, from, to, diff);
  }

  // Utility for applying a change to a line by handle or number,
  // returning the number and optionally registering the line as
  // changed.
  function changeLine(doc, handle, changeType, op) {
    var no = handle, line = handle;
    if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); }
    else { no = lineNo(handle); }
    if (no == null) { return null }
    if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); }
    return line
  }

  // The document is represented as a BTree consisting of leaves, with
  // chunk of lines in them, and branches, with up to ten leaves or
  // other branch nodes below them. The top node is always a branch
  // node, and is the document object itself (meaning it has
  // additional methods and properties).
  //
  // All nodes have parent links. The tree is used both to go from
  // line numbers to line objects, and to go from objects to numbers.
  // It also indexes by height, and is used to convert between height
  // and line object, and to find the total height of the document.
  //
  // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html

  function LeafChunk(lines) {
    this.lines = lines;
    this.parent = null;
    var height = 0;
    for (var i = 0; i < lines.length; ++i) {
      lines[i].parent = this;
      height += lines[i].height;
    }
    this.height = height;
  }

  LeafChunk.prototype = {
    chunkSize: function() { return this.lines.length },

    // Remove the n lines at offset 'at'.
    removeInner: function(at, n) {
      for (var i = at, e = at + n; i < e; ++i) {
        var line = this.lines[i];
        this.height -= line.height;
        cleanUpLine(line);
        signalLater(line, "delete");
      }
      this.lines.splice(at, n);
    },

    // Helper used to collapse a small branch into a single leaf.
    collapse: function(lines) {
      lines.push.apply(lines, this.lines);
    },

    // Insert the given array of lines at offset 'at', count them as
    // having the given height.
    insertInner: function(at, lines, height) {
      this.height += height;
      this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
      for (var i = 0; i < lines.length; ++i) { lines[i].parent = this; }
    },

    // Used to iterate over a part of the tree.
    iterN: function(at, n, op) {
      for (var e = at + n; at < e; ++at)
        { if (op(this.lines[at])) { return true } }
    }
  };

  function BranchChunk(children) {
    this.children = children;
    var size = 0, height = 0;
    for (var i = 0; i < children.length; ++i) {
      var ch = children[i];
      size += ch.chunkSize(); height += ch.height;
      ch.parent = this;
    }
    this.size = size;
    this.height = height;
    this.parent = null;
  }

  BranchChunk.prototype = {
    chunkSize: function() { return this.size },

    removeInner: function(at, n) {
      this.size -= n;
      for (var i = 0; i < this.children.length; ++i) {
        var child = this.children[i], sz = child.chunkSize();
        if (at < sz) {
          var rm = Math.min(n, sz - at), oldHeight = child.height;
          child.removeInner(at, rm);
          this.height -= oldHeight - child.height;
          if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
          if ((n -= rm) == 0) { break }
          at = 0;
        } else { at -= sz; }
      }
      // If the result is smaller than 25 lines, ensure that it is a
      // single leaf node.
      if (this.size - n < 25 &&
          (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
        var lines = [];
        this.collapse(lines);
        this.children = [new LeafChunk(lines)];
        this.children[0].parent = this;
      }
    },

    collapse: function(lines) {
      for (var i = 0; i < this.children.length; ++i) { this.children[i].collapse(lines); }
    },

    insertInner: function(at, lines, height) {
      this.size += lines.length;
      this.height += height;
      for (var i = 0; i < this.children.length; ++i) {
        var child = this.children[i], sz = child.chunkSize();
        if (at <= sz) {
          child.insertInner(at, lines, height);
          if (child.lines && child.lines.length > 50) {
            // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
            // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
            var remaining = child.lines.length % 25 + 25;
            for (var pos = remaining; pos < child.lines.length;) {
              var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));
              child.height -= leaf.height;
              this.children.splice(++i, 0, leaf);
              leaf.parent = this;
            }
            child.lines = child.lines.slice(0, remaining);
            this.maybeSpill();
          }
          break
        }
        at -= sz;
      }
    },

    // When a node has grown, check whether it should be split.
    maybeSpill: function() {
      if (this.children.length <= 10) { return }
      var me = this;
      do {
        var spilled = me.children.splice(me.children.length - 5, 5);
        var sibling = new BranchChunk(spilled);
        if (!me.parent) { // Become the parent node
          var copy = new BranchChunk(me.children);
          copy.parent = me;
          me.children = [copy, sibling];
          me = copy;
       } else {
          me.size -= sibling.size;
          me.height -= sibling.height;
          var myIndex = indexOf(me.parent.children, me);
          me.parent.children.splice(myIndex + 1, 0, sibling);
        }
        sibling.parent = me.parent;
      } while (me.children.length > 10)
      me.parent.maybeSpill();
    },

    iterN: function(at, n, op) {
      for (var i = 0; i < this.children.length; ++i) {
        var child = this.children[i], sz = child.chunkSize();
        if (at < sz) {
          var used = Math.min(n, sz - at);
          if (child.iterN(at, used, op)) { return true }
          if ((n -= used) == 0) { break }
          at = 0;
        } else { at -= sz; }
      }
    }
  };

  // Line widgets are block elements displayed above or below a line.

  var LineWidget = function(doc, node, options) {
    if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
      { this[opt] = options[opt]; } } }
    this.doc = doc;
    this.node = node;
  };

  LineWidget.prototype.clear = function () {
    var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
    if (no == null || !ws) { return }
    for (var i = 0; i < ws.length; ++i) { if (ws[i] == this) { ws.splice(i--, 1); } }
    if (!ws.length) { line.widgets = null; }
    var height = widgetHeight(this);
    updateLineHeight(line, Math.max(0, line.height - height));
    if (cm) {
      runInOp(cm, function () {
        adjustScrollWhenAboveVisible(cm, line, -height);
        regLineChange(cm, no, "widget");
      });
      signalLater(cm, "lineWidgetCleared", cm, this, no);
    }
  };

  LineWidget.prototype.changed = function () {
      var this$1 = this;

    var oldH = this.height, cm = this.doc.cm, line = this.line;
    this.height = null;
    var diff = widgetHeight(this) - oldH;
    if (!diff) { return }
    if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); }
    if (cm) {
      runInOp(cm, function () {
        cm.curOp.forceUpdate = true;
        adjustScrollWhenAboveVisible(cm, line, diff);
        signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line));
      });
    }
  };
  eventMixin(LineWidget);

  function adjustScrollWhenAboveVisible(cm, line, diff) {
    if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
      { addToScrollTop(cm, diff); }
  }

  function addLineWidget(doc, handle, node, options) {
    var widget = new LineWidget(doc, node, options);
    var cm = doc.cm;
    if (cm && widget.noHScroll) { cm.display.alignWidgets = true; }
    changeLine(doc, handle, "widget", function (line) {
      var widgets = line.widgets || (line.widgets = []);
      if (widget.insertAt == null) { widgets.push(widget); }
      else { widgets.splice(Math.min(widgets.length, Math.max(0, widget.insertAt)), 0, widget); }
      widget.line = line;
      if (cm && !lineIsHidden(doc, line)) {
        var aboveVisible = heightAtLine(line) < doc.scrollTop;
        updateLineHeight(line, line.height + widgetHeight(widget));
        if (aboveVisible) { addToScrollTop(cm, widget.height); }
        cm.curOp.forceUpdate = true;
      }
      return true
    });
    if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); }
    return widget
  }

  // TEXTMARKERS

  // Created with markText and setBookmark methods. A TextMarker is a
  // handle that can be used to clear or find a marked position in the
  // document. Line objects hold arrays (markedSpans) containing
  // {from, to, marker} object pointing to such marker objects, and
  // indicating that such a marker is present on that line. Multiple
  // lines may point to the same marker when it spans across lines.
  // The spans will have null for their from/to properties when the
  // marker continues beyond the start/end of the line. Markers have
  // links back to the lines they currently touch.

  // Collapsed markers have unique ids, in order to be able to order
  // them, which is needed for uniquely determining an outer marker
  // when they overlap (they may nest, but not partially overlap).
  var nextMarkerId = 0;

  var TextMarker = function(doc, type) {
    this.lines = [];
    this.type = type;
    this.doc = doc;
    this.id = ++nextMarkerId;
  };

  // Clear the marker.
  TextMarker.prototype.clear = function () {
    if (this.explicitlyCleared) { return }
    var cm = this.doc.cm, withOp = cm && !cm.curOp;
    if (withOp) { startOperation(cm); }
    if (hasHandler(this, "clear")) {
      var found = this.find();
      if (found) { signalLater(this, "clear", found.from, found.to); }
    }
    var min = null, max = null;
    for (var i = 0; i < this.lines.length; ++i) {
      var line = this.lines[i];
      var span = getMarkedSpanFor(line.markedSpans, this);
      if (cm && !this.collapsed) { regLineChange(cm, lineNo(line), "text"); }
      else if (cm) {
        if (span.to != null) { max = lineNo(line); }
        if (span.from != null) { min = lineNo(line); }
      }
      line.markedSpans = removeMarkedSpan(line.markedSpans, span);
      if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm)
        { updateLineHeight(line, textHeight(cm.display)); }
    }
    if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
      var visual = visualLine(this.lines[i$1]), len = lineLength(visual);
      if (len > cm.display.maxLineLength) {
        cm.display.maxLine = visual;
        cm.display.maxLineLength = len;
        cm.display.maxLineChanged = true;
      }
    } }

    if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); }
    this.lines.length = 0;
    this.explicitlyCleared = true;
    if (this.atomic && this.doc.cantEdit) {
      this.doc.cantEdit = false;
      if (cm) { reCheckSelection(cm.doc); }
    }
    if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); }
    if (withOp) { endOperation(cm); }
    if (this.parent) { this.parent.clear(); }
  };

  // Find the position of the marker in the document. Returns a {from,
  // to} object by default. Side can be passed to get a specific side
  // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
  // Pos objects returned contain a line object, rather than a line
  // number (used to prevent looking up the same line twice).
  TextMarker.prototype.find = function (side, lineObj) {
    if (side == null && this.type == "bookmark") { side = 1; }
    var from, to;
    for (var i = 0; i < this.lines.length; ++i) {
      var line = this.lines[i];
      var span = getMarkedSpanFor(line.markedSpans, this);
      if (span.from != null) {
        from = Pos(lineObj ? line : lineNo(line), span.from);
        if (side == -1) { return from }
      }
      if (span.to != null) {
        to = Pos(lineObj ? line : lineNo(line), span.to);
        if (side == 1) { return to }
      }
    }
    return from && {from: from, to: to}
  };

  // Signals that the marker's widget changed, and surrounding layout
  // should be recomputed.
  TextMarker.prototype.changed = function () {
      var this$1 = this;

    var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
    if (!pos || !cm) { return }
    runInOp(cm, function () {
      var line = pos.line, lineN = lineNo(pos.line);
      var view = findViewForLine(cm, lineN);
      if (view) {
        clearLineMeasurementCacheFor(view);
        cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
      }
      cm.curOp.updateMaxLine = true;
      if (!lineIsHidden(widget.doc, line) && widget.height != null) {
        var oldHeight = widget.height;
        widget.height = null;
        var dHeight = widgetHeight(widget) - oldHeight;
        if (dHeight)
          { updateLineHeight(line, line.height + dHeight); }
      }
      signalLater(cm, "markerChanged", cm, this$1);
    });
  };

  TextMarker.prototype.attachLine = function (line) {
    if (!this.lines.length && this.doc.cm) {
      var op = this.doc.cm.curOp;
      if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
        { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); }
    }
    this.lines.push(line);
  };

  TextMarker.prototype.detachLine = function (line) {
    this.lines.splice(indexOf(this.lines, line), 1);
    if (!this.lines.length && this.doc.cm) {
      var op = this.doc.cm.curOp
      ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
    }
  };
  eventMixin(TextMarker);

  // Create a marker, wire it up to the right lines, and
  function markText(doc, from, to, options, type) {
    // Shared markers (across linked documents) are handled separately
    // (markTextShared will call out to this again, once per
    // document).
    if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
    // Ensure we are in an operation.
    if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }

    var marker = new TextMarker(doc, type), diff = cmp(from, to);
    if (options) { copyObj(options, marker, false); }
    // Don't connect empty markers unless clearWhenEmpty is false
    if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
      { return marker }
    if (marker.replacedWith) {
      // Showing up as a widget implies collapsed (widget replaces text)
      marker.collapsed = true;
      marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget");
      if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); }
      if (options.insertLeft) { marker.widgetNode.insertLeft = true; }
    }
    if (marker.collapsed) {
      if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
          from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
        { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
      seeCollapsedSpans();
    }

    if (marker.addToHistory)
      { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); }

    var curLine = from.line, cm = doc.cm, updateMaxLine;
    doc.iter(curLine, to.line + 1, function (line) {
      if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
        { updateMaxLine = true; }
      if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); }
      addMarkedSpan(line, new MarkedSpan(marker,
                                         curLine == from.line ? from.ch : null,
                                         curLine == to.line ? to.ch : null));
      ++curLine;
    });
    // lineIsHidden depends on the presence of the spans, so needs a second pass
    if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
      if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); }
    }); }

    if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); }

    if (marker.readOnly) {
      seeReadOnlySpans();
      if (doc.history.done.length || doc.history.undone.length)
        { doc.clearHistory(); }
    }
    if (marker.collapsed) {
      marker.id = ++nextMarkerId;
      marker.atomic = true;
    }
    if (cm) {
      // Sync editor state
      if (updateMaxLine) { cm.curOp.updateMaxLine = true; }
      if (marker.collapsed)
        { regChange(cm, from.line, to.line + 1); }
      else if (marker.className || marker.startStyle || marker.endStyle || marker.css ||
               marker.attributes || marker.title)
        { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } }
      if (marker.atomic) { reCheckSelection(cm.doc); }
      signalLater(cm, "markerAdded", cm, marker);
    }
    return marker
  }

  // SHARED TEXTMARKERS

  // A shared marker spans multiple linked documents. It is
  // implemented as a meta-marker-object controlling multiple normal
  // markers.
  var SharedTextMarker = function(markers, primary) {
    this.markers = markers;
    this.primary = primary;
    for (var i = 0; i < markers.length; ++i)
      { markers[i].parent = this; }
  };

  SharedTextMarker.prototype.clear = function () {
    if (this.explicitlyCleared) { return }
    this.explicitlyCleared = true;
    for (var i = 0; i < this.markers.length; ++i)
      { this.markers[i].clear(); }
    signalLater(this, "clear");
  };

  SharedTextMarker.prototype.find = function (side, lineObj) {
    return this.primary.find(side, lineObj)
  };
  eventMixin(SharedTextMarker);

  function markTextShared(doc, from, to, options, type) {
    options = copyObj(options);
    options.shared = false;
    var markers = [markText(doc, from, to, options, type)], primary = markers[0];
    var widget = options.widgetNode;
    linkedDocs(doc, function (doc) {
      if (widget) { options.widgetNode = widget.cloneNode(true); }
      markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
      for (var i = 0; i < doc.linked.length; ++i)
        { if (doc.linked[i].isParent) { return } }
      primary = lst(markers);
    });
    return new SharedTextMarker(markers, primary)
  }

  function findSharedMarkers(doc) {
    return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
  }

  function copySharedMarkers(doc, markers) {
    for (var i = 0; i < markers.length; i++) {
      var marker = markers[i], pos = marker.find();
      var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
      if (cmp(mFrom, mTo)) {
        var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
        marker.markers.push(subMark);
        subMark.parent = marker;
      }
    }
  }

  function detachSharedMarkers(markers) {
    var loop = function ( i ) {
      var marker = markers[i], linked = [marker.primary.doc];
      linkedDocs(marker.primary.doc, function (d) { return linked.push(d); });
      for (var j = 0; j < marker.markers.length; j++) {
        var subMarker = marker.markers[j];
        if (indexOf(linked, subMarker.doc) == -1) {
          subMarker.parent = null;
          marker.markers.splice(j--, 1);
        }
      }
    };

    for (var i = 0; i < markers.length; i++) loop( i );
  }

  var nextDocId = 0;
  var Doc = function(text, mode, firstLine, lineSep, direction) {
    if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }
    if (firstLine == null) { firstLine = 0; }

    BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
    this.first = firstLine;
    this.scrollTop = this.scrollLeft = 0;
    this.cantEdit = false;
    this.cleanGeneration = 1;
    this.modeFrontier = this.highlightFrontier = firstLine;
    var start = Pos(firstLine, 0);
    this.sel = simpleSelection(start);
    this.history = new History(null);
    this.id = ++nextDocId;
    this.modeOption = mode;
    this.lineSep = lineSep;
    this.direction = (direction == "rtl") ? "rtl" : "ltr";
    this.extend = false;

    if (typeof text == "string") { text = this.splitLines(text); }
    updateDoc(this, {from: start, to: start, text: text});
    setSelection(this, simpleSelection(start), sel_dontScroll);
  };

  Doc.prototype = createObj(BranchChunk.prototype, {
    constructor: Doc,
    // Iterate over the document. Supports two forms -- with only one
    // argument, it calls that for each line in the document. With
    // three, it iterates over the range given by the first two (with
    // the second being non-inclusive).
    iter: function(from, to, op) {
      if (op) { this.iterN(from - this.first, to - from, op); }
      else { this.iterN(this.first, this.first + this.size, from); }
    },

    // Non-public interface for adding and removing lines.
    insert: function(at, lines) {
      var height = 0;
      for (var i = 0; i < lines.length; ++i) { height += lines[i].height; }
      this.insertInner(at - this.first, lines, height);
    },
    remove: function(at, n) { this.removeInner(at - this.first, n); },

    // From here, the methods are part of the public interface. Most
    // are also available from CodeMirror (editor) instances.

    getValue: function(lineSep) {
      var lines = getLines(this, this.first, this.first + this.size);
      if (lineSep === false) { return lines }
      return lines.join(lineSep || this.lineSeparator())
    },
    setValue: docMethodOp(function(code) {
      var top = Pos(this.first, 0), last = this.first + this.size - 1;
      makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
                        text: this.splitLines(code), origin: "setValue", full: true}, true);
      if (this.cm) { scrollToCoords(this.cm, 0, 0); }
      setSelection(this, simpleSelection(top), sel_dontScroll);
    }),
    replaceRange: function(code, from, to, origin) {
      from = clipPos(this, from);
      to = to ? clipPos(this, to) : from;
      replaceRange(this, code, from, to, origin);
    },
    getRange: function(from, to, lineSep) {
      var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
      if (lineSep === false) { return lines }
      return lines.join(lineSep || this.lineSeparator())
    },

    getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},

    getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
    getLineNumber: function(line) {return lineNo(line)},

    getLineHandleVisualStart: function(line) {
      if (typeof line == "number") { line = getLine(this, line); }
      return visualLine(line)
    },

    lineCount: function() {return this.size},
    firstLine: function() {return this.first},
    lastLine: function() {return this.first + this.size - 1},

    clipPos: function(pos) {return clipPos(this, pos)},

    getCursor: function(start) {
      var range = this.sel.primary(), pos;
      if (start == null || start == "head") { pos = range.head; }
      else if (start == "anchor") { pos = range.anchor; }
      else if (start == "end" || start == "to" || start === false) { pos = range.to(); }
      else { pos = range.from(); }
      return pos
    },
    listSelections: function() { return this.sel.ranges },
    somethingSelected: function() {return this.sel.somethingSelected()},

    setCursor: docMethodOp(function(line, ch, options) {
      setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
    }),
    setSelection: docMethodOp(function(anchor, head, options) {
      setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
    }),
    extendSelection: docMethodOp(function(head, other, options) {
      extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
    }),
    extendSelections: docMethodOp(function(heads, options) {
      extendSelections(this, clipPosArray(this, heads), options);
    }),
    extendSelectionsBy: docMethodOp(function(f, options) {
      var heads = map(this.sel.ranges, f);
      extendSelections(this, clipPosArray(this, heads), options);
    }),
    setSelections: docMethodOp(function(ranges, primary, options) {
      if (!ranges.length) { return }
      var out = [];
      for (var i = 0; i < ranges.length; i++)
        { out[i] = new Range(clipPos(this, ranges[i].anchor),
                           clipPos(this, ranges[i].head)); }
      if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); }
      setSelection(this, normalizeSelection(this.cm, out, primary), options);
    }),
    addSelection: docMethodOp(function(anchor, head, options) {
      var ranges = this.sel.ranges.slice(0);
      ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
      setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options);
    }),

    getSelection: function(lineSep) {
      var ranges = this.sel.ranges, lines;
      for (var i = 0; i < ranges.length; i++) {
        var sel = getBetween(this, ranges[i].from(), ranges[i].to());
        lines = lines ? lines.concat(sel) : sel;
      }
      if (lineSep === false) { return lines }
      else { return lines.join(lineSep || this.lineSeparator()) }
    },
    getSelections: function(lineSep) {
      var parts = [], ranges = this.sel.ranges;
      for (var i = 0; i < ranges.length; i++) {
        var sel = getBetween(this, ranges[i].from(), ranges[i].to());
        if (lineSep !== false) { sel = sel.join(lineSep || this.lineSeparator()); }
        parts[i] = sel;
      }
      return parts
    },
    replaceSelection: function(code, collapse, origin) {
      var dup = [];
      for (var i = 0; i < this.sel.ranges.length; i++)
        { dup[i] = code; }
      this.replaceSelections(dup, collapse, origin || "+input");
    },
    replaceSelections: docMethodOp(function(code, collapse, origin) {
      var changes = [], sel = this.sel;
      for (var i = 0; i < sel.ranges.length; i++) {
        var range = sel.ranges[i];
        changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin};
      }
      var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
      for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
        { makeChange(this, changes[i$1]); }
      if (newSel) { setSelectionReplaceHistory(this, newSel); }
      else if (this.cm) { ensureCursorVisible(this.cm); }
    }),
    undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
    redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
    undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
    redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),

    setExtending: function(val) {this.extend = val;},
    getExtending: function() {return this.extend},

    historySize: function() {
      var hist = this.history, done = 0, undone = 0;
      for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } }
      for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } }
      return {undo: done, redo: undone}
    },
    clearHistory: function() {
      var this$1 = this;

      this.history = new History(this.history.maxGeneration);
      linkedDocs(this, function (doc) { return doc.history = this$1.history; }, true);
    },

    markClean: function() {
      this.cleanGeneration = this.changeGeneration(true);
    },
    changeGeneration: function(forceSplit) {
      if (forceSplit)
        { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; }
      return this.history.generation
    },
    isClean: function (gen) {
      return this.history.generation == (gen || this.cleanGeneration)
    },

    getHistory: function() {
      return {done: copyHistoryArray(this.history.done),
              undone: copyHistoryArray(this.history.undone)}
    },
    setHistory: function(histData) {
      var hist = this.history = new History(this.history.maxGeneration);
      hist.done = copyHistoryArray(histData.done.slice(0), null, true);
      hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
    },

    setGutterMarker: docMethodOp(function(line, gutterID, value) {
      return changeLine(this, line, "gutter", function (line) {
        var markers = line.gutterMarkers || (line.gutterMarkers = {});
        markers[gutterID] = value;
        if (!value && isEmpty(markers)) { line.gutterMarkers = null; }
        return true
      })
    }),

    clearGutter: docMethodOp(function(gutterID) {
      var this$1 = this;

      this.iter(function (line) {
        if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
          changeLine(this$1, line, "gutter", function () {
            line.gutterMarkers[gutterID] = null;
            if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; }
            return true
          });
        }
      });
    }),

    lineInfo: function(line) {
      var n;
      if (typeof line == "number") {
        if (!isLine(this, line)) { return null }
        n = line;
        line = getLine(this, line);
        if (!line) { return null }
      } else {
        n = lineNo(line);
        if (n == null) { return null }
      }
      return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
              textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
              widgets: line.widgets}
    },

    addLineClass: docMethodOp(function(handle, where, cls) {
      return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
        var prop = where == "text" ? "textClass"
                 : where == "background" ? "bgClass"
                 : where == "gutter" ? "gutterClass" : "wrapClass";
        if (!line[prop]) { line[prop] = cls; }
        else if (classTest(cls).test(line[prop])) { return false }
        else { line[prop] += " " + cls; }
        return true
      })
    }),
    removeLineClass: docMethodOp(function(handle, where, cls) {
      return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
        var prop = where == "text" ? "textClass"
                 : where == "background" ? "bgClass"
                 : where == "gutter" ? "gutterClass" : "wrapClass";
        var cur = line[prop];
        if (!cur) { return false }
        else if (cls == null) { line[prop] = null; }
        else {
          var found = cur.match(classTest(cls));
          if (!found) { return false }
          var end = found.index + found[0].length;
          line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
        }
        return true
      })
    }),

    addLineWidget: docMethodOp(function(handle, node, options) {
      return addLineWidget(this, handle, node, options)
    }),
    removeLineWidget: function(widget) { widget.clear(); },

    markText: function(from, to, options) {
      return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
    },
    setBookmark: function(pos, options) {
      var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
                      insertLeft: options && options.insertLeft,
                      clearWhenEmpty: false, shared: options && options.shared,
                      handleMouseEvents: options && options.handleMouseEvents};
      pos = clipPos(this, pos);
      return markText(this, pos, pos, realOpts, "bookmark")
    },
    findMarksAt: function(pos) {
      pos = clipPos(this, pos);
      var markers = [], spans = getLine(this, pos.line).markedSpans;
      if (spans) { for (var i = 0; i < spans.length; ++i) {
        var span = spans[i];
        if ((span.from == null || span.from <= pos.ch) &&
            (span.to == null || span.to >= pos.ch))
          { markers.push(span.marker.parent || span.marker); }
      } }
      return markers
    },
    findMarks: function(from, to, filter) {
      from = clipPos(this, from); to = clipPos(this, to);
      var found = [], lineNo = from.line;
      this.iter(from.line, to.line + 1, function (line) {
        var spans = line.markedSpans;
        if (spans) { for (var i = 0; i < spans.length; i++) {
          var span = spans[i];
          if (!(span.to != null && lineNo == from.line && from.ch >= span.to ||
                span.from == null && lineNo != from.line ||
                span.from != null && lineNo == to.line && span.from >= to.ch) &&
              (!filter || filter(span.marker)))
            { found.push(span.marker.parent || span.marker); }
        } }
        ++lineNo;
      });
      return found
    },
    getAllMarks: function() {
      var markers = [];
      this.iter(function (line) {
        var sps = line.markedSpans;
        if (sps) { for (var i = 0; i < sps.length; ++i)
          { if (sps[i].from != null) { markers.push(sps[i].marker); } } }
      });
      return markers
    },

    posFromIndex: function(off) {
      var ch, lineNo = this.first, sepSize = this.lineSeparator().length;
      this.iter(function (line) {
        var sz = line.text.length + sepSize;
        if (sz > off) { ch = off; return true }
        off -= sz;
        ++lineNo;
      });
      return clipPos(this, Pos(lineNo, ch))
    },
    indexFromPos: function (coords) {
      coords = clipPos(this, coords);
      var index = coords.ch;
      if (coords.line < this.first || coords.ch < 0) { return 0 }
      var sepSize = this.lineSeparator().length;
      this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
        index += line.text.length + sepSize;
      });
      return index
    },

    copy: function(copyHistory) {
      var doc = new Doc(getLines(this, this.first, this.first + this.size),
                        this.modeOption, this.first, this.lineSep, this.direction);
      doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
      doc.sel = this.sel;
      doc.extend = false;
      if (copyHistory) {
        doc.history.undoDepth = this.history.undoDepth;
        doc.setHistory(this.getHistory());
      }
      return doc
    },

    linkedDoc: function(options) {
      if (!options) { options = {}; }
      var from = this.first, to = this.first + this.size;
      if (options.from != null && options.from > from) { from = options.from; }
      if (options.to != null && options.to < to) { to = options.to; }
      var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction);
      if (options.sharedHist) { copy.history = this.history
      ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
      copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
      copySharedMarkers(copy, findSharedMarkers(this));
      return copy
    },
    unlinkDoc: function(other) {
      if (other instanceof CodeMirror) { other = other.doc; }
      if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
        var link = this.linked[i];
        if (link.doc != other) { continue }
        this.linked.splice(i, 1);
        other.unlinkDoc(this);
        detachSharedMarkers(findSharedMarkers(this));
        break
      } }
      // If the histories were shared, split them again
      if (other.history == this.history) {
        var splitIds = [other.id];
        linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true);
        other.history = new History(null);
        other.history.done = copyHistoryArray(this.history.done, splitIds);
        other.history.undone = copyHistoryArray(this.history.undone, splitIds);
      }
    },
    iterLinkedDocs: function(f) {linkedDocs(this, f);},

    getMode: function() {return this.mode},
    getEditor: function() {return this.cm},

    splitLines: function(str) {
      if (this.lineSep) { return str.split(this.lineSep) }
      return splitLinesAuto(str)
    },
    lineSeparator: function() { return this.lineSep || "\n" },

    setDirection: docMethodOp(function (dir) {
      if (dir != "rtl") { dir = "ltr"; }
      if (dir == this.direction) { return }
      this.direction = dir;
      this.iter(function (line) { return line.order = null; });
      if (this.cm) { directionChanged(this.cm); }
    })
  });

  // Public alias.
  Doc.prototype.eachLine = Doc.prototype.iter;

  // Kludge to work around strange IE behavior where it'll sometimes
  // re-fire a series of drag-related events right after the drop (#1551)
  var lastDrop = 0;

  function onDrop(e) {
    var cm = this;
    clearDragCursor(cm);
    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
      { return }
    e_preventDefault(e);
    if (ie) { lastDrop = +new Date; }
    var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
    if (!pos || cm.isReadOnly()) { return }
    // Might be a file drop, in which case we simply extract the text
    // and insert it.
    if (files && files.length && window.FileReader && window.File) {
      var n = files.length, text = Array(n), read = 0;
      var markAsReadAndPasteIfAllFilesAreRead = function () {
        if (++read == n) {
          operation(cm, function () {
            pos = clipPos(cm.doc, pos);
            var change = {from: pos, to: pos,
                          text: cm.doc.splitLines(
                              text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())),
                          origin: "paste"};
            makeChange(cm.doc, change);
            setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change))));
          })();
        }
      };
      var readTextFromFile = function (file, i) {
        if (cm.options.allowDropFileTypes &&
            indexOf(cm.options.allowDropFileTypes, file.type) == -1) {
          markAsReadAndPasteIfAllFilesAreRead();
          return
        }
        var reader = new FileReader;
        reader.onerror = function () { return markAsReadAndPasteIfAllFilesAreRead(); };
        reader.onload = function () {
          var content = reader.result;
          if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) {
            markAsReadAndPasteIfAllFilesAreRead();
            return
          }
          text[i] = content;
          markAsReadAndPasteIfAllFilesAreRead();
        };
        reader.readAsText(file);
      };
      for (var i = 0; i < files.length; i++) { readTextFromFile(files[i], i); }
    } else { // Normal drop
      // Don't do a replace if the drop happened inside of the selected text.
      if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
        cm.state.draggingText(e);
        // Ensure the editor is re-focused
        setTimeout(function () { return cm.display.input.focus(); }, 20);
        return
      }
      try {
        var text$1 = e.dataTransfer.getData("Text");
        if (text$1) {
          var selected;
          if (cm.state.draggingText && !cm.state.draggingText.copy)
            { selected = cm.listSelections(); }
          setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
          if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
            { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } }
          cm.replaceSelection(text$1, "around", "paste");
          cm.display.input.focus();
        }
      }
      catch(e$1){}
    }
  }

  function onDragStart(cm, e) {
    if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }

    e.dataTransfer.setData("Text", cm.getSelection());
    e.dataTransfer.effectAllowed = "copyMove";

    // Use dummy image instead of default browsers image.
    // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
    if (e.dataTransfer.setDragImage && !safari) {
      var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
      img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
      if (presto) {
        img.width = img.height = 1;
        cm.display.wrapper.appendChild(img);
        // Force a relayout, or Opera won't use our image for some obscure reason
        img._top = img.offsetTop;
      }
      e.dataTransfer.setDragImage(img, 0, 0);
      if (presto) { img.parentNode.removeChild(img); }
    }
  }

  function onDragOver(cm, e) {
    var pos = posFromMouse(cm, e);
    if (!pos) { return }
    var frag = document.createDocumentFragment();
    drawSelectionCursor(cm, pos, frag);
    if (!cm.display.dragCursor) {
      cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors");
      cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);
    }
    removeChildrenAndAdd(cm.display.dragCursor, frag);
  }

  function clearDragCursor(cm) {
    if (cm.display.dragCursor) {
      cm.display.lineSpace.removeChild(cm.display.dragCursor);
      cm.display.dragCursor = null;
    }
  }

  // These must be handled carefully, because naively registering a
  // handler for each editor will cause the editors to never be
  // garbage collected.

  function forEachCodeMirror(f) {
    if (!document.getElementsByClassName) { return }
    var byClass = document.getElementsByClassName("CodeMirror"), editors = [];
    for (var i = 0; i < byClass.length; i++) {
      var cm = byClass[i].CodeMirror;
      if (cm) { editors.push(cm); }
    }
    if (editors.length) { editors[0].operation(function () {
      for (var i = 0; i < editors.length; i++) { f(editors[i]); }
    }); }
  }

  var globalsRegistered = false;
  function ensureGlobalHandlers() {
    if (globalsRegistered) { return }
    registerGlobalHandlers();
    globalsRegistered = true;
  }
  function registerGlobalHandlers() {
    // When the window resizes, we need to refresh active editors.
    var resizeTimer;
    on(window, "resize", function () {
      if (resizeTimer == null) { resizeTimer = setTimeout(function () {
        resizeTimer = null;
        forEachCodeMirror(onResize);
      }, 100); }
    });
    // When the window loses focus, we want to show the editor as blurred
    on(window, "blur", function () { return forEachCodeMirror(onBlur); });
  }
  // Called when the window resizes
  function onResize(cm) {
    var d = cm.display;
    // Might be a text scaling operation, clear size caches.
    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
    d.scrollbarsClipped = false;
    cm.setSize();
  }

  var keyNames = {
    3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
    19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
    36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
    46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
    106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock",
    173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
    221: "]", 222: "'", 224: "Mod", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
    63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
  };

  // Number keys
  for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); }
  // Alphabetic keys
  for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); }
  // Function keys
  for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; }

  var keyMap = {};

  keyMap.basic = {
    "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
    "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
    "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
    "Tab": "defaultTab", "Shift-Tab": "indentAuto",
    "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
    "Esc": "singleSelection"
  };
  // Note that the save and find-related commands aren't defined by
  // default. User code or addons can define them. Unknown commands
  // are simply ignored.
  keyMap.pcDefault = {
    "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
    "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
    "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
    "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
    "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
    "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
    "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
    "fallthrough": "basic"
  };
  // Very basic readline/emacs-style bindings, which are standard on Mac.
  keyMap.emacsy = {
    "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
    "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
    "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
    "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
    "Ctrl-O": "openLine"
  };
  keyMap.macDefault = {
    "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
    "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
    "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
    "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
    "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
    "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
    "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
    "fallthrough": ["basic", "emacsy"]
  };
  keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;

  // KEYMAP DISPATCH

  function normalizeKeyName(name) {
    var parts = name.split(/-(?!$)/);
    name = parts[parts.length - 1];
    var alt, ctrl, shift, cmd;
    for (var i = 0; i < parts.length - 1; i++) {
      var mod = parts[i];
      if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; }
      else if (/^a(lt)?$/i.test(mod)) { alt = true; }
      else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; }
      else if (/^s(hift)?$/i.test(mod)) { shift = true; }
      else { throw new Error("Unrecognized modifier name: " + mod) }
    }
    if (alt) { name = "Alt-" + name; }
    if (ctrl) { name = "Ctrl-" + name; }
    if (cmd) { name = "Cmd-" + name; }
    if (shift) { name = "Shift-" + name; }
    return name
  }

  // This is a kludge to keep keymaps mostly working as raw objects
  // (backwards compatibility) while at the same time support features
  // like normalization and multi-stroke key bindings. It compiles a
  // new normalized keymap, and then updates the old object to reflect
  // this.
  function normalizeKeyMap(keymap) {
    var copy = {};
    for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
      var value = keymap[keyname];
      if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
      if (value == "...") { delete keymap[keyname]; continue }

      var keys = map(keyname.split(" "), normalizeKeyName);
      for (var i = 0; i < keys.length; i++) {
        var val = (void 0), name = (void 0);
        if (i == keys.length - 1) {
          name = keys.join(" ");
          val = value;
        } else {
          name = keys.slice(0, i + 1).join(" ");
          val = "...";
        }
        var prev = copy[name];
        if (!prev) { copy[name] = val; }
        else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
      }
      delete keymap[keyname];
    } }
    for (var prop in copy) { keymap[prop] = copy[prop]; }
    return keymap
  }

  function lookupKey(key, map, handle, context) {
    map = getKeyMap(map);
    var found = map.call ? map.call(key, context) : map[key];
    if (found === false) { return "nothing" }
    if (found === "...") { return "multi" }
    if (found != null && handle(found)) { return "handled" }

    if (map.fallthrough) {
      if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
        { return lookupKey(key, map.fallthrough, handle, context) }
      for (var i = 0; i < map.fallthrough.length; i++) {
        var result = lookupKey(key, map.fallthrough[i], handle, context);
        if (result) { return result }
      }
    }
  }

  // Modifier key presses don't count as 'real' key presses for the
  // purpose of keymap fallthrough.
  function isModifierKey(value) {
    var name = typeof value == "string" ? value : keyNames[value.keyCode];
    return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
  }

  function addModifierNames(name, event, noShift) {
    var base = name;
    if (event.altKey && base != "Alt") { name = "Alt-" + name; }
    if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; }
    if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Mod") { name = "Cmd-" + name; }
    if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; }
    return name
  }

  // Look up the name of a key as indicated by an event object.
  function keyName(event, noShift) {
    if (presto && event.keyCode == 34 && event["char"]) { return false }
    var name = keyNames[event.keyCode];
    if (name == null || event.altGraphKey) { return false }
    // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
    // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
    if (event.keyCode == 3 && event.code) { name = event.code; }
    return addModifierNames(name, event, noShift)
  }

  function getKeyMap(val) {
    return typeof val == "string" ? keyMap[val] : val
  }

  // Helper for deleting text near the selection(s), used to implement
  // backspace, delete, and similar functionality.
  function deleteNearSelection(cm, compute) {
    var ranges = cm.doc.sel.ranges, kill = [];
    // Build up a set of ranges to kill first, merging overlapping
    // ranges.
    for (var i = 0; i < ranges.length; i++) {
      var toKill = compute(ranges[i]);
      while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
        var replaced = kill.pop();
        if (cmp(replaced.from, toKill.from) < 0) {
          toKill.from = replaced.from;
          break
        }
      }
      kill.push(toKill);
    }
    // Next, remove those actual ranges.
    runInOp(cm, function () {
      for (var i = kill.length - 1; i >= 0; i--)
        { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); }
      ensureCursorVisible(cm);
    });
  }

  function moveCharLogically(line, ch, dir) {
    var target = skipExtendingChars(line.text, ch + dir, dir);
    return target < 0 || target > line.text.length ? null : target
  }

  function moveLogically(line, start, dir) {
    var ch = moveCharLogically(line, start.ch, dir);
    return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
  }

  function endOfLine(visually, cm, lineObj, lineNo, dir) {
    if (visually) {
      if (cm.doc.direction == "rtl") { dir = -dir; }
      var order = getOrder(lineObj, cm.doc.direction);
      if (order) {
        var part = dir < 0 ? lst(order) : order[0];
        var moveInStorageOrder = (dir < 0) == (part.level == 1);
        var sticky = moveInStorageOrder ? "after" : "before";
        var ch;
        // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
        // it could be that the last bidi part is not on the last visual line,
        // since visual lines contain content order-consecutive chunks.
        // Thus, in rtl, we are looking for the first (content-order) character
        // in the rtl chunk that is on the last line (that is, the same line
        // as the last (content-order) character).
        if (part.level > 0 || cm.doc.direction == "rtl") {
          var prep = prepareMeasureForLine(cm, lineObj);
          ch = dir < 0 ? lineObj.text.length - 1 : 0;
          var targetTop = measureCharPrepared(cm, prep, ch).top;
          ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch);
          if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); }
        } else { ch = dir < 0 ? part.to : part.from; }
        return new Pos(lineNo, ch, sticky)
      }
    }
    return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
  }

  function moveVisually(cm, line, start, dir) {
    var bidi = getOrder(line, cm.doc.direction);
    if (!bidi) { return moveLogically(line, start, dir) }
    if (start.ch >= line.text.length) {
      start.ch = line.text.length;
      start.sticky = "before";
    } else if (start.ch <= 0) {
      start.ch = 0;
      start.sticky = "after";
    }
    var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos];
    if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
      // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
      // nothing interesting happens.
      return moveLogically(line, start, dir)
    }

    var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); };
    var prep;
    var getWrappedLineExtent = function (ch) {
      if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
      prep = prep || prepareMeasureForLine(cm, line);
      return wrappedLineExtentChar(cm, line, prep, ch)
    };
    var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch);

    if (cm.doc.direction == "rtl" || part.level == 1) {
      var moveInStorageOrder = (part.level == 1) == (dir < 0);
      var ch = mv(start, moveInStorageOrder ? 1 : -1);
      if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
        // Case 2: We move within an rtl part or in an rtl editor on the same visual line
        var sticky = moveInStorageOrder ? "before" : "after";
        return new Pos(start.line, ch, sticky)
      }
    }

    // Case 3: Could not move within this bidi part in this visual line, so leave
    // the current bidi part

    var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
      var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
        ? new Pos(start.line, mv(ch, 1), "before")
        : new Pos(start.line, ch, "after"); };

      for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
        var part = bidi[partPos];
        var moveInStorageOrder = (dir > 0) == (part.level != 1);
        var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1);
        if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
        ch = moveInStorageOrder ? part.from : mv(part.to, -1);
        if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
      }
    };

    // Case 3a: Look for other bidi parts on the same visual line
    var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent);
    if (res) { return res }

    // Case 3b: Look for other bidi parts on the next visual line
    var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1);
    if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
      res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh));
      if (res) { return res }
    }

    // Case 4: Nowhere to move
    return null
  }

  // Commands are parameter-less actions that can be performed on an
  // editor, mostly used for keybindings.
  var commands = {
    selectAll: selectAll,
    singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
    killLine: function (cm) { return deleteNearSelection(cm, function (range) {
      if (range.empty()) {
        var len = getLine(cm.doc, range.head.line).text.length;
        if (range.head.ch == len && range.head.line < cm.lastLine())
          { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
        else
          { return {from: range.head, to: Pos(range.head.line, len)} }
      } else {
        return {from: range.from(), to: range.to()}
      }
    }); },
    deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
      from: Pos(range.from().line, 0),
      to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
    }); }); },
    delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
      from: Pos(range.from().line, 0), to: range.from()
    }); }); },
    delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
      var top = cm.charCoords(range.head, "div").top + 5;
      var leftPos = cm.coordsChar({left: 0, top: top}, "div");
      return {from: leftPos, to: range.from()}
    }); },
    delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
      var top = cm.charCoords(range.head, "div").top + 5;
      var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
      return {from: range.from(), to: rightPos }
    }); },
    undo: function (cm) { return cm.undo(); },
    redo: function (cm) { return cm.redo(); },
    undoSelection: function (cm) { return cm.undoSelection(); },
    redoSelection: function (cm) { return cm.redoSelection(); },
    goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
    goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
    goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
      {origin: "+move", bias: 1}
    ); },
    goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
      {origin: "+move", bias: 1}
    ); },
    goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
      {origin: "+move", bias: -1}
    ); },
    goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
      var top = cm.cursorCoords(range.head, "div").top + 5;
      return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
    }, sel_move); },
    goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
      var top = cm.cursorCoords(range.head, "div").top + 5;
      return cm.coordsChar({left: 0, top: top}, "div")
    }, sel_move); },
    goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
      var top = cm.cursorCoords(range.head, "div").top + 5;
      var pos = cm.coordsChar({left: 0, top: top}, "div");
      if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
      return pos
    }, sel_move); },
    goLineUp: function (cm) { return cm.moveV(-1, "line"); },
    goLineDown: function (cm) { return cm.moveV(1, "line"); },
    goPageUp: function (cm) { return cm.moveV(-1, "page"); },
    goPageDown: function (cm) { return cm.moveV(1, "page"); },
    goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
    goCharRight: function (cm) { return cm.moveH(1, "char"); },
    goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
    goColumnRight: function (cm) { return cm.moveH(1, "column"); },
    goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
    goGroupRight: function (cm) { return cm.moveH(1, "group"); },
    goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
    goWordRight: function (cm) { return cm.moveH(1, "word"); },
    delCharBefore: function (cm) { return cm.deleteH(-1, "codepoint"); },
    delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
    delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
    delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
    delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
    delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
    indentAuto: function (cm) { return cm.indentSelection("smart"); },
    indentMore: function (cm) { return cm.indentSelection("add"); },
    indentLess: function (cm) { return cm.indentSelection("subtract"); },
    insertTab: function (cm) { return cm.replaceSelection("\t"); },
    insertSoftTab: function (cm) {
      var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
      for (var i = 0; i < ranges.length; i++) {
        var pos = ranges[i].from();
        var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
        spaces.push(spaceStr(tabSize - col % tabSize));
      }
      cm.replaceSelections(spaces);
    },
    defaultTab: function (cm) {
      if (cm.somethingSelected()) { cm.indentSelection("add"); }
      else { cm.execCommand("insertTab"); }
    },
    // Swap the two chars left and right of each selection's head.
    // Move cursor behind the two swapped characters afterwards.
    //
    // Doesn't consider line feeds a character.
    // Doesn't scan more than one line above to find a character.
    // Doesn't do anything on an empty line.
    // Doesn't do anything with non-empty selections.
    transposeChars: function (cm) { return runInOp(cm, function () {
      var ranges = cm.listSelections(), newSel = [];
      for (var i = 0; i < ranges.length; i++) {
        if (!ranges[i].empty()) { continue }
        var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
        if (line) {
          if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); }
          if (cur.ch > 0) {
            cur = new Pos(cur.line, cur.ch + 1);
            cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
                            Pos(cur.line, cur.ch - 2), cur, "+transpose");
          } else if (cur.line > cm.doc.first) {
            var prev = getLine(cm.doc, cur.line - 1).text;
            if (prev) {
              cur = new Pos(cur.line, 1);
              cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
                              prev.charAt(prev.length - 1),
                              Pos(cur.line - 1, prev.length - 1), cur, "+transpose");
            }
          }
        }
        newSel.push(new Range(cur, cur));
      }
      cm.setSelections(newSel);
    }); },
    newlineAndIndent: function (cm) { return runInOp(cm, function () {
      var sels = cm.listSelections();
      for (var i = sels.length - 1; i >= 0; i--)
        { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); }
      sels = cm.listSelections();
      for (var i$1 = 0; i$1 < sels.length; i$1++)
        { cm.indentLine(sels[i$1].from().line, null, true); }
      ensureCursorVisible(cm);
    }); },
    openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
    toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
  };


  function lineStart(cm, lineN) {
    var line = getLine(cm.doc, lineN);
    var visual = visualLine(line);
    if (visual != line) { lineN = lineNo(visual); }
    return endOfLine(true, cm, visual, lineN, 1)
  }
  function lineEnd(cm, lineN) {
    var line = getLine(cm.doc, lineN);
    var visual = visualLineEnd(line);
    if (visual != line) { lineN = lineNo(visual); }
    return endOfLine(true, cm, line, lineN, -1)
  }
  function lineStartSmart(cm, pos) {
    var start = lineStart(cm, pos.line);
    var line = getLine(cm.doc, start.line);
    var order = getOrder(line, cm.doc.direction);
    if (!order || order[0].level == 0) {
      var firstNonWS = Math.max(start.ch, line.text.search(/\S/));
      var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
      return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
    }
    return start
  }

  // Run a handler that was bound to a key.
  function doHandleBinding(cm, bound, dropShift) {
    if (typeof bound == "string") {
      bound = commands[bound];
      if (!bound) { return false }
    }
    // Ensure previous input has been read, so that the handler sees a
    // consistent view of the document
    cm.display.input.ensurePolled();
    var prevShift = cm.display.shift, done = false;
    try {
      if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
      if (dropShift) { cm.display.shift = false; }
      done = bound(cm) != Pass;
    } finally {
      cm.display.shift = prevShift;
      cm.state.suppressEdits = false;
    }
    return done
  }

  function lookupKeyForEditor(cm, name, handle) {
    for (var i = 0; i < cm.state.keyMaps.length; i++) {
      var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
      if (result) { return result }
    }
    return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
      || lookupKey(name, cm.options.keyMap, handle, cm)
  }

  // Note that, despite the name, this function is also used to check
  // for bound mouse clicks.

  var stopSeq = new Delayed;

  function dispatchKey(cm, name, e, handle) {
    var seq = cm.state.keySeq;
    if (seq) {
      if (isModifierKey(name)) { return "handled" }
      if (/\'$/.test(name))
        { cm.state.keySeq = null; }
      else
        { stopSeq.set(50, function () {
          if (cm.state.keySeq == seq) {
            cm.state.keySeq = null;
            cm.display.input.reset();
          }
        }); }
      if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true }
    }
    return dispatchKeyInner(cm, name, e, handle)
  }

  function dispatchKeyInner(cm, name, e, handle) {
    var result = lookupKeyForEditor(cm, name, handle);

    if (result == "multi")
      { cm.state.keySeq = name; }
    if (result == "handled")
      { signalLater(cm, "keyHandled", cm, name, e); }

    if (result == "handled" || result == "multi") {
      e_preventDefault(e);
      restartBlink(cm);
    }

    return !!result
  }

  // Handle a key from the keydown event.
  function handleKeyBinding(cm, e) {
    var name = keyName(e, true);
    if (!name) { return false }

    if (e.shiftKey && !cm.state.keySeq) {
      // First try to resolve full name (including 'Shift-'). Failing
      // that, see if there is a cursor-motion command (starting with
      // 'go') bound to the keyname without 'Shift-'.
      return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
          || dispatchKey(cm, name, e, function (b) {
               if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
                 { return doHandleBinding(cm, b) }
             })
    } else {
      return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
    }
  }

  // Handle a key from the keypress event
  function handleCharBinding(cm, e, ch) {
    return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
  }

  var lastStoppedKey = null;
  function onKeyDown(e) {
    var cm = this;
    if (e.target && e.target != cm.display.input.getField()) { return }
    cm.curOp.focus = activeElt();
    if (signalDOMEvent(cm, e)) { return }
    // IE does strange things with escape.
    if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; }
    var code = e.keyCode;
    cm.display.shift = code == 16 || e.shiftKey;
    var handled = handleKeyBinding(cm, e);
    if (presto) {
      lastStoppedKey = handled ? code : null;
      // Opera has no cut event... we try to at least catch the key combo
      if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
        { cm.replaceSelection("", null, "cut"); }
    }
    if (gecko && !mac && !handled && code == 46 && e.shiftKey && !e.ctrlKey && document.execCommand)
      { document.execCommand("cut"); }

    // Turn mouse into crosshair when Alt is held on Mac.
    if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
      { showCrossHair(cm); }
  }

  function showCrossHair(cm) {
    var lineDiv = cm.display.lineDiv;
    addClass(lineDiv, "CodeMirror-crosshair");

    function up(e) {
      if (e.keyCode == 18 || !e.altKey) {
        rmClass(lineDiv, "CodeMirror-crosshair");
        off(document, "keyup", up);
        off(document, "mouseover", up);
      }
    }
    on(document, "keyup", up);
    on(document, "mouseover", up);
  }

  function onKeyUp(e) {
    if (e.keyCode == 16) { this.doc.sel.shift = false; }
    signalDOMEvent(this, e);
  }

  function onKeyPress(e) {
    var cm = this;
    if (e.target && e.target != cm.display.input.getField()) { return }
    if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
    var keyCode = e.keyCode, charCode = e.charCode;
    if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
    if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
    var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
    // Some browsers fire keypress events for backspace
    if (ch == "\x08") { return }
    if (handleCharBinding(cm, e, ch)) { return }
    cm.display.input.onKeyPress(e);
  }

  var DOUBLECLICK_DELAY = 400;

  var PastClick = function(time, pos, button) {
    this.time = time;
    this.pos = pos;
    this.button = button;
  };

  PastClick.prototype.compare = function (time, pos, button) {
    return this.time + DOUBLECLICK_DELAY > time &&
      cmp(pos, this.pos) == 0 && button == this.button
  };

  var lastClick, lastDoubleClick;
  function clickRepeat(pos, button) {
    var now = +new Date;
    if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
      lastClick = lastDoubleClick = null;
      return "triple"
    } else if (lastClick && lastClick.compare(now, pos, button)) {
      lastDoubleClick = new PastClick(now, pos, button);
      lastClick = null;
      return "double"
    } else {
      lastClick = new PastClick(now, pos, button);
      lastDoubleClick = null;
      return "single"
    }
  }

  // A mouse down can be a single click, double click, triple click,
  // start of selection drag, start of text drag, new cursor
  // (ctrl-click), rectangle drag (alt-drag), or xwin
  // middle-click-paste. Or it might be a click on something we should
  // not interfere with, such as a scrollbar or widget.
  function onMouseDown(e) {
    var cm = this, display = cm.display;
    if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
    display.input.ensurePolled();
    display.shift = e.shiftKey;

    if (eventInWidget(display, e)) {
      if (!webkit) {
        // Briefly turn off draggability, to allow widgets to do
        // normal dragging things.
        display.scroller.draggable = false;
        setTimeout(function () { return display.scroller.draggable = true; }, 100);
      }
      return
    }
    if (clickInGutter(cm, e)) { return }
    var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single";
    window.focus();

    // #3261: make sure, that we're not starting a second selection
    if (button == 1 && cm.state.selectingText)
      { cm.state.selectingText(e); }

    if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }

    if (button == 1) {
      if (pos) { leftButtonDown(cm, pos, repeat, e); }
      else if (e_target(e) == display.scroller) { e_preventDefault(e); }
    } else if (button == 2) {
      if (pos) { extendSelection(cm.doc, pos); }
      setTimeout(function () { return display.input.focus(); }, 20);
    } else if (button == 3) {
      if (captureRightClick) { cm.display.input.onContextMenu(e); }
      else { delayBlurEvent(cm); }
    }
  }

  function handleMappedButton(cm, button, pos, repeat, event) {
    var name = "Click";
    if (repeat == "double") { name = "Double" + name; }
    else if (repeat == "triple") { name = "Triple" + name; }
    name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name;

    return dispatchKey(cm,  addModifierNames(name, event), event, function (bound) {
      if (typeof bound == "string") { bound = commands[bound]; }
      if (!bound) { return false }
      var done = false;
      try {
        if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
        done = bound(cm, pos) != Pass;
      } finally {
        cm.state.suppressEdits = false;
      }
      return done
    })
  }

  function configureMouse(cm, repeat, event) {
    var option = cm.getOption("configureMouse");
    var value = option ? option(cm, repeat, event) : {};
    if (value.unit == null) {
      var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey;
      value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line";
    }
    if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; }
    if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; }
    if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); }
    return value
  }

  function leftButtonDown(cm, pos, repeat, event) {
    if (ie) { setTimeout(bind(ensureFocus, cm), 0); }
    else { cm.curOp.focus = activeElt(); }

    var behavior = configureMouse(cm, repeat, event);

    var sel = cm.doc.sel, contained;
    if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
        repeat == "single" && (contained = sel.contains(pos)) > -1 &&
        (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&
        (cmp(contained.to(), pos) > 0 || pos.xRel < 0))
      { leftButtonStartDrag(cm, event, pos, behavior); }
    else
      { leftButtonSelect(cm, event, pos, behavior); }
  }

  // Start a text drag. When it ends, see if any dragging actually
  // happen, and treat as a click if it didn't.
  function leftButtonStartDrag(cm, event, pos, behavior) {
    var display = cm.display, moved = false;
    var dragEnd = operation(cm, function (e) {
      if (webkit) { display.scroller.draggable = false; }
      cm.state.draggingText = false;
      if (cm.state.delayingBlurEvent) {
        if (cm.hasFocus()) { cm.state.delayingBlurEvent = false; }
        else { delayBlurEvent(cm); }
      }
      off(display.wrapper.ownerDocument, "mouseup", dragEnd);
      off(display.wrapper.ownerDocument, "mousemove", mouseMove);
      off(display.scroller, "dragstart", dragStart);
      off(display.scroller, "drop", dragEnd);
      if (!moved) {
        e_preventDefault(e);
        if (!behavior.addNew)
          { extendSelection(cm.doc, pos, null, null, behavior.extend); }
        // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
        if ((webkit && !safari) || ie && ie_version == 9)
          { setTimeout(function () {display.wrapper.ownerDocument.body.focus({preventScroll: true}); display.input.focus();}, 20); }
        else
          { display.input.focus(); }
      }
    });
    var mouseMove = function(e2) {
      moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10;
    };
    var dragStart = function () { return moved = true; };
    // Let the drag handler handle this.
    if (webkit) { display.scroller.draggable = true; }
    cm.state.draggingText = dragEnd;
    dragEnd.copy = !behavior.moveOnDrag;
    on(display.wrapper.ownerDocument, "mouseup", dragEnd);
    on(display.wrapper.ownerDocument, "mousemove", mouseMove);
    on(display.scroller, "dragstart", dragStart);
    on(display.scroller, "drop", dragEnd);

    cm.state.delayingBlurEvent = true;
    setTimeout(function () { return display.input.focus(); }, 20);
    // IE's approach to draggable
    if (display.scroller.dragDrop) { display.scroller.dragDrop(); }
  }

  function rangeForUnit(cm, pos, unit) {
    if (unit == "char") { return new Range(pos, pos) }
    if (unit == "word") { return cm.findWordAt(pos) }
    if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
    var result = unit(cm, pos);
    return new Range(result.from, result.to)
  }

  // Normal selection, as opposed to text dragging.
  function leftButtonSelect(cm, event, start, behavior) {
    if (ie) { delayBlurEvent(cm); }
    var display = cm.display, doc = cm.doc;
    e_preventDefault(event);

    var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
    if (behavior.addNew && !behavior.extend) {
      ourIndex = doc.sel.contains(start);
      if (ourIndex > -1)
        { ourRange = ranges[ourIndex]; }
      else
        { ourRange = new Range(start, start); }
    } else {
      ourRange = doc.sel.primary();
      ourIndex = doc.sel.primIndex;
    }

    if (behavior.unit == "rectangle") {
      if (!behavior.addNew) { ourRange = new Range(start, start); }
      start = posFromMouse(cm, event, true, true);
      ourIndex = -1;
    } else {
      var range = rangeForUnit(cm, start, behavior.unit);
      if (behavior.extend)
        { ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend); }
      else
        { ourRange = range; }
    }

    if (!behavior.addNew) {
      ourIndex = 0;
      setSelection(doc, new Selection([ourRange], 0), sel_mouse);
      startSel = doc.sel;
    } else if (ourIndex == -1) {
      ourIndex = ranges.length;
      setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex),
                   {scroll: false, origin: "*mouse"});
    } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) {
      setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
                   {scroll: false, origin: "*mouse"});
      startSel = doc.sel;
    } else {
      replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
    }

    var lastPos = start;
    function extendTo(pos) {
      if (cmp(lastPos, pos) == 0) { return }
      lastPos = pos;

      if (behavior.unit == "rectangle") {
        var ranges = [], tabSize = cm.options.tabSize;
        var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
        var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
        var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
        for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
             line <= end; line++) {
          var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
          if (left == right)
            { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); }
          else if (text.length > leftPos)
            { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); }
        }
        if (!ranges.length) { ranges.push(new Range(start, start)); }
        setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
                     {origin: "*mouse", scroll: false});
        cm.scrollIntoView(pos);
      } else {
        var oldRange = ourRange;
        var range = rangeForUnit(cm, pos, behavior.unit);
        var anchor = oldRange.anchor, head;
        if (cmp(range.anchor, anchor) > 0) {
          head = range.head;
          anchor = minPos(oldRange.from(), range.anchor);
        } else {
          head = range.anchor;
          anchor = maxPos(oldRange.to(), range.head);
        }
        var ranges$1 = startSel.ranges.slice(0);
        ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head));
        setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse);
      }
    }

    var editorSize = display.wrapper.getBoundingClientRect();
    // Used to ensure timeout re-tries don't fire when another extend
    // happened in the meantime (clearTimeout isn't reliable -- at
    // least on Chrome, the timeouts still happen even when cleared,
    // if the clear happens after their scheduled firing time).
    var counter = 0;

    function extend(e) {
      var curCount = ++counter;
      var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle");
      if (!cur) { return }
      if (cmp(cur, lastPos) != 0) {
        cm.curOp.focus = activeElt();
        extendTo(cur);
        var visible = visibleLines(display, doc);
        if (cur.line >= visible.to || cur.line < visible.from)
          { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); }
      } else {
        var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
        if (outside) { setTimeout(operation(cm, function () {
          if (counter != curCount) { return }
          display.scroller.scrollTop += outside;
          extend(e);
        }), 50); }
      }
    }

    function done(e) {
      cm.state.selectingText = false;
      counter = Infinity;
      // If e is null or undefined we interpret this as someone trying
      // to explicitly cancel the selection rather than the user
      // letting go of the mouse button.
      if (e) {
        e_preventDefault(e);
        display.input.focus();
      }
      off(display.wrapper.ownerDocument, "mousemove", move);
      off(display.wrapper.ownerDocument, "mouseup", up);
      doc.history.lastSelOrigin = null;
    }

    var move = operation(cm, function (e) {
      if (e.buttons === 0 || !e_button(e)) { done(e); }
      else { extend(e); }
    });
    var up = operation(cm, done);
    cm.state.selectingText = up;
    on(display.wrapper.ownerDocument, "mousemove", move);
    on(display.wrapper.ownerDocument, "mouseup", up);
  }

  // Used when mouse-selecting to adjust the anchor to the proper side
  // of a bidi jump depending on the visual position of the head.
  function bidiSimplify(cm, range) {
    var anchor = range.anchor;
    var head = range.head;
    var anchorLine = getLine(cm.doc, anchor.line);
    if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range }
    var order = getOrder(anchorLine);
    if (!order) { return range }
    var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index];
    if (part.from != anchor.ch && part.to != anchor.ch) { return range }
    var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1);
    if (boundary == 0 || boundary == order.length) { return range }

    // Compute the relative visual position of the head compared to the
    // anchor (<0 is to the left, >0 to the right)
    var leftSide;
    if (head.line != anchor.line) {
      leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0;
    } else {
      var headIndex = getBidiPartAt(order, head.ch, head.sticky);
      var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1);
      if (headIndex == boundary - 1 || headIndex == boundary)
        { leftSide = dir < 0; }
      else
        { leftSide = dir > 0; }
    }

    var usePart = order[boundary + (leftSide ? -1 : 0)];
    var from = leftSide == (usePart.level == 1);
    var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before";
    return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head)
  }


  // Determines whether an event happened in the gutter, and fires the
  // handlers for the corresponding event.
  function gutterEvent(cm, e, type, prevent) {
    var mX, mY;
    if (e.touches) {
      mX = e.touches[0].clientX;
      mY = e.touches[0].clientY;
    } else {
      try { mX = e.clientX; mY = e.clientY; }
      catch(e$1) { return false }
    }
    if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
    if (prevent) { e_preventDefault(e); }

    var display = cm.display;
    var lineBox = display.lineDiv.getBoundingClientRect();

    if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
    mY -= lineBox.top - display.viewOffset;

    for (var i = 0; i < cm.display.gutterSpecs.length; ++i) {
      var g = display.gutters.childNodes[i];
      if (g && g.getBoundingClientRect().right >= mX) {
        var line = lineAtHeight(cm.doc, mY);
        var gutter = cm.display.gutterSpecs[i];
        signal(cm, type, cm, line, gutter.className, e);
        return e_defaultPrevented(e)
      }
    }
  }

  function clickInGutter(cm, e) {
    return gutterEvent(cm, e, "gutterClick", true)
  }

  // CONTEXT MENU HANDLING

  // To make the context menu work, we need to briefly unhide the
  // textarea (making it as unobtrusive as possible) to let the
  // right-click take effect on it.
  function onContextMenu(cm, e) {
    if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
    if (signalDOMEvent(cm, e, "contextmenu")) { return }
    if (!captureRightClick) { cm.display.input.onContextMenu(e); }
  }

  function contextMenuInGutter(cm, e) {
    if (!hasHandler(cm, "gutterContextMenu")) { return false }
    return gutterEvent(cm, e, "gutterContextMenu", false)
  }

  function themeChanged(cm) {
    cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
      cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
    clearCaches(cm);
  }

  var Init = {toString: function(){return "CodeMirror.Init"}};

  var defaults = {};
  var optionHandlers = {};

  function defineOptions(CodeMirror) {
    var optionHandlers = CodeMirror.optionHandlers;

    function option(name, deflt, handle, notOnInit) {
      CodeMirror.defaults[name] = deflt;
      if (handle) { optionHandlers[name] =
        notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; }
    }

    CodeMirror.defineOption = option;

    // Passed to option handlers when there is no old value.
    CodeMirror.Init = Init;

    // These two are, on init, called from the constructor because they
    // have to be initialized before the editor can start at all.
    option("value", "", function (cm, val) { return cm.setValue(val); }, true);
    option("mode", null, function (cm, val) {
      cm.doc.modeOption = val;
      loadMode(cm);
    }, true);

    option("indentUnit", 2, loadMode, true);
    option("indentWithTabs", false);
    option("smartIndent", true);
    option("tabSize", 4, function (cm) {
      resetModeState(cm);
      clearCaches(cm);
      regChange(cm);
    }, true);

    option("lineSeparator", null, function (cm, val) {
      cm.doc.lineSep = val;
      if (!val) { return }
      var newBreaks = [], lineNo = cm.doc.first;
      cm.doc.iter(function (line) {
        for (var pos = 0;;) {
          var found = line.text.indexOf(val, pos);
          if (found == -1) { break }
          pos = found + val.length;
          newBreaks.push(Pos(lineNo, found));
        }
        lineNo++;
      });
      for (var i = newBreaks.length - 1; i >= 0; i--)
        { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); }
    });
    option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200c\u200e\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g, function (cm, val, old) {
      cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
      if (old != Init) { cm.refresh(); }
    });
    option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true);
    option("electricChars", true);
    option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
      throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
    }, true);
    option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true);
    option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true);
    option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true);
    option("rtlMoveVisually", !windows);
    option("wholeLineUpdateBefore", true);

    option("theme", "default", function (cm) {
      themeChanged(cm);
      updateGutters(cm);
    }, true);
    option("keyMap", "default", function (cm, val, old) {
      var next = getKeyMap(val);
      var prev = old != Init && getKeyMap(old);
      if (prev && prev.detach) { prev.detach(cm, next); }
      if (next.attach) { next.attach(cm, prev || null); }
    });
    option("extraKeys", null);
    option("configureMouse", null);

    option("lineWrapping", false, wrappingChanged, true);
    option("gutters", [], function (cm, val) {
      cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers);
      updateGutters(cm);
    }, true);
    option("fixedGutter", true, function (cm, val) {
      cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
      cm.refresh();
    }, true);
    option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true);
    option("scrollbarStyle", "native", function (cm) {
      initScrollbars(cm);
      updateScrollbars(cm);
      cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
      cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
    }, true);
    option("lineNumbers", false, function (cm, val) {
      cm.display.gutterSpecs = getGutters(cm.options.gutters, val);
      updateGutters(cm);
    }, true);
    option("firstLineNumber", 1, updateGutters, true);
    option("lineNumberFormatter", function (integer) { return integer; }, updateGutters, true);
    option("showCursorWhenSelecting", false, updateSelection, true);

    option("resetSelectionOnContextMenu", true);
    option("lineWiseCopyCut", true);
    option("pasteLinesPerSelection", true);
    option("selectionsMayTouch", false);

    option("readOnly", false, function (cm, val) {
      if (val == "nocursor") {
        onBlur(cm);
        cm.display.input.blur();
      }
      cm.display.input.readOnlyChanged(val);
    });

    option("screenReaderLabel", null, function (cm, val) {
      val = (val === '') ? null : val;
      cm.display.input.screenReaderLabelChanged(val);
    });

    option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true);
    option("dragDrop", true, dragDropChanged);
    option("allowDropFileTypes", null);

    option("cursorBlinkRate", 530);
    option("cursorScrollMargin", 0);
    option("cursorHeight", 1, updateSelection, true);
    option("singleCursorHeightPerLine", true, updateSelection, true);
    option("workTime", 100);
    option("workDelay", 100);
    option("flattenSpans", true, resetModeState, true);
    option("addModeClass", false, resetModeState, true);
    option("pollInterval", 100);
    option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; });
    option("historyEventDelay", 1250);
    option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true);
    option("maxHighlightLength", 10000, resetModeState, true);
    option("moveInputWithCursor", true, function (cm, val) {
      if (!val) { cm.display.input.resetPosition(); }
    });

    option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; });
    option("autofocus", null);
    option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true);
    option("phrases", null);
  }

  function dragDropChanged(cm, value, old) {
    var wasOn = old && old != Init;
    if (!value != !wasOn) {
      var funcs = cm.display.dragFunctions;
      var toggle = value ? on : off;
      toggle(cm.display.scroller, "dragstart", funcs.start);
      toggle(cm.display.scroller, "dragenter", funcs.enter);
      toggle(cm.display.scroller, "dragover", funcs.over);
      toggle(cm.display.scroller, "dragleave", funcs.leave);
      toggle(cm.display.scroller, "drop", funcs.drop);
    }
  }

  function wrappingChanged(cm) {
    if (cm.options.lineWrapping) {
      addClass(cm.display.wrapper, "CodeMirror-wrap");
      cm.display.sizer.style.minWidth = "";
      cm.display.sizerWidth = null;
    } else {
      rmClass(cm.display.wrapper, "CodeMirror-wrap");
      findMaxLine(cm);
    }
    estimateLineHeights(cm);
    regChange(cm);
    clearCaches(cm);
    setTimeout(function () { return updateScrollbars(cm); }, 100);
  }

  // A CodeMirror instance represents an editor. This is the object
  // that user code is usually dealing with.

  function CodeMirror(place, options) {
    var this$1 = this;

    if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }

    this.options = options = options ? copyObj(options) : {};
    // Determine effective options based on given values and defaults.
    copyObj(defaults, options, false);

    var doc = options.value;
    if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); }
    else if (options.mode) { doc.modeOption = options.mode; }
    this.doc = doc;

    var input = new CodeMirror.inputStyles[options.inputStyle](this);
    var display = this.display = new Display(place, doc, input, options);
    display.wrapper.CodeMirror = this;
    themeChanged(this);
    if (options.lineWrapping)
      { this.display.wrapper.className += " CodeMirror-wrap"; }
    initScrollbars(this);

    this.state = {
      keyMaps: [],  // stores maps added by addKeyMap
      overlays: [], // highlighting overlays, as added by addOverlay
      modeGen: 0,   // bumped when mode/overlay changes, used to invalidate highlighting info
      overwrite: false,
      delayingBlurEvent: false,
      focused: false,
      suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
      pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll
      selectingText: false,
      draggingText: false,
      highlight: new Delayed(), // stores highlight worker timeout
      keySeq: null,  // Unfinished key sequence
      specialChars: null
    };

    if (options.autofocus && !mobile) { display.input.focus(); }

    // Override magic textarea content restore that IE sometimes does
    // on our hidden textarea on reload
    if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); }

    registerEventHandlers(this);
    ensureGlobalHandlers();

    startOperation(this);
    this.curOp.forceUpdate = true;
    attachDoc(this, doc);

    if ((options.autofocus && !mobile) || this.hasFocus())
      { setTimeout(function () {
        if (this$1.hasFocus() && !this$1.state.focused) { onFocus(this$1); }
      }, 20); }
    else
      { onBlur(this); }

    for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
      { optionHandlers[opt](this, options[opt], Init); } }
    maybeUpdateLineNumberWidth(this);
    if (options.finishInit) { options.finishInit(this); }
    for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this); }
    endOperation(this);
    // Suppress optimizelegibility in Webkit, since it breaks text
    // measuring on line wrapping boundaries.
    if (webkit && options.lineWrapping &&
        getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
      { display.lineDiv.style.textRendering = "auto"; }
  }

  // The default configuration options.
  CodeMirror.defaults = defaults;
  // Functions to run when options are changed.
  CodeMirror.optionHandlers = optionHandlers;

  // Attach the necessary event handlers when initializing the editor
  function registerEventHandlers(cm) {
    var d = cm.display;
    on(d.scroller, "mousedown", operation(cm, onMouseDown));
    // Older IE's will not fire a second mousedown for a double click
    if (ie && ie_version < 11)
      { on(d.scroller, "dblclick", operation(cm, function (e) {
        if (signalDOMEvent(cm, e)) { return }
        var pos = posFromMouse(cm, e);
        if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
        e_preventDefault(e);
        var word = cm.findWordAt(pos);
        extendSelection(cm.doc, word.anchor, word.head);
      })); }
    else
      { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); }
    // Some browsers fire contextmenu *after* opening the menu, at
    // which point we can't mess with it anymore. Context menu is
    // handled in onMouseDown for these browsers.
    on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); });
    on(d.input.getField(), "contextmenu", function (e) {
      if (!d.scroller.contains(e.target)) { onContextMenu(cm, e); }
    });

    // Used to suppress mouse event handling when a touch happens
    var touchFinished, prevTouch = {end: 0};
    function finishTouch() {
      if (d.activeTouch) {
        touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000);
        prevTouch = d.activeTouch;
        prevTouch.end = +new Date;
      }
    }
    function isMouseLikeTouchEvent(e) {
      if (e.touches.length != 1) { return false }
      var touch = e.touches[0];
      return touch.radiusX <= 1 && touch.radiusY <= 1
    }
    function farAway(touch, other) {
      if (other.left == null) { return true }
      var dx = other.left - touch.left, dy = other.top - touch.top;
      return dx * dx + dy * dy > 20 * 20
    }
    on(d.scroller, "touchstart", function (e) {
      if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
        d.input.ensurePolled();
        clearTimeout(touchFinished);
        var now = +new Date;
        d.activeTouch = {start: now, moved: false,
                         prev: now - prevTouch.end <= 300 ? prevTouch : null};
        if (e.touches.length == 1) {
          d.activeTouch.left = e.touches[0].pageX;
          d.activeTouch.top = e.touches[0].pageY;
        }
      }
    });
    on(d.scroller, "touchmove", function () {
      if (d.activeTouch) { d.activeTouch.moved = true; }
    });
    on(d.scroller, "touchend", function (e) {
      var touch = d.activeTouch;
      if (touch && !eventInWidget(d, e) && touch.left != null &&
          !touch.moved && new Date - touch.start < 300) {
        var pos = cm.coordsChar(d.activeTouch, "page"), range;
        if (!touch.prev || farAway(touch, touch.prev)) // Single tap
          { range = new Range(pos, pos); }
        else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
          { range = cm.findWordAt(pos); }
        else // Triple tap
          { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); }
        cm.setSelection(range.anchor, range.head);
        cm.focus();
        e_preventDefault(e);
      }
      finishTouch();
    });
    on(d.scroller, "touchcancel", finishTouch);

    // Sync scrolling between fake scrollbars and real scrollable
    // area, ensure viewport is updated when scrolling.
    on(d.scroller, "scroll", function () {
      if (d.scroller.clientHeight) {
        updateScrollTop(cm, d.scroller.scrollTop);
        setScrollLeft(cm, d.scroller.scrollLeft, true);
        signal(cm, "scroll", cm);
      }
    });

    // Listen to wheel events in order to try and update the viewport on time.
    on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); });
    on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); });

    // Prevent wrapper from ever scrolling
    on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });

    d.dragFunctions = {
      enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }},
      over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},
      start: function (e) { return onDragStart(cm, e); },
      drop: operation(cm, onDrop),
      leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}
    };

    var inp = d.input.getField();
    on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); });
    on(inp, "keydown", operation(cm, onKeyDown));
    on(inp, "keypress", operation(cm, onKeyPress));
    on(inp, "focus", function (e) { return onFocus(cm, e); });
    on(inp, "blur", function (e) { return onBlur(cm, e); });
  }

  var initHooks = [];
  CodeMirror.defineInitHook = function (f) { return initHooks.push(f); };

  // Indent the given line. The how parameter can be "smart",
  // "add"/null, "subtract", or "prev". When aggressive is false
  // (typically set to true for forced single-line indents), empty
  // lines are not indented, and places where the mode returns Pass
  // are left alone.
  function indentLine(cm, n, how, aggressive) {
    var doc = cm.doc, state;
    if (how == null) { how = "add"; }
    if (how == "smart") {
      // Fall back to "prev" when the mode doesn't have an indentation
      // method.
      if (!doc.mode.indent) { how = "prev"; }
      else { state = getContextBefore(cm, n).state; }
    }

    var tabSize = cm.options.tabSize;
    var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
    if (line.stateAfter) { line.stateAfter = null; }
    var curSpaceString = line.text.match(/^\s*/)[0], indentation;
    if (!aggressive && !/\S/.test(line.text)) {
      indentation = 0;
      how = "not";
    } else if (how == "smart") {
      indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
      if (indentation == Pass || indentation > 150) {
        if (!aggressive) { return }
        how = "prev";
      }
    }
    if (how == "prev") {
      if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); }
      else { indentation = 0; }
    } else if (how == "add") {
      indentation = curSpace + cm.options.indentUnit;
    } else if (how == "subtract") {
      indentation = curSpace - cm.options.indentUnit;
    } else if (typeof how == "number") {
      indentation = curSpace + how;
    }
    indentation = Math.max(0, indentation);

    var indentString = "", pos = 0;
    if (cm.options.indentWithTabs)
      { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} }
    if (pos < indentation) { indentString += spaceStr(indentation - pos); }

    if (indentString != curSpaceString) {
      replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
      line.stateAfter = null;
      return true
    } else {
      // Ensure that, if the cursor was in the whitespace at the start
      // of the line, it is moved to the end of that space.
      for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
        var range = doc.sel.ranges[i$1];
        if (range.head.line == n && range.head.ch < curSpaceString.length) {
          var pos$1 = Pos(n, curSpaceString.length);
          replaceOneSelection(doc, i$1, new Range(pos$1, pos$1));
          break
        }
      }
    }
  }

  // This will be set to a {lineWise: bool, text: [string]} object, so
  // that, when pasting, we know what kind of selections the copied
  // text was made out of.
  var lastCopied = null;

  function setLastCopied(newLastCopied) {
    lastCopied = newLastCopied;
  }

  function applyTextInput(cm, inserted, deleted, sel, origin) {
    var doc = cm.doc;
    cm.display.shift = false;
    if (!sel) { sel = doc.sel; }

    var recent = +new Date - 200;
    var paste = origin == "paste" || cm.state.pasteIncoming > recent;
    var textLines = splitLinesAuto(inserted), multiPaste = null;
    // When pasting N lines into N selections, insert one line per selection
    if (paste && sel.ranges.length > 1) {
      if (lastCopied && lastCopied.text.join("\n") == inserted) {
        if (sel.ranges.length % lastCopied.text.length == 0) {
          multiPaste = [];
          for (var i = 0; i < lastCopied.text.length; i++)
            { multiPaste.push(doc.splitLines(lastCopied.text[i])); }
        }
      } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
        multiPaste = map(textLines, function (l) { return [l]; });
      }
    }

    var updateInput = cm.curOp.updateInput;
    // Normal behavior is to insert the new text into every selection
    for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
      var range = sel.ranges[i$1];
      var from = range.from(), to = range.to();
      if (range.empty()) {
        if (deleted && deleted > 0) // Handle deletion
          { from = Pos(from.line, from.ch - deleted); }
        else if (cm.state.overwrite && !paste) // Handle overwrite
          { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); }
        else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == textLines.join("\n"))
          { from = to = Pos(from.line, 0); }
      }
      var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
                         origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")};
      makeChange(cm.doc, changeEvent);
      signalLater(cm, "inputRead", cm, changeEvent);
    }
    if (inserted && !paste)
      { triggerElectric(cm, inserted); }

    ensureCursorVisible(cm);
    if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; }
    cm.curOp.typing = true;
    cm.state.pasteIncoming = cm.state.cutIncoming = -1;
  }

  function handlePaste(e, cm) {
    var pasted = e.clipboardData && e.clipboardData.getData("Text");
    if (pasted) {
      e.preventDefault();
      if (!cm.isReadOnly() && !cm.options.disableInput)
        { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); }
      return true
    }
  }

  function triggerElectric(cm, inserted) {
    // When an 'electric' character is inserted, immediately trigger a reindent
    if (!cm.options.electricChars || !cm.options.smartIndent) { return }
    var sel = cm.doc.sel;

    for (var i = sel.ranges.length - 1; i >= 0; i--) {
      var range = sel.ranges[i];
      if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue }
      var mode = cm.getModeAt(range.head);
      var indented = false;
      if (mode.electricChars) {
        for (var j = 0; j < mode.electricChars.length; j++)
          { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
            indented = indentLine(cm, range.head.line, "smart");
            break
          } }
      } else if (mode.electricInput) {
        if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))
          { indented = indentLine(cm, range.head.line, "smart"); }
      }
      if (indented) { signalLater(cm, "electricInput", cm, range.head.line); }
    }
  }

  function copyableRanges(cm) {
    var text = [], ranges = [];
    for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
      var line = cm.doc.sel.ranges[i].head.line;
      var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
      ranges.push(lineRange);
      text.push(cm.getRange(lineRange.anchor, lineRange.head));
    }
    return {text: text, ranges: ranges}
  }

  function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) {
    field.setAttribute("autocorrect", autocorrect ? "" : "off");
    field.setAttribute("autocapitalize", autocapitalize ? "" : "off");
    field.setAttribute("spellcheck", !!spellcheck);
  }

  function hiddenTextarea() {
    var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none");
    var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
    // The textarea is kept positioned near the cursor to prevent the
    // fact that it'll be scrolled into view on input from scrolling
    // our fake cursor out of view. On webkit, when wrap=off, paste is
    // very slow. So make the area wide instead.
    if (webkit) { te.style.width = "1000px"; }
    else { te.setAttribute("wrap", "off"); }
    // If border: 0; -- iOS fails to open keyboard (issue #1287)
    if (ios) { te.style.border = "1px solid black"; }
    disableBrowserMagic(te);
    return div
  }

  // The publicly visible API. Note that methodOp(f) means
  // 'wrap f in an operation, performed on its `this` parameter'.

  // This is not the complete set of editor methods. Most of the
  // methods defined on the Doc type are also injected into
  // CodeMirror.prototype, for backwards compatibility and
  // convenience.

  function addEditorMethods(CodeMirror) {
    var optionHandlers = CodeMirror.optionHandlers;

    var helpers = CodeMirror.helpers = {};

    CodeMirror.prototype = {
      constructor: CodeMirror,
      focus: function(){window.focus(); this.display.input.focus();},

      setOption: function(option, value) {
        var options = this.options, old = options[option];
        if (options[option] == value && option != "mode") { return }
        options[option] = value;
        if (optionHandlers.hasOwnProperty(option))
          { operation(this, optionHandlers[option])(this, value, old); }
        signal(this, "optionChange", this, option);
      },

      getOption: function(option) {return this.options[option]},
      getDoc: function() {return this.doc},

      addKeyMap: function(map, bottom) {
        this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map));
      },
      removeKeyMap: function(map) {
        var maps = this.state.keyMaps;
        for (var i = 0; i < maps.length; ++i)
          { if (maps[i] == map || maps[i].name == map) {
            maps.splice(i, 1);
            return true
          } }
      },

      addOverlay: methodOp(function(spec, options) {
        var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
        if (mode.startState) { throw new Error("Overlays may not be stateful.") }
        insertSorted(this.state.overlays,
                     {mode: mode, modeSpec: spec, opaque: options && options.opaque,
                      priority: (options && options.priority) || 0},
                     function (overlay) { return overlay.priority; });
        this.state.modeGen++;
        regChange(this);
      }),
      removeOverlay: methodOp(function(spec) {
        var overlays = this.state.overlays;
        for (var i = 0; i < overlays.length; ++i) {
          var cur = overlays[i].modeSpec;
          if (cur == spec || typeof spec == "string" && cur.name == spec) {
            overlays.splice(i, 1);
            this.state.modeGen++;
            regChange(this);
            return
          }
        }
      }),

      indentLine: methodOp(function(n, dir, aggressive) {
        if (typeof dir != "string" && typeof dir != "number") {
          if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; }
          else { dir = dir ? "add" : "subtract"; }
        }
        if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); }
      }),
      indentSelection: methodOp(function(how) {
        var ranges = this.doc.sel.ranges, end = -1;
        for (var i = 0; i < ranges.length; i++) {
          var range = ranges[i];
          if (!range.empty()) {
            var from = range.from(), to = range.to();
            var start = Math.max(end, from.line);
            end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
            for (var j = start; j < end; ++j)
              { indentLine(this, j, how); }
            var newRanges = this.doc.sel.ranges;
            if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
              { replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); }
          } else if (range.head.line > end) {
            indentLine(this, range.head.line, how, true);
            end = range.head.line;
            if (i == this.doc.sel.primIndex) { ensureCursorVisible(this); }
          }
        }
      }),

      // Fetch the parser token for a given character. Useful for hacks
      // that want to inspect the mode state (say, for completion).
      getTokenAt: function(pos, precise) {
        return takeToken(this, pos, precise)
      },

      getLineTokens: function(line, precise) {
        return takeToken(this, Pos(line), precise, true)
      },

      getTokenTypeAt: function(pos) {
        pos = clipPos(this.doc, pos);
        var styles = getLineStyles(this, getLine(this.doc, pos.line));
        var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
        var type;
        if (ch == 0) { type = styles[2]; }
        else { for (;;) {
          var mid = (before + after) >> 1;
          if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; }
          else if (styles[mid * 2 + 1] < ch) { before = mid + 1; }
          else { type = styles[mid * 2 + 2]; break }
        } }
        var cut = type ? type.indexOf("overlay ") : -1;
        return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
      },

      getModeAt: function(pos) {
        var mode = this.doc.mode;
        if (!mode.innerMode) { return mode }
        return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
      },

      getHelper: function(pos, type) {
        return this.getHelpers(pos, type)[0]
      },

      getHelpers: function(pos, type) {
        var found = [];
        if (!helpers.hasOwnProperty(type)) { return found }
        var help = helpers[type], mode = this.getModeAt(pos);
        if (typeof mode[type] == "string") {
          if (help[mode[type]]) { found.push(help[mode[type]]); }
        } else if (mode[type]) {
          for (var i = 0; i < mode[type].length; i++) {
            var val = help[mode[type][i]];
            if (val) { found.push(val); }
          }
        } else if (mode.helperType && help[mode.helperType]) {
          found.push(help[mode.helperType]);
        } else if (help[mode.name]) {
          found.push(help[mode.name]);
        }
        for (var i$1 = 0; i$1 < help._global.length; i$1++) {
          var cur = help._global[i$1];
          if (cur.pred(mode, this) && indexOf(found, cur.val) == -1)
            { found.push(cur.val); }
        }
        return found
      },

      getStateAfter: function(line, precise) {
        var doc = this.doc;
        line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
        return getContextBefore(this, line + 1, precise).state
      },

      cursorCoords: function(start, mode) {
        var pos, range = this.doc.sel.primary();
        if (start == null) { pos = range.head; }
        else if (typeof start == "object") { pos = clipPos(this.doc, start); }
        else { pos = start ? range.from() : range.to(); }
        return cursorCoords(this, pos, mode || "page")
      },

      charCoords: function(pos, mode) {
        return charCoords(this, clipPos(this.doc, pos), mode || "page")
      },

      coordsChar: function(coords, mode) {
        coords = fromCoordSystem(this, coords, mode || "page");
        return coordsChar(this, coords.left, coords.top)
      },

      lineAtHeight: function(height, mode) {
        height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
        return lineAtHeight(this.doc, height + this.display.viewOffset)
      },
      heightAtLine: function(line, mode, includeWidgets) {
        var end = false, lineObj;
        if (typeof line == "number") {
          var last = this.doc.first + this.doc.size - 1;
          if (line < this.doc.first) { line = this.doc.first; }
          else if (line > last) { line = last; end = true; }
          lineObj = getLine(this.doc, line);
        } else {
          lineObj = line;
        }
        return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top +
          (end ? this.doc.height - heightAtLine(lineObj) : 0)
      },

      defaultTextHeight: function() { return textHeight(this.display) },
      defaultCharWidth: function() { return charWidth(this.display) },

      getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},

      addWidget: function(pos, node, scroll, vert, horiz) {
        var display = this.display;
        pos = cursorCoords(this, clipPos(this.doc, pos));
        var top = pos.bottom, left = pos.left;
        node.style.position = "absolute";
        node.setAttribute("cm-ignore-events", "true");
        this.display.input.setUneditable(node);
        display.sizer.appendChild(node);
        if (vert == "over") {
          top = pos.top;
        } else if (vert == "above" || vert == "near") {
          var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
          hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
          // Default to positioning above (if specified and possible); otherwise default to positioning below
          if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
            { top = pos.top - node.offsetHeight; }
          else if (pos.bottom + node.offsetHeight <= vspace)
            { top = pos.bottom; }
          if (left + node.offsetWidth > hspace)
            { left = hspace - node.offsetWidth; }
        }
        node.style.top = top + "px";
        node.style.left = node.style.right = "";
        if (horiz == "right") {
          left = display.sizer.clientWidth - node.offsetWidth;
          node.style.right = "0px";
        } else {
          if (horiz == "left") { left = 0; }
          else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; }
          node.style.left = left + "px";
        }
        if (scroll)
          { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); }
      },

      triggerOnKeyDown: methodOp(onKeyDown),
      triggerOnKeyPress: methodOp(onKeyPress),
      triggerOnKeyUp: onKeyUp,
      triggerOnMouseDown: methodOp(onMouseDown),

      execCommand: function(cmd) {
        if (commands.hasOwnProperty(cmd))
          { return commands[cmd].call(null, this) }
      },

      triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),

      findPosH: function(from, amount, unit, visually) {
        var dir = 1;
        if (amount < 0) { dir = -1; amount = -amount; }
        var cur = clipPos(this.doc, from);
        for (var i = 0; i < amount; ++i) {
          cur = findPosH(this.doc, cur, dir, unit, visually);
          if (cur.hitSide) { break }
        }
        return cur
      },

      moveH: methodOp(function(dir, unit) {
        var this$1 = this;

        this.extendSelectionsBy(function (range) {
          if (this$1.display.shift || this$1.doc.extend || range.empty())
            { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) }
          else
            { return dir < 0 ? range.from() : range.to() }
        }, sel_move);
      }),

      deleteH: methodOp(function(dir, unit) {
        var sel = this.doc.sel, doc = this.doc;
        if (sel.somethingSelected())
          { doc.replaceSelection("", null, "+delete"); }
        else
          { deleteNearSelection(this, function (range) {
            var other = findPosH(doc, range.head, dir, unit, false);
            return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other}
          }); }
      }),

      findPosV: function(from, amount, unit, goalColumn) {
        var dir = 1, x = goalColumn;
        if (amount < 0) { dir = -1; amount = -amount; }
        var cur = clipPos(this.doc, from);
        for (var i = 0; i < amount; ++i) {
          var coords = cursorCoords(this, cur, "div");
          if (x == null) { x = coords.left; }
          else { coords.left = x; }
          cur = findPosV(this, coords, dir, unit);
          if (cur.hitSide) { break }
        }
        return cur
      },

      moveV: methodOp(function(dir, unit) {
        var this$1 = this;

        var doc = this.doc, goals = [];
        var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected();
        doc.extendSelectionsBy(function (range) {
          if (collapse)
            { return dir < 0 ? range.from() : range.to() }
          var headPos = cursorCoords(this$1, range.head, "div");
          if (range.goalColumn != null) { headPos.left = range.goalColumn; }
          goals.push(headPos.left);
          var pos = findPosV(this$1, headPos, dir, unit);
          if (unit == "page" && range == doc.sel.primary())
            { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); }
          return pos
        }, sel_move);
        if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
          { doc.sel.ranges[i].goalColumn = goals[i]; } }
      }),

      // Find the word at the given position (as returned by coordsChar).
      findWordAt: function(pos) {
        var doc = this.doc, line = getLine(doc, pos.line).text;
        var start = pos.ch, end = pos.ch;
        if (line) {
          var helper = this.getHelper(pos, "wordChars");
          if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; }
          var startChar = line.charAt(start);
          var check = isWordChar(startChar, helper)
            ? function (ch) { return isWordChar(ch, helper); }
            : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
            : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); };
          while (start > 0 && check(line.charAt(start - 1))) { --start; }
          while (end < line.length && check(line.charAt(end))) { ++end; }
        }
        return new Range(Pos(pos.line, start), Pos(pos.line, end))
      },

      toggleOverwrite: function(value) {
        if (value != null && value == this.state.overwrite) { return }
        if (this.state.overwrite = !this.state.overwrite)
          { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
        else
          { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); }

        signal(this, "overwriteToggle", this, this.state.overwrite);
      },
      hasFocus: function() { return this.display.input.getField() == activeElt() },
      isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },

      scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }),
      getScrollInfo: function() {
        var scroller = this.display.scroller;
        return {left: scroller.scrollLeft, top: scroller.scrollTop,
                height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
                width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
                clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
      },

      scrollIntoView: methodOp(function(range, margin) {
        if (range == null) {
          range = {from: this.doc.sel.primary().head, to: null};
          if (margin == null) { margin = this.options.cursorScrollMargin; }
        } else if (typeof range == "number") {
          range = {from: Pos(range, 0), to: null};
        } else if (range.from == null) {
          range = {from: range, to: null};
        }
        if (!range.to) { range.to = range.from; }
        range.margin = margin || 0;

        if (range.from.line != null) {
          scrollToRange(this, range);
        } else {
          scrollToCoordsRange(this, range.from, range.to, range.margin);
        }
      }),

      setSize: methodOp(function(width, height) {
        var this$1 = this;

        var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; };
        if (width != null) { this.display.wrapper.style.width = interpret(width); }
        if (height != null) { this.display.wrapper.style.height = interpret(height); }
        if (this.options.lineWrapping) { clearLineMeasurementCache(this); }
        var lineNo = this.display.viewFrom;
        this.doc.iter(lineNo, this.display.viewTo, function (line) {
          if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
            { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widget"); break } } }
          ++lineNo;
        });
        this.curOp.forceUpdate = true;
        signal(this, "refresh", this);
      }),

      operation: function(f){return runInOp(this, f)},
      startOperation: function(){return startOperation(this)},
      endOperation: function(){return endOperation(this)},

      refresh: methodOp(function() {
        var oldHeight = this.display.cachedTextHeight;
        regChange(this);
        this.curOp.forceUpdate = true;
        clearCaches(this);
        scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);
        updateGutterSpace(this.display);
        if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5 || this.options.lineWrapping)
          { estimateLineHeights(this); }
        signal(this, "refresh", this);
      }),

      swapDoc: methodOp(function(doc) {
        var old = this.doc;
        old.cm = null;
        // Cancel the current text selection if any (#5821)
        if (this.state.selectingText) { this.state.selectingText(); }
        attachDoc(this, doc);
        clearCaches(this);
        this.display.input.reset();
        scrollToCoords(this, doc.scrollLeft, doc.scrollTop);
        this.curOp.forceScroll = true;
        signalLater(this, "swapDoc", this, old);
        return old
      }),

      phrase: function(phraseText) {
        var phrases = this.options.phrases;
        return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText
      },

      getInputField: function(){return this.display.input.getField()},
      getWrapperElement: function(){return this.display.wrapper},
      getScrollerElement: function(){return this.display.scroller},
      getGutterElement: function(){return this.display.gutters}
    };
    eventMixin(CodeMirror);

    CodeMirror.registerHelper = function(type, name, value) {
      if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; }
      helpers[type][name] = value;
    };
    CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
      CodeMirror.registerHelper(type, name, value);
      helpers[type]._global.push({pred: predicate, val: value});
    };
  }

  // Used for horizontal relative motion. Dir is -1 or 1 (left or
  // right), unit can be "codepoint", "char", "column" (like char, but
  // doesn't cross line boundaries), "word" (across next word), or
  // "group" (to the start of next group of word or
  // non-word-non-whitespace chars). The visually param controls
  // whether, in right-to-left text, direction 1 means to move towards
  // the next index in the string, or towards the character to the right
  // of the current position. The resulting position will have a
  // hitSide=true property if it reached the end of the document.
  function findPosH(doc, pos, dir, unit, visually) {
    var oldPos = pos;
    var origDir = dir;
    var lineObj = getLine(doc, pos.line);
    var lineDir = visually && doc.direction == "rtl" ? -dir : dir;
    function findNextLine() {
      var l = pos.line + lineDir;
      if (l < doc.first || l >= doc.first + doc.size) { return false }
      pos = new Pos(l, pos.ch, pos.sticky);
      return lineObj = getLine(doc, l)
    }
    function moveOnce(boundToLine) {
      var next;
      if (unit == "codepoint") {
        var ch = lineObj.text.charCodeAt(pos.ch + (dir > 0 ? 0 : -1));
        if (isNaN(ch)) {
          next = null;
        } else {
          var astral = dir > 0 ? ch >= 0xD800 && ch < 0xDC00 : ch >= 0xDC00 && ch < 0xDFFF;
          next = new Pos(pos.line, Math.max(0, Math.min(lineObj.text.length, pos.ch + dir * (astral ? 2 : 1))), -dir);
        }
      } else if (visually) {
        next = moveVisually(doc.cm, lineObj, pos, dir);
      } else {
        next = moveLogically(lineObj, pos, dir);
      }
      if (next == null) {
        if (!boundToLine && findNextLine())
          { pos = endOfLine(visually, doc.cm, lineObj, pos.line, lineDir); }
        else
          { return false }
      } else {
        pos = next;
      }
      return true
    }

    if (unit == "char" || unit == "codepoint") {
      moveOnce();
    } else if (unit == "column") {
      moveOnce(true);
    } else if (unit == "word" || unit == "group") {
      var sawType = null, group = unit == "group";
      var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
      for (var first = true;; first = false) {
        if (dir < 0 && !moveOnce(!first)) { break }
        var cur = lineObj.text.charAt(pos.ch) || "\n";
        var type = isWordChar(cur, helper) ? "w"
          : group && cur == "\n" ? "n"
          : !group || /\s/.test(cur) ? null
          : "p";
        if (group && !first && !type) { type = "s"; }
        if (sawType && sawType != type) {
          if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";}
          break
        }

        if (type) { sawType = type; }
        if (dir > 0 && !moveOnce(!first)) { break }
      }
    }
    var result = skipAtomic(doc, pos, oldPos, origDir, true);
    if (equalCursorPos(oldPos, result)) { result.hitSide = true; }
    return result
  }

  // For relative vertical movement. Dir may be -1 or 1. Unit can be
  // "page" or "line". The resulting position will have a hitSide=true
  // property if it reached the end of the document.
  function findPosV(cm, pos, dir, unit) {
    var doc = cm.doc, x = pos.left, y;
    if (unit == "page") {
      var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
      var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3);
      y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount;

    } else if (unit == "line") {
      y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
    }
    var target;
    for (;;) {
      target = coordsChar(cm, x, y);
      if (!target.outside) { break }
      if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
      y += dir * 5;
    }
    return target
  }

  // CONTENTEDITABLE INPUT STYLE

  var ContentEditableInput = function(cm) {
    this.cm = cm;
    this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
    this.polling = new Delayed();
    this.composing = null;
    this.gracePeriod = false;
    this.readDOMTimeout = null;
  };

  ContentEditableInput.prototype.init = function (display) {
      var this$1 = this;

    var input = this, cm = input.cm;
    var div = input.div = display.lineDiv;
    disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize);

    function belongsToInput(e) {
      for (var t = e.target; t; t = t.parentNode) {
        if (t == div) { return true }
        if (/\bCodeMirror-(?:line)?widget\b/.test(t.className)) { break }
      }
      return false
    }

    on(div, "paste", function (e) {
      if (!belongsToInput(e) || signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
      // IE doesn't fire input events, so we schedule a read for the pasted content in this way
      if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }
    });

    on(div, "compositionstart", function (e) {
      this$1.composing = {data: e.data, done: false};
    });
    on(div, "compositionupdate", function (e) {
      if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; }
    });
    on(div, "compositionend", function (e) {
      if (this$1.composing) {
        if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); }
        this$1.composing.done = true;
      }
    });

    on(div, "touchstart", function () { return input.forceCompositionEnd(); });

    on(div, "input", function () {
      if (!this$1.composing) { this$1.readFromDOMSoon(); }
    });

    function onCopyCut(e) {
      if (!belongsToInput(e) || signalDOMEvent(cm, e)) { return }
      if (cm.somethingSelected()) {
        setLastCopied({lineWise: false, text: cm.getSelections()});
        if (e.type == "cut") { cm.replaceSelection("", null, "cut"); }
      } else if (!cm.options.lineWiseCopyCut) {
        return
      } else {
        var ranges = copyableRanges(cm);
        setLastCopied({lineWise: true, text: ranges.text});
        if (e.type == "cut") {
          cm.operation(function () {
            cm.setSelections(ranges.ranges, 0, sel_dontScroll);
            cm.replaceSelection("", null, "cut");
          });
        }
      }
      if (e.clipboardData) {
        e.clipboardData.clearData();
        var content = lastCopied.text.join("\n");
        // iOS exposes the clipboard API, but seems to discard content inserted into it
        e.clipboardData.setData("Text", content);
        if (e.clipboardData.getData("Text") == content) {
          e.preventDefault();
          return
        }
      }
      // Old-fashioned briefly-focus-a-textarea hack
      var kludge = hiddenTextarea(), te = kludge.firstChild;
      cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
      te.value = lastCopied.text.join("\n");
      var hadFocus = document.activeElement;
      selectInput(te);
      setTimeout(function () {
        cm.display.lineSpace.removeChild(kludge);
        hadFocus.focus();
        if (hadFocus == div) { input.showPrimarySelection(); }
      }, 50);
    }
    on(div, "copy", onCopyCut);
    on(div, "cut", onCopyCut);
  };

  ContentEditableInput.prototype.screenReaderLabelChanged = function (label) {
    // Label for screenreaders, accessibility
    if(label) {
      this.div.setAttribute('aria-label', label);
    } else {
      this.div.removeAttribute('aria-label');
    }
  };

  ContentEditableInput.prototype.prepareSelection = function () {
    var result = prepareSelection(this.cm, false);
    result.focus = document.activeElement == this.div;
    return result
  };

  ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
    if (!info || !this.cm.display.view.length) { return }
    if (info.focus || takeFocus) { this.showPrimarySelection(); }
    this.showMultipleSelections(info);
  };

  ContentEditableInput.prototype.getSelection = function () {
    return this.cm.display.wrapper.ownerDocument.getSelection()
  };

  ContentEditableInput.prototype.showPrimarySelection = function () {
    var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();
    var from = prim.from(), to = prim.to();

    if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
      sel.removeAllRanges();
      return
    }

    var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
    var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset);
    if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
        cmp(minPos(curAnchor, curFocus), from) == 0 &&
        cmp(maxPos(curAnchor, curFocus), to) == 0)
      { return }

    var view = cm.display.view;
    var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||
        {node: view[0].measure.map[2], offset: 0};
    var end = to.line < cm.display.viewTo && posToDOM(cm, to);
    if (!end) {
      var measure = view[view.length - 1].measure;
      var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
      end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]};
    }

    if (!start || !end) {
      sel.removeAllRanges();
      return
    }

    var old = sel.rangeCount && sel.getRangeAt(0), rng;
    try { rng = range(start.node, start.offset, end.offset, end.node); }
    catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
    if (rng) {
      if (!gecko && cm.state.focused) {
        sel.collapse(start.node, start.offset);
        if (!rng.collapsed) {
          sel.removeAllRanges();
          sel.addRange(rng);
        }
      } else {
        sel.removeAllRanges();
        sel.addRange(rng);
      }
      if (old && sel.anchorNode == null) { sel.addRange(old); }
      else if (gecko) { this.startGracePeriod(); }
    }
    this.rememberSelection();
  };

  ContentEditableInput.prototype.startGracePeriod = function () {
      var this$1 = this;

    clearTimeout(this.gracePeriod);
    this.gracePeriod = setTimeout(function () {
      this$1.gracePeriod = false;
      if (this$1.selectionChanged())
        { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); }
    }, 20);
  };

  ContentEditableInput.prototype.showMultipleSelections = function (info) {
    removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
    removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
  };

  ContentEditableInput.prototype.rememberSelection = function () {
    var sel = this.getSelection();
    this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
    this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
  };

  ContentEditableInput.prototype.selectionInEditor = function () {
    var sel = this.getSelection();
    if (!sel.rangeCount) { return false }
    var node = sel.getRangeAt(0).commonAncestorContainer;
    return contains(this.div, node)
  };

  ContentEditableInput.prototype.focus = function () {
    if (this.cm.options.readOnly != "nocursor") {
      if (!this.selectionInEditor() || document.activeElement != this.div)
        { this.showSelection(this.prepareSelection(), true); }
      this.div.focus();
    }
  };
  ContentEditableInput.prototype.blur = function () { this.div.blur(); };
  ContentEditableInput.prototype.getField = function () { return this.div };

  ContentEditableInput.prototype.supportsTouch = function () { return true };

  ContentEditableInput.prototype.receivedFocus = function () {
    var input = this;
    if (this.selectionInEditor())
      { this.pollSelection(); }
    else
      { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); }

    function poll() {
      if (input.cm.state.focused) {
        input.pollSelection();
        input.polling.set(input.cm.options.pollInterval, poll);
      }
    }
    this.polling.set(this.cm.options.pollInterval, poll);
  };

  ContentEditableInput.prototype.selectionChanged = function () {
    var sel = this.getSelection();
    return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
      sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
  };

  ContentEditableInput.prototype.pollSelection = function () {
    if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
    var sel = this.getSelection(), cm = this.cm;
    // On Android Chrome (version 56, at least), backspacing into an
    // uneditable block element will put the cursor in that element,
    // and then, because it's not editable, hide the virtual keyboard.
    // Because Android doesn't allow us to actually detect backspace
    // presses in a sane way, this code checks for when that happens
    // and simulates a backspace press in this case.
    if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) {
      this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs});
      this.blur();
      this.focus();
      return
    }
    if (this.composing) { return }
    this.rememberSelection();
    var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
    var head = domToPos(cm, sel.focusNode, sel.focusOffset);
    if (anchor && head) { runInOp(cm, function () {
      setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
      if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; }
    }); }
  };

  ContentEditableInput.prototype.pollContent = function () {
    if (this.readDOMTimeout != null) {
      clearTimeout(this.readDOMTimeout);
      this.readDOMTimeout = null;
    }

    var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
    var from = sel.from(), to = sel.to();
    if (from.ch == 0 && from.line > cm.firstLine())
      { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); }
    if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
      { to = Pos(to.line + 1, 0); }
    if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }

    var fromIndex, fromLine, fromNode;
    if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
      fromLine = lineNo(display.view[0].line);
      fromNode = display.view[0].node;
    } else {
      fromLine = lineNo(display.view[fromIndex].line);
      fromNode = display.view[fromIndex - 1].node.nextSibling;
    }
    var toIndex = findViewIndex(cm, to.line);
    var toLine, toNode;
    if (toIndex == display.view.length - 1) {
      toLine = display.viewTo - 1;
      toNode = display.lineDiv.lastChild;
    } else {
      toLine = lineNo(display.view[toIndex + 1].line) - 1;
      toNode = display.view[toIndex + 1].node.previousSibling;
    }

    if (!fromNode) { return false }
    var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
    var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
    while (newText.length > 1 && oldText.length > 1) {
      if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
      else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
      else { break }
    }

    var cutFront = 0, cutEnd = 0;
    var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
    while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
      { ++cutFront; }
    var newBot = lst(newText), oldBot = lst(oldText);
    var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
                             oldBot.length - (oldText.length == 1 ? cutFront : 0));
    while (cutEnd < maxCutEnd &&
           newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
      { ++cutEnd; }
    // Try to move start of change to start of selection if ambiguous
    if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
      while (cutFront && cutFront > from.ch &&
             newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
        cutFront--;
        cutEnd++;
      }
    }

    newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "");
    newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "");

    var chFrom = Pos(fromLine, cutFront);
    var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
    if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
      replaceRange(cm.doc, newText, chFrom, chTo, "+input");
      return true
    }
  };

  ContentEditableInput.prototype.ensurePolled = function () {
    this.forceCompositionEnd();
  };
  ContentEditableInput.prototype.reset = function () {
    this.forceCompositionEnd();
  };
  ContentEditableInput.prototype.forceCompositionEnd = function () {
    if (!this.composing) { return }
    clearTimeout(this.readDOMTimeout);
    this.composing = null;
    this.updateFromDOM();
    this.div.blur();
    this.div.focus();
  };
  ContentEditableInput.prototype.readFromDOMSoon = function () {
      var this$1 = this;

    if (this.readDOMTimeout != null) { return }
    this.readDOMTimeout = setTimeout(function () {
      this$1.readDOMTimeout = null;
      if (this$1.composing) {
        if (this$1.composing.done) { this$1.composing = null; }
        else { return }
      }
      this$1.updateFromDOM();
    }, 80);
  };

  ContentEditableInput.prototype.updateFromDOM = function () {
      var this$1 = this;

    if (this.cm.isReadOnly() || !this.pollContent())
      { runInOp(this.cm, function () { return regChange(this$1.cm); }); }
  };

  ContentEditableInput.prototype.setUneditable = function (node) {
    node.contentEditable = "false";
  };

  ContentEditableInput.prototype.onKeyPress = function (e) {
    if (e.charCode == 0 || this.composing) { return }
    e.preventDefault();
    if (!this.cm.isReadOnly())
      { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }
  };

  ContentEditableInput.prototype.readOnlyChanged = function (val) {
    this.div.contentEditable = String(val != "nocursor");
  };

  ContentEditableInput.prototype.onContextMenu = function () {};
  ContentEditableInput.prototype.resetPosition = function () {};

  ContentEditableInput.prototype.needsContentAttribute = true;

  function posToDOM(cm, pos) {
    var view = findViewForLine(cm, pos.line);
    if (!view || view.hidden) { return null }
    var line = getLine(cm.doc, pos.line);
    var info = mapFromLineView(view, line, pos.line);

    var order = getOrder(line, cm.doc.direction), side = "left";
    if (order) {
      var partPos = getBidiPartAt(order, pos.ch);
      side = partPos % 2 ? "right" : "left";
    }
    var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
    result.offset = result.collapse == "right" ? result.end : result.start;
    return result
  }

  function isInGutter(node) {
    for (var scan = node; scan; scan = scan.parentNode)
      { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
    return false
  }

  function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }

  function domTextBetween(cm, from, to, fromLine, toLine) {
    var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false;
    function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
    function close() {
      if (closing) {
        text += lineSep;
        if (extraLinebreak) { text += lineSep; }
        closing = extraLinebreak = false;
      }
    }
    function addText(str) {
      if (str) {
        close();
        text += str;
      }
    }
    function walk(node) {
      if (node.nodeType == 1) {
        var cmText = node.getAttribute("cm-text");
        if (cmText) {
          addText(cmText);
          return
        }
        var markerID = node.getAttribute("cm-marker"), range;
        if (markerID) {
          var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
          if (found.length && (range = found[0].find(0)))
            { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)); }
          return
        }
        if (node.getAttribute("contenteditable") == "false") { return }
        var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName);
        if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return }

        if (isBlock) { close(); }
        for (var i = 0; i < node.childNodes.length; i++)
          { walk(node.childNodes[i]); }

        if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; }
        if (isBlock) { closing = true; }
      } else if (node.nodeType == 3) {
        addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " "));
      }
    }
    for (;;) {
      walk(from);
      if (from == to) { break }
      from = from.nextSibling;
      extraLinebreak = false;
    }
    return text
  }

  function domToPos(cm, node, offset) {
    var lineNode;
    if (node == cm.display.lineDiv) {
      lineNode = cm.display.lineDiv.childNodes[offset];
      if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
      node = null; offset = 0;
    } else {
      for (lineNode = node;; lineNode = lineNode.parentNode) {
        if (!lineNode || lineNode == cm.display.lineDiv) { return null }
        if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
      }
    }
    for (var i = 0; i < cm.display.view.length; i++) {
      var lineView = cm.display.view[i];
      if (lineView.node == lineNode)
        { return locateNodeInLineView(lineView, node, offset) }
    }
  }

  function locateNodeInLineView(lineView, node, offset) {
    var wrapper = lineView.text.firstChild, bad = false;
    if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
    if (node == wrapper) {
      bad = true;
      node = wrapper.childNodes[offset];
      offset = 0;
      if (!node) {
        var line = lineView.rest ? lst(lineView.rest) : lineView.line;
        return badPos(Pos(lineNo(line), line.text.length), bad)
      }
    }

    var textNode = node.nodeType == 3 ? node : null, topNode = node;
    if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
      textNode = node.firstChild;
      if (offset) { offset = textNode.nodeValue.length; }
    }
    while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; }
    var measure = lineView.measure, maps = measure.maps;

    function find(textNode, topNode, offset) {
      for (var i = -1; i < (maps ? maps.length : 0); i++) {
        var map = i < 0 ? measure.map : maps[i];
        for (var j = 0; j < map.length; j += 3) {
          var curNode = map[j + 2];
          if (curNode == textNode || curNode == topNode) {
            var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
            var ch = map[j] + offset;
            if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)]; }
            return Pos(line, ch)
          }
        }
      }
    }
    var found = find(textNode, topNode, offset);
    if (found) { return badPos(found, bad) }

    // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
    for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
      found = find(after, after.firstChild, 0);
      if (found)
        { return badPos(Pos(found.line, found.ch - dist), bad) }
      else
        { dist += after.textContent.length; }
    }
    for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
      found = find(before, before.firstChild, -1);
      if (found)
        { return badPos(Pos(found.line, found.ch + dist$1), bad) }
      else
        { dist$1 += before.textContent.length; }
    }
  }

  // TEXTAREA INPUT STYLE

  var TextareaInput = function(cm) {
    this.cm = cm;
    // See input.poll and input.reset
    this.prevInput = "";

    // Flag that indicates whether we expect input to appear real soon
    // now (after some event like 'keypress' or 'input') and are
    // polling intensively.
    this.pollingFast = false;
    // Self-resetting timeout for the poller
    this.polling = new Delayed();
    // Used to work around IE issue with selection being forgotten when focus moves away from textarea
    this.hasSelection = false;
    this.composing = null;
  };

  TextareaInput.prototype.init = function (display) {
      var this$1 = this;

    var input = this, cm = this.cm;
    this.createField(display);
    var te = this.textarea;

    display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild);

    // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
    if (ios) { te.style.width = "0px"; }

    on(te, "input", function () {
      if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; }
      input.poll();
    });

    on(te, "paste", function (e) {
      if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }

      cm.state.pasteIncoming = +new Date;
      input.fastPoll();
    });

    function prepareCopyCut(e) {
      if (signalDOMEvent(cm, e)) { return }
      if (cm.somethingSelected()) {
        setLastCopied({lineWise: false, text: cm.getSelections()});
      } else if (!cm.options.lineWiseCopyCut) {
        return
      } else {
        var ranges = copyableRanges(cm);
        setLastCopied({lineWise: true, text: ranges.text});
        if (e.type == "cut") {
          cm.setSelections(ranges.ranges, null, sel_dontScroll);
        } else {
          input.prevInput = "";
          te.value = ranges.text.join("\n");
          selectInput(te);
        }
      }
      if (e.type == "cut") { cm.state.cutIncoming = +new Date; }
    }
    on(te, "cut", prepareCopyCut);
    on(te, "copy", prepareCopyCut);

    on(display.scroller, "paste", function (e) {
      if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
      if (!te.dispatchEvent) {
        cm.state.pasteIncoming = +new Date;
        input.focus();
        return
      }

      // Pass the `paste` event to the textarea so it's handled by its event listener.
      var event = new Event("paste");
      event.clipboardData = e.clipboardData;
      te.dispatchEvent(event);
    });

    // Prevent normal selection in the editor (we handle our own)
    on(display.lineSpace, "selectstart", function (e) {
      if (!eventInWidget(display, e)) { e_preventDefault(e); }
    });

    on(te, "compositionstart", function () {
      var start = cm.getCursor("from");
      if (input.composing) { input.composing.range.clear(); }
      input.composing = {
        start: start,
        range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
      };
    });
    on(te, "compositionend", function () {
      if (input.composing) {
        input.poll();
        input.composing.range.clear();
        input.composing = null;
      }
    });
  };

  TextareaInput.prototype.createField = function (_display) {
    // Wraps and hides input textarea
    this.wrapper = hiddenTextarea();
    // The semihidden textarea that is focused when the editor is
    // focused, and receives input.
    this.textarea = this.wrapper.firstChild;
  };

  TextareaInput.prototype.screenReaderLabelChanged = function (label) {
    // Label for screenreaders, accessibility
    if(label) {
      this.textarea.setAttribute('aria-label', label);
    } else {
      this.textarea.removeAttribute('aria-label');
    }
  };

  TextareaInput.prototype.prepareSelection = function () {
    // Redraw the selection and/or cursor
    var cm = this.cm, display = cm.display, doc = cm.doc;
    var result = prepareSelection(cm);

    // Move the hidden textarea near the cursor to prevent scrolling artifacts
    if (cm.options.moveInputWithCursor) {
      var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
      var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
      result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
                                          headPos.top + lineOff.top - wrapOff.top));
      result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
                                           headPos.left + lineOff.left - wrapOff.left));
    }

    return result
  };

  TextareaInput.prototype.showSelection = function (drawn) {
    var cm = this.cm, display = cm.display;
    removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
    removeChildrenAndAdd(display.selectionDiv, drawn.selection);
    if (drawn.teTop != null) {
      this.wrapper.style.top = drawn.teTop + "px";
      this.wrapper.style.left = drawn.teLeft + "px";
    }
  };

  // Reset the input to correspond to the selection (or to be empty,
  // when not typing and nothing is selected)
  TextareaInput.prototype.reset = function (typing) {
    if (this.contextMenuPending || this.composing) { return }
    var cm = this.cm;
    if (cm.somethingSelected()) {
      this.prevInput = "";
      var content = cm.getSelection();
      this.textarea.value = content;
      if (cm.state.focused) { selectInput(this.textarea); }
      if (ie && ie_version >= 9) { this.hasSelection = content; }
    } else if (!typing) {
      this.prevInput = this.textarea.value = "";
      if (ie && ie_version >= 9) { this.hasSelection = null; }
    }
  };

  TextareaInput.prototype.getField = function () { return this.textarea };

  TextareaInput.prototype.supportsTouch = function () { return false };

  TextareaInput.prototype.focus = function () {
    if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
      try { this.textarea.focus(); }
      catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
    }
  };

  TextareaInput.prototype.blur = function () { this.textarea.blur(); };

  TextareaInput.prototype.resetPosition = function () {
    this.wrapper.style.top = this.wrapper.style.left = 0;
  };

  TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); };

  // Poll for input changes, using the normal rate of polling. This
  // runs as long as the editor is focused.
  TextareaInput.prototype.slowPoll = function () {
      var this$1 = this;

    if (this.pollingFast) { return }
    this.polling.set(this.cm.options.pollInterval, function () {
      this$1.poll();
      if (this$1.cm.state.focused) { this$1.slowPoll(); }
    });
  };

  // When an event has just come in that is likely to add or change
  // something in the input textarea, we poll faster, to ensure that
  // the change appears on the screen quickly.
  TextareaInput.prototype.fastPoll = function () {
    var missed = false, input = this;
    input.pollingFast = true;
    function p() {
      var changed = input.poll();
      if (!changed && !missed) {missed = true; input.polling.set(60, p);}
      else {input.pollingFast = false; input.slowPoll();}
    }
    input.polling.set(20, p);
  };

  // Read input from the textarea, and update the document to match.
  // When something is selected, it is present in the textarea, and
  // selected (unless it is huge, in which case a placeholder is
  // used). When nothing is selected, the cursor sits after previously
  // seen text (can be empty), which is stored in prevInput (we must
  // not reset the textarea when typing, because that breaks IME).
  TextareaInput.prototype.poll = function () {
      var this$1 = this;

    var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
    // Since this is called a *lot*, try to bail out as cheaply as
    // possible when it is clear that nothing happened. hasSelection
    // will be the case when there is a lot of text in the textarea,
    // in which case reading its value would be expensive.
    if (this.contextMenuPending || !cm.state.focused ||
        (hasSelection(input) && !prevInput && !this.composing) ||
        cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
      { return false }

    var text = input.value;
    // If nothing changed, bail.
    if (text == prevInput && !cm.somethingSelected()) { return false }
    // Work around nonsensical selection resetting in IE9/10, and
    // inexplicable appearance of private area unicode characters on
    // some key combos in Mac (#2689).
    if (ie && ie_version >= 9 && this.hasSelection === text ||
        mac && /[\uf700-\uf7ff]/.test(text)) {
      cm.display.input.reset();
      return false
    }

    if (cm.doc.sel == cm.display.selForContextMenu) {
      var first = text.charCodeAt(0);
      if (first == 0x200b && !prevInput) { prevInput = "\u200b"; }
      if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
    }
    // Find the part of the input that is actually new
    var same = 0, l = Math.min(prevInput.length, text.length);
    while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; }

    runInOp(cm, function () {
      applyTextInput(cm, text.slice(same), prevInput.length - same,
                     null, this$1.composing ? "*compose" : null);

      // Don't leave long text in the textarea, since it makes further polling slow
      if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; }
      else { this$1.prevInput = text; }

      if (this$1.composing) {
        this$1.composing.range.clear();
        this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
                                           {className: "CodeMirror-composing"});
      }
    });
    return true
  };

  TextareaInput.prototype.ensurePolled = function () {
    if (this.pollingFast && this.poll()) { this.pollingFast = false; }
  };

  TextareaInput.prototype.onKeyPress = function () {
    if (ie && ie_version >= 9) { this.hasSelection = null; }
    this.fastPoll();
  };

  TextareaInput.prototype.onContextMenu = function (e) {
    var input = this, cm = input.cm, display = cm.display, te = input.textarea;
    if (input.contextMenuPending) { input.contextMenuPending(); }
    var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
    if (!pos || presto) { return } // Opera is difficult.

    // Reset the current text selection only if the click is done outside of the selection
    // and 'resetSelectionOnContextMenu' option is true.
    var reset = cm.options.resetSelectionOnContextMenu;
    if (reset && cm.doc.sel.contains(pos) == -1)
      { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); }

    var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;
    var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect();
    input.wrapper.style.cssText = "position: static";
    te.style.cssText = "position: absolute; width: 30px; height: 30px;\n      top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n      z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n      outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
    var oldScrollY;
    if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712)
    display.input.focus();
    if (webkit) { window.scrollTo(null, oldScrollY); }
    display.input.reset();
    // Adds "Select all" to context menu in FF
    if (!cm.somethingSelected()) { te.value = input.prevInput = " "; }
    input.contextMenuPending = rehide;
    display.selForContextMenu = cm.doc.sel;
    clearTimeout(display.detectingSelectAll);

    // Select-all will be greyed out if there's nothing to select, so
    // this adds a zero-width space so that we can later check whether
    // it got selected.
    function prepareSelectAllHack() {
      if (te.selectionStart != null) {
        var selected = cm.somethingSelected();
        var extval = "\u200b" + (selected ? te.value : "");
        te.value = "\u21da"; // Used to catch context-menu undo
        te.value = extval;
        input.prevInput = selected ? "" : "\u200b";
        te.selectionStart = 1; te.selectionEnd = extval.length;
        // Re-set this, in case some other handler touched the
        // selection in the meantime.
        display.selForContextMenu = cm.doc.sel;
      }
    }
    function rehide() {
      if (input.contextMenuPending != rehide) { return }
      input.contextMenuPending = false;
      input.wrapper.style.cssText = oldWrapperCSS;
      te.style.cssText = oldCSS;
      if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); }

      // Try to detect the user choosing select-all
      if (te.selectionStart != null) {
        if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); }
        var i = 0, poll = function () {
          if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
              te.selectionEnd > 0 && input.prevInput == "\u200b") {
            operation(cm, selectAll)(cm);
          } else if (i++ < 10) {
            display.detectingSelectAll = setTimeout(poll, 500);
          } else {
            display.selForContextMenu = null;
            display.input.reset();
          }
        };
        display.detectingSelectAll = setTimeout(poll, 200);
      }
    }

    if (ie && ie_version >= 9) { prepareSelectAllHack(); }
    if (captureRightClick) {
      e_stop(e);
      var mouseup = function () {
        off(window, "mouseup", mouseup);
        setTimeout(rehide, 20);
      };
      on(window, "mouseup", mouseup);
    } else {
      setTimeout(rehide, 50);
    }
  };

  TextareaInput.prototype.readOnlyChanged = function (val) {
    if (!val) { this.reset(); }
    this.textarea.disabled = val == "nocursor";
    this.textarea.readOnly = !!val;
  };

  TextareaInput.prototype.setUneditable = function () {};

  TextareaInput.prototype.needsContentAttribute = false;

  function fromTextArea(textarea, options) {
    options = options ? copyObj(options) : {};
    options.value = textarea.value;
    if (!options.tabindex && textarea.tabIndex)
      { options.tabindex = textarea.tabIndex; }
    if (!options.placeholder && textarea.placeholder)
      { options.placeholder = textarea.placeholder; }
    // Set autofocus to true if this textarea is focused, or if it has
    // autofocus and no other element is focused.
    if (options.autofocus == null) {
      var hasFocus = activeElt();
      options.autofocus = hasFocus == textarea ||
        textarea.getAttribute("autofocus") != null && hasFocus == document.body;
    }

    function save() {textarea.value = cm.getValue();}

    var realSubmit;
    if (textarea.form) {
      on(textarea.form, "submit", save);
      // Deplorable hack to make the submit method do the right thing.
      if (!options.leaveSubmitMethodAlone) {
        var form = textarea.form;
        realSubmit = form.submit;
        try {
          var wrappedSubmit = form.submit = function () {
            save();
            form.submit = realSubmit;
            form.submit();
            form.submit = wrappedSubmit;
          };
        } catch(e) {}
      }
    }

    options.finishInit = function (cm) {
      cm.save = save;
      cm.getTextArea = function () { return textarea; };
      cm.toTextArea = function () {
        cm.toTextArea = isNaN; // Prevent this from being ran twice
        save();
        textarea.parentNode.removeChild(cm.getWrapperElement());
        textarea.style.display = "";
        if (textarea.form) {
          off(textarea.form, "submit", save);
          if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function")
            { textarea.form.submit = realSubmit; }
        }
      };
    };

    textarea.style.display = "none";
    var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
      options);
    return cm
  }

  function addLegacyProps(CodeMirror) {
    CodeMirror.off = off;
    CodeMirror.on = on;
    CodeMirror.wheelEventPixels = wheelEventPixels;
    CodeMirror.Doc = Doc;
    CodeMirror.splitLines = splitLinesAuto;
    CodeMirror.countColumn = countColumn;
    CodeMirror.findColumn = findColumn;
    CodeMirror.isWordChar = isWordCharBasic;
    CodeMirror.Pass = Pass;
    CodeMirror.signal = signal;
    CodeMirror.Line = Line;
    CodeMirror.changeEnd = changeEnd;
    CodeMirror.scrollbarModel = scrollbarModel;
    CodeMirror.Pos = Pos;
    CodeMirror.cmpPos = cmp;
    CodeMirror.modes = modes;
    CodeMirror.mimeModes = mimeModes;
    CodeMirror.resolveMode = resolveMode;
    CodeMirror.getMode = getMode;
    CodeMirror.modeExtensions = modeExtensions;
    CodeMirror.extendMode = extendMode;
    CodeMirror.copyState = copyState;
    CodeMirror.startState = startState;
    CodeMirror.innerMode = innerMode;
    CodeMirror.commands = commands;
    CodeMirror.keyMap = keyMap;
    CodeMirror.keyName = keyName;
    CodeMirror.isModifierKey = isModifierKey;
    CodeMirror.lookupKey = lookupKey;
    CodeMirror.normalizeKeyMap = normalizeKeyMap;
    CodeMirror.StringStream = StringStream;
    CodeMirror.SharedTextMarker = SharedTextMarker;
    CodeMirror.TextMarker = TextMarker;
    CodeMirror.LineWidget = LineWidget;
    CodeMirror.e_preventDefault = e_preventDefault;
    CodeMirror.e_stopPropagation = e_stopPropagation;
    CodeMirror.e_stop = e_stop;
    CodeMirror.addClass = addClass;
    CodeMirror.contains = contains;
    CodeMirror.rmClass = rmClass;
    CodeMirror.keyNames = keyNames;
  }

  // EDITOR CONSTRUCTOR

  defineOptions(CodeMirror);

  addEditorMethods(CodeMirror);

  // Set up methods on CodeMirror's prototype to redirect to the editor's document.
  var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
  for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
    { CodeMirror.prototype[prop] = (function(method) {
      return function() {return method.apply(this.doc, arguments)}
    })(Doc.prototype[prop]); } }

  eventMixin(Doc);
  CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};

  // Extra arguments are stored as the mode's dependencies, which is
  // used by (legacy) mechanisms like loadmode.js to automatically
  // load a mode. (Preferred mechanism is the require/define calls.)
  CodeMirror.defineMode = function(name/*, mode, …*/) {
    if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; }
    defineMode.apply(this, arguments);
  };

  CodeMirror.defineMIME = defineMIME;

  // Minimal default mode.
  CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });
  CodeMirror.defineMIME("text/plain", "null");

  // EXTENSIONS

  CodeMirror.defineExtension = function (name, func) {
    CodeMirror.prototype[name] = func;
  };
  CodeMirror.defineDocExtension = function (name, func) {
    Doc.prototype[name] = func;
  };

  CodeMirror.fromTextArea = fromTextArea;

  addLegacyProps(CodeMirror);

  CodeMirror.version = "5.59.2";

  return CodeMirror;

})));


/***/ }),

/***/ "./node_modules/codemirror/mode/clike/clike.js":
/*!*****************************************************!*\
  !*** ./node_modules/codemirror/mode/clike/clike.js ***!
  \*****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE

(function(mod) {
  if (true) // CommonJS
    mod(__webpack_require__(/*! ../../lib/codemirror */ "./node_modules/codemirror/lib/codemirror.js"));
  else {}
})(function(CodeMirror) {
"use strict";

function Context(indented, column, type, info, align, prev) {
  this.indented = indented;
  this.column = column;
  this.type = type;
  this.info = info;
  this.align = align;
  this.prev = prev;
}
function pushContext(state, col, type, info) {
  var indent = state.indented;
  if (state.context && state.context.type == "statement" && type != "statement")
    indent = state.context.indented;
  return state.context = new Context(indent, col, type, info, null, state.context);
}
function popContext(state) {
  var t = state.context.type;
  if (t == ")" || t == "]" || t == "}")
    state.indented = state.context.indented;
  return state.context = state.context.prev;
}

function typeBefore(stream, state, pos) {
  if (state.prevToken == "variable" || state.prevToken == "type") return true;
  if (/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(stream.string.slice(0, pos))) return true;
  if (state.typeAtEndOfLine && stream.column() == stream.indentation()) return true;
}

function isTopScope(context) {
  for (;;) {
    if (!context || context.type == "top") return true;
    if (context.type == "}" && context.prev.info != "namespace") return false;
    context = context.prev;
  }
}

CodeMirror.defineMode("clike", function(config, parserConfig) {
  var indentUnit = config.indentUnit,
      statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
      dontAlignCalls = parserConfig.dontAlignCalls,
      keywords = parserConfig.keywords || {},
      types = parserConfig.types || {},
      builtin = parserConfig.builtin || {},
      blockKeywords = parserConfig.blockKeywords || {},
      defKeywords = parserConfig.defKeywords || {},
      atoms = parserConfig.atoms || {},
      hooks = parserConfig.hooks || {},
      multiLineStrings = parserConfig.multiLineStrings,
      indentStatements = parserConfig.indentStatements !== false,
      indentSwitch = parserConfig.indentSwitch !== false,
      namespaceSeparator = parserConfig.namespaceSeparator,
      isPunctuationChar = parserConfig.isPunctuationChar || /[\[\]{}\(\),;\:\.]/,
      numberStart = parserConfig.numberStart || /[\d\.]/,
      number = parserConfig.number || /^(?:0x[a-f\d]+|0b[01]+|(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)(u|ll?|l|f)?/i,
      isOperatorChar = parserConfig.isOperatorChar || /[+\-*&%=<>!?|\/]/,
      isIdentifierChar = parserConfig.isIdentifierChar || /[\w\$_\xa1-\uffff]/,
      // An optional function that takes a {string} token and returns true if it
      // should be treated as a builtin.
      isReservedIdentifier = parserConfig.isReservedIdentifier || false;

  var curPunc, isDefKeyword;

  function tokenBase(stream, state) {
    var ch = stream.next();
    if (hooks[ch]) {
      var result = hooks[ch](stream, state);
      if (result !== false) return result;
    }
    if (ch == '"' || ch == "'") {
      state.tokenize = tokenString(ch);
      return state.tokenize(stream, state);
    }
    if (numberStart.test(ch)) {
      stream.backUp(1)
      if (stream.match(number)) return "number"
      stream.next()
    }
    if (isPunctuationChar.test(ch)) {
      curPunc = ch;
      return null;
    }
    if (ch == "/") {
      if (stream.eat("*")) {
        state.tokenize = tokenComment;
        return tokenComment(stream, state);
      }
      if (stream.eat("/")) {
        stream.skipToEnd();
        return "comment";
      }
    }
    if (isOperatorChar.test(ch)) {
      while (!stream.match(/^\/[\/*]/, false) && stream.eat(isOperatorChar)) {}
      return "operator";
    }
    stream.eatWhile(isIdentifierChar);
    if (namespaceSeparator) while (stream.match(namespaceSeparator))
      stream.eatWhile(isIdentifierChar);

    var cur = stream.current();
    if (contains(keywords, cur)) {
      if (contains(blockKeywords, cur)) curPunc = "newstatement";
      if (contains(defKeywords, cur)) isDefKeyword = true;
      return "keyword";
    }
    if (contains(types, cur)) return "type";
    if (contains(builtin, cur)
        || (isReservedIdentifier && isReservedIdentifier(cur))) {
      if (contains(blockKeywords, cur)) curPunc = "newstatement";
      return "builtin";
    }
    if (contains(atoms, cur)) return "atom";
    return "variable";
  }

  function tokenString(quote) {
    return function(stream, state) {
      var escaped = false, next, end = false;
      while ((next = stream.next()) != null) {
        if (next == quote && !escaped) {end = true; break;}
        escaped = !escaped && next == "\\";
      }
      if (end || !(escaped || multiLineStrings))
        state.tokenize = null;
      return "string";
    };
  }

  function tokenComment(stream, state) {
    var maybeEnd = false, ch;
    while (ch = stream.next()) {
      if (ch == "/" && maybeEnd) {
        state.tokenize = null;
        break;
      }
      maybeEnd = (ch == "*");
    }
    return "comment";
  }

  function maybeEOL(stream, state) {
    if (parserConfig.typeFirstDefinitions && stream.eol() && isTopScope(state.context))
      state.typeAtEndOfLine = typeBefore(stream, state, stream.pos)
  }

  // Interface

  return {
    startState: function(basecolumn) {
      return {
        tokenize: null,
        context: new Context((basecolumn || 0) - indentUnit, 0, "top", null, false),
        indented: 0,
        startOfLine: true,
        prevToken: null
      };
    },

    token: function(stream, state) {
      var ctx = state.context;
      if (stream.sol()) {
        if (ctx.align == null) ctx.align = false;
        state.indented = stream.indentation();
        state.startOfLine = true;
      }
      if (stream.eatSpace()) { maybeEOL(stream, state); return null; }
      curPunc = isDefKeyword = null;
      var style = (state.tokenize || tokenBase)(stream, state);
      if (style == "comment" || style == "meta") return style;
      if (ctx.align == null) ctx.align = true;

      if (curPunc == ";" || curPunc == ":" || (curPunc == "," && stream.match(/^\s*(?:\/\/.*)?$/, false)))
        while (state.context.type == "statement") popContext(state);
      else if (curPunc == "{") pushContext(state, stream.column(), "}");
      else if (curPunc == "[") pushContext(state, stream.column(), "]");
      else if (curPunc == "(") pushContext(state, stream.column(), ")");
      else if (curPunc == "}") {
        while (ctx.type == "statement") ctx = popContext(state);
        if (ctx.type == "}") ctx = popContext(state);
        while (ctx.type == "statement") ctx = popContext(state);
      }
      else if (curPunc == ctx.type) popContext(state);
      else if (indentStatements &&
               (((ctx.type == "}" || ctx.type == "top") && curPunc != ";") ||
                (ctx.type == "statement" && curPunc == "newstatement"))) {
        pushContext(state, stream.column(), "statement", stream.current());
      }

      if (style == "variable" &&
          ((state.prevToken == "def" ||
            (parserConfig.typeFirstDefinitions && typeBefore(stream, state, stream.start) &&
             isTopScope(state.context) && stream.match(/^\s*\(/, false)))))
        style = "def";

      if (hooks.token) {
        var result = hooks.token(stream, state, style);
        if (result !== undefined) style = result;
      }

      if (style == "def" && parserConfig.styleDefs === false) style = "variable";

      state.startOfLine = false;
      state.prevToken = isDefKeyword ? "def" : style || curPunc;
      maybeEOL(stream, state);
      return style;
    },

    indent: function(state, textAfter) {
      if (state.tokenize != tokenBase && state.tokenize != null || state.typeAtEndOfLine) return CodeMirror.Pass;
      var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
      var closing = firstChar == ctx.type;
      if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
      if (parserConfig.dontIndentStatements)
        while (ctx.type == "statement" && parserConfig.dontIndentStatements.test(ctx.info))
          ctx = ctx.prev
      if (hooks.indent) {
        var hook = hooks.indent(state, ctx, textAfter, indentUnit);
        if (typeof hook == "number") return hook
      }
      var switchBlock = ctx.prev && ctx.prev.info == "switch";
      if (parserConfig.allmanIndentation && /[{(]/.test(firstChar)) {
        while (ctx.type != "top" && ctx.type != "}") ctx = ctx.prev
        return ctx.indented
      }
      if (ctx.type == "statement")
        return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
      if (ctx.align && (!dontAlignCalls || ctx.type != ")"))
        return ctx.column + (closing ? 0 : 1);
      if (ctx.type == ")" && !closing)
        return ctx.indented + statementIndentUnit;

      return ctx.indented + (closing ? 0 : indentUnit) +
        (!closing && switchBlock && !/^(?:case|default)\b/.test(textAfter) ? indentUnit : 0);
    },

    electricInput: indentSwitch ? /^\s*(?:case .*?:|default:|\{\}?|\})$/ : /^\s*[{}]$/,
    blockCommentStart: "/*",
    blockCommentEnd: "*/",
    blockCommentContinue: " * ",
    lineComment: "//",
    fold: "brace"
  };
});

  function words(str) {
    var obj = {}, words = str.split(" ");
    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
    return obj;
  }
  function contains(words, word) {
    if (typeof words === "function") {
      return words(word);
    } else {
      return words.propertyIsEnumerable(word);
    }
  }
  var cKeywords = "auto if break case register continue return default do sizeof " +
    "static else struct switch extern typedef union for goto while enum const " +
    "volatile inline restrict asm fortran";

  // Keywords from https://en.cppreference.com/w/cpp/keyword includes C++20.
  var cppKeywords = "alignas alignof and and_eq audit axiom bitand bitor catch " +
  "class compl concept constexpr const_cast decltype delete dynamic_cast " +
  "explicit export final friend import module mutable namespace new noexcept " +
  "not not_eq operator or or_eq override private protected public " +
  "reinterpret_cast requires static_assert static_cast template this " +
  "thread_local throw try typeid typename using virtual xor xor_eq";

  var objCKeywords = "bycopy byref in inout oneway out self super atomic nonatomic retain copy " +
  "readwrite readonly strong weak assign typeof nullable nonnull null_resettable _cmd " +
  "@interface @implementation @end @protocol @encode @property @synthesize @dynamic @class " +
  "@public @package @private @protected @required @optional @try @catch @finally @import " +
  "@selector @encode @defs @synchronized @autoreleasepool @compatibility_alias @available";

  var objCBuiltins = "FOUNDATION_EXPORT FOUNDATION_EXTERN NS_INLINE NS_FORMAT_FUNCTION " +
  " NS_RETURNS_RETAINEDNS_ERROR_ENUM NS_RETURNS_NOT_RETAINED NS_RETURNS_INNER_POINTER " +
  "NS_DESIGNATED_INITIALIZER NS_ENUM NS_OPTIONS NS_REQUIRES_NIL_TERMINATION " +
  "NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_SWIFT_NAME NS_REFINED_FOR_SWIFT"

  // Do not use this. Use the cTypes function below. This is global just to avoid
  // excessive calls when cTypes is being called multiple times during a parse.
  var basicCTypes = words("int long char short double float unsigned signed " +
    "void bool");

  // Do not use this. Use the objCTypes function below. This is global just to avoid
  // excessive calls when objCTypes is being called multiple times during a parse.
  var basicObjCTypes = words("SEL instancetype id Class Protocol BOOL");

  // Returns true if identifier is a "C" type.
  // C type is defined as those that are reserved by the compiler (basicTypes),
  // and those that end in _t (Reserved by POSIX for types)
  // http://www.gnu.org/software/libc/manual/html_node/Reserved-Names.html
  function cTypes(identifier) {
    return contains(basicCTypes, identifier) || /.+_t$/.test(identifier);
  }

  // Returns true if identifier is a "Objective C" type.
  function objCTypes(identifier) {
    return cTypes(identifier) || contains(basicObjCTypes, identifier);
  }

  var cBlockKeywords = "case do else for if switch while struct enum union";
  var cDefKeywords = "struct enum union";

  function cppHook(stream, state) {
    if (!state.startOfLine) return false
    for (var ch, next = null; ch = stream.peek();) {
      if (ch == "\\" && stream.match(/^.$/)) {
        next = cppHook
        break
      } else if (ch == "/" && stream.match(/^\/[\/\*]/, false)) {
        break
      }
      stream.next()
    }
    state.tokenize = next
    return "meta"
  }

  function pointerHook(_stream, state) {
    if (state.prevToken == "type") return "type";
    return false;
  }

  // For C and C++ (and ObjC): identifiers starting with __
  // or _ followed by a capital letter are reserved for the compiler.
  function cIsReservedIdentifier(token) {
    if (!token || token.length < 2) return false;
    if (token[0] != '_') return false;
    return (token[1] == '_') || (token[1] !== token[1].toLowerCase());
  }

  function cpp14Literal(stream) {
    stream.eatWhile(/[\w\.']/);
    return "number";
  }

  function cpp11StringHook(stream, state) {
    stream.backUp(1);
    // Raw strings.
    if (stream.match(/^(?:R|u8R|uR|UR|LR)/)) {
      var match = stream.match(/^"([^\s\\()]{0,16})\(/);
      if (!match) {
        return false;
      }
      state.cpp11RawStringDelim = match[1];
      state.tokenize = tokenRawString;
      return tokenRawString(stream, state);
    }
    // Unicode strings/chars.
    if (stream.match(/^(?:u8|u|U|L)/)) {
      if (stream.match(/^["']/, /* eat */ false)) {
        return "string";
      }
      return false;
    }
    // Ignore this hook.
    stream.next();
    return false;
  }

  function cppLooksLikeConstructor(word) {
    var lastTwo = /(\w+)::~?(\w+)$/.exec(word);
    return lastTwo && lastTwo[1] == lastTwo[2];
  }

  // C#-style strings where "" escapes a quote.
  function tokenAtString(stream, state) {
    var next;
    while ((next = stream.next()) != null) {
      if (next == '"' && !stream.eat('"')) {
        state.tokenize = null;
        break;
      }
    }
    return "string";
  }

  // C++11 raw string literal is <prefix>"<delim>( anything )<delim>", where
  // <delim> can be a string up to 16 characters long.
  function tokenRawString(stream, state) {
    // Escape characters that have special regex meanings.
    var delim = state.cpp11RawStringDelim.replace(/[^\w\s]/g, '\\$&');
    var match = stream.match(new RegExp(".*?\\)" + delim + '"'));
    if (match)
      state.tokenize = null;
    else
      stream.skipToEnd();
    return "string";
  }

  function def(mimes, mode) {
    if (typeof mimes == "string") mimes = [mimes];
    var words = [];
    function add(obj) {
      if (obj) for (var prop in obj) if (obj.hasOwnProperty(prop))
        words.push(prop);
    }
    add(mode.keywords);
    add(mode.types);
    add(mode.builtin);
    add(mode.atoms);
    if (words.length) {
      mode.helperType = mimes[0];
      CodeMirror.registerHelper("hintWords", mimes[0], words);
    }

    for (var i = 0; i < mimes.length; ++i)
      CodeMirror.defineMIME(mimes[i], mode);
  }

  def(["text/x-csrc", "text/x-c", "text/x-chdr"], {
    name: "clike",
    keywords: words(cKeywords),
    types: cTypes,
    blockKeywords: words(cBlockKeywords),
    defKeywords: words(cDefKeywords),
    typeFirstDefinitions: true,
    atoms: words("NULL true false"),
    isReservedIdentifier: cIsReservedIdentifier,
    hooks: {
      "#": cppHook,
      "*": pointerHook,
    },
    modeProps: {fold: ["brace", "include"]}
  });

  def(["text/x-c++src", "text/x-c++hdr"], {
    name: "clike",
    keywords: words(cKeywords + " " + cppKeywords),
    types: cTypes,
    blockKeywords: words(cBlockKeywords + " class try catch"),
    defKeywords: words(cDefKeywords + " class namespace"),
    typeFirstDefinitions: true,
    atoms: words("true false NULL nullptr"),
    dontIndentStatements: /^template$/,
    isIdentifierChar: /[\w\$_~\xa1-\uffff]/,
    isReservedIdentifier: cIsReservedIdentifier,
    hooks: {
      "#": cppHook,
      "*": pointerHook,
      "u": cpp11StringHook,
      "U": cpp11StringHook,
      "L": cpp11StringHook,
      "R": cpp11StringHook,
      "0": cpp14Literal,
      "1": cpp14Literal,
      "2": cpp14Literal,
      "3": cpp14Literal,
      "4": cpp14Literal,
      "5": cpp14Literal,
      "6": cpp14Literal,
      "7": cpp14Literal,
      "8": cpp14Literal,
      "9": cpp14Literal,
      token: function(stream, state, style) {
        if (style == "variable" && stream.peek() == "(" &&
            (state.prevToken == ";" || state.prevToken == null ||
             state.prevToken == "}") &&
            cppLooksLikeConstructor(stream.current()))
          return "def";
      }
    },
    namespaceSeparator: "::",
    modeProps: {fold: ["brace", "include"]}
  });

  def("text/x-java", {
    name: "clike",
    keywords: words("abstract assert break case catch class const continue default " +
                    "do else enum extends final finally for goto if implements import " +
                    "instanceof interface native new package private protected public " +
                    "return static strictfp super switch synchronized this throw throws transient " +
                    "try volatile while @interface"),
    types: words("byte short int long float double boolean char void Boolean Byte Character Double Float " +
                 "Integer Long Number Object Short String StringBuffer StringBuilder Void"),
    blockKeywords: words("catch class do else finally for if switch try while"),
    defKeywords: words("class interface enum @interface"),
    typeFirstDefinitions: true,
    atoms: words("true false null"),
    number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
    hooks: {
      "@": function(stream) {
        // Don't match the @interface keyword.
        if (stream.match('interface', false)) return false;

        stream.eatWhile(/[\w\$_]/);
        return "meta";
      }
    },
    modeProps: {fold: ["brace", "import"]}
  });

  def("text/x-csharp", {
    name: "clike",
    keywords: words("abstract as async await base break case catch checked class const continue" +
                    " default delegate do else enum event explicit extern finally fixed for" +
                    " foreach goto if implicit in interface internal is lock namespace new" +
                    " operator out override params private protected public readonly ref return sealed" +
                    " sizeof stackalloc static struct switch this throw try typeof unchecked" +
                    " unsafe using virtual void volatile while add alias ascending descending dynamic from get" +
                    " global group into join let orderby partial remove select set value var yield"),
    types: words("Action Boolean Byte Char DateTime DateTimeOffset Decimal Double Func" +
                 " Guid Int16 Int32 Int64 Object SByte Single String Task TimeSpan UInt16 UInt32" +
                 " UInt64 bool byte char decimal double short int long object"  +
                 " sbyte float string ushort uint ulong"),
    blockKeywords: words("catch class do else finally for foreach if struct switch try while"),
    defKeywords: words("class interface namespace struct var"),
    typeFirstDefinitions: true,
    atoms: words("true false null"),
    hooks: {
      "@": function(stream, state) {
        if (stream.eat('"')) {
          state.tokenize = tokenAtString;
          return tokenAtString(stream, state);
        }
        stream.eatWhile(/[\w\$_]/);
        return "meta";
      }
    }
  });

  function tokenTripleString(stream, state) {
    var escaped = false;
    while (!stream.eol()) {
      if (!escaped && stream.match('"""')) {
        state.tokenize = null;
        break;
      }
      escaped = stream.next() == "\\" && !escaped;
    }
    return "string";
  }

  function tokenNestedComment(depth) {
    return function (stream, state) {
      var ch
      while (ch = stream.next()) {
        if (ch == "*" && stream.eat("/")) {
          if (depth == 1) {
            state.tokenize = null
            break
          } else {
            state.tokenize = tokenNestedComment(depth - 1)
            return state.tokenize(stream, state)
          }
        } else if (ch == "/" && stream.eat("*")) {
          state.tokenize = tokenNestedComment(depth + 1)
          return state.tokenize(stream, state)
        }
      }
      return "comment"
    }
  }

  def("text/x-scala", {
    name: "clike",
    keywords: words(
      /* scala */
      "abstract case catch class def do else extends final finally for forSome if " +
      "implicit import lazy match new null object override package private protected return " +
      "sealed super this throw trait try type val var while with yield _ " +

      /* package scala */
      "assert assume require print println printf readLine readBoolean readByte readShort " +
      "readChar readInt readLong readFloat readDouble"
    ),
    types: words(
      "AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " +
      "Enumeration Equiv Error Exception Fractional Function IndexedSeq Int Integral Iterable " +
      "Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering " +
      "Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder " +
      "StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector " +

      /* package java.lang */
      "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
      "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
      "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
      "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
    ),
    multiLineStrings: true,
    blockKeywords: words("catch class enum do else finally for forSome if match switch try while"),
    defKeywords: words("class enum def object package trait type val var"),
    atoms: words("true false null"),
    indentStatements: false,
    indentSwitch: false,
    isOperatorChar: /[+\-*&%=<>!?|\/#:@]/,
    hooks: {
      "@": function(stream) {
        stream.eatWhile(/[\w\$_]/);
        return "meta";
      },
      '"': function(stream, state) {
        if (!stream.match('""')) return false;
        state.tokenize = tokenTripleString;
        return state.tokenize(stream, state);
      },
      "'": function(stream) {
        stream.eatWhile(/[\w\$_\xa1-\uffff]/);
        return "atom";
      },
      "=": function(stream, state) {
        var cx = state.context
        if (cx.type == "}" && cx.align && stream.eat(">")) {
          state.context = new Context(cx.indented, cx.column, cx.type, cx.info, null, cx.prev)
          return "operator"
        } else {
          return false
        }
      },

      "/": function(stream, state) {
        if (!stream.eat("*")) return false
        state.tokenize = tokenNestedComment(1)
        return state.tokenize(stream, state)
      }
    },
    modeProps: {closeBrackets: {pairs: '()[]{}""', triples: '"'}}
  });

  function tokenKotlinString(tripleString){
    return function (stream, state) {
      var escaped = false, next, end = false;
      while (!stream.eol()) {
        if (!tripleString && !escaped && stream.match('"') ) {end = true; break;}
        if (tripleString && stream.match('"""')) {end = true; break;}
        next = stream.next();
        if(!escaped && next == "$" && stream.match('{'))
          stream.skipTo("}");
        escaped = !escaped && next == "\\" && !tripleString;
      }
      if (end || !tripleString)
        state.tokenize = null;
      return "string";
    }
  }

  def("text/x-kotlin", {
    name: "clike",
    keywords: words(
      /*keywords*/
      "package as typealias class interface this super val operator " +
      "var fun for is in This throw return annotation " +
      "break continue object if else while do try when !in !is as? " +

      /*soft keywords*/
      "file import where by get set abstract enum open inner override private public internal " +
      "protected catch finally out final vararg reified dynamic companion constructor init " +
      "sealed field property receiver param sparam lateinit data inline noinline tailrec " +
      "external annotation crossinline const operator infix suspend actual expect setparam"
    ),
    types: words(
      /* package java.lang */
      "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
      "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
      "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
      "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void Annotation Any BooleanArray " +
      "ByteArray Char CharArray DeprecationLevel DoubleArray Enum FloatArray Function Int IntArray Lazy " +
      "LazyThreadSafetyMode LongArray Nothing ShortArray Unit"
    ),
    intendSwitch: false,
    indentStatements: false,
    multiLineStrings: true,
    number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+(\.\d+)?|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
    blockKeywords: words("catch class do else finally for if where try while enum"),
    defKeywords: words("class val var object interface fun"),
    atoms: words("true false null this"),
    hooks: {
      "@": function(stream) {
        stream.eatWhile(/[\w\$_]/);
        return "meta";
      },
      '*': function(_stream, state) {
        return state.prevToken == '.' ? 'variable' : 'operator';
      },
      '"': function(stream, state) {
        state.tokenize = tokenKotlinString(stream.match('""'));
        return state.tokenize(stream, state);
      },
      "/": function(stream, state) {
        if (!stream.eat("*")) return false;
        state.tokenize = tokenNestedComment(1);
        return state.tokenize(stream, state)
      },
      indent: function(state, ctx, textAfter, indentUnit) {
        var firstChar = textAfter && textAfter.charAt(0);
        if ((state.prevToken == "}" || state.prevToken == ")") && textAfter == "")
          return state.indented;
        if ((state.prevToken == "operator" && textAfter != "}" && state.context.type != "}") ||
          state.prevToken == "variable" && firstChar == "." ||
          (state.prevToken == "}" || state.prevToken == ")") && firstChar == ".")
          return indentUnit * 2 + ctx.indented;
        if (ctx.align && ctx.type == "}")
          return ctx.indented + (state.context.type == (textAfter || "").charAt(0) ? 0 : indentUnit);
      }
    },
    modeProps: {closeBrackets: {triples: '"'}}
  });

  def(["x-shader/x-vertex", "x-shader/x-fragment"], {
    name: "clike",
    keywords: words("sampler1D sampler2D sampler3D samplerCube " +
                    "sampler1DShadow sampler2DShadow " +
                    "const attribute uniform varying " +
                    "break continue discard return " +
                    "for while do if else struct " +
                    "in out inout"),
    types: words("float int bool void " +
                 "vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 " +
                 "mat2 mat3 mat4"),
    blockKeywords: words("for while do if else struct"),
    builtin: words("radians degrees sin cos tan asin acos atan " +
                    "pow exp log exp2 sqrt inversesqrt " +
                    "abs sign floor ceil fract mod min max clamp mix step smoothstep " +
                    "length distance dot cross normalize ftransform faceforward " +
                    "reflect refract matrixCompMult " +
                    "lessThan lessThanEqual greaterThan greaterThanEqual " +
                    "equal notEqual any all not " +
                    "texture1D texture1DProj texture1DLod texture1DProjLod " +
                    "texture2D texture2DProj texture2DLod texture2DProjLod " +
                    "texture3D texture3DProj texture3DLod texture3DProjLod " +
                    "textureCube textureCubeLod " +
                    "shadow1D shadow2D shadow1DProj shadow2DProj " +
                    "shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod " +
                    "dFdx dFdy fwidth " +
                    "noise1 noise2 noise3 noise4"),
    atoms: words("true false " +
                "gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex " +
                "gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 " +
                "gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 " +
                "gl_FogCoord gl_PointCoord " +
                "gl_Position gl_PointSize gl_ClipVertex " +
                "gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor " +
                "gl_TexCoord gl_FogFragCoord " +
                "gl_FragCoord gl_FrontFacing " +
                "gl_FragData gl_FragDepth " +
                "gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix " +
                "gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse " +
                "gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse " +
                "gl_TextureMatrixTranspose gl_ModelViewMatrixInverseTranspose " +
                "gl_ProjectionMatrixInverseTranspose " +
                "gl_ModelViewProjectionMatrixInverseTranspose " +
                "gl_TextureMatrixInverseTranspose " +
                "gl_NormalScale gl_DepthRange gl_ClipPlane " +
                "gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel " +
                "gl_FrontLightModelProduct gl_BackLightModelProduct " +
                "gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ " +
                "gl_FogParameters " +
                "gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords " +
                "gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats " +
                "gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits " +
                "gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits " +
                "gl_MaxDrawBuffers"),
    indentSwitch: false,
    hooks: {"#": cppHook},
    modeProps: {fold: ["brace", "include"]}
  });

  def("text/x-nesc", {
    name: "clike",
    keywords: words(cKeywords + " as atomic async call command component components configuration event generic " +
                    "implementation includes interface module new norace nx_struct nx_union post provides " +
                    "signal task uses abstract extends"),
    types: cTypes,
    blockKeywords: words(cBlockKeywords),
    atoms: words("null true false"),
    hooks: {"#": cppHook},
    modeProps: {fold: ["brace", "include"]}
  });

  def("text/x-objectivec", {
    name: "clike",
    keywords: words(cKeywords + " " + objCKeywords),
    types: objCTypes,
    builtin: words(objCBuiltins),
    blockKeywords: words(cBlockKeywords + " @synthesize @try @catch @finally @autoreleasepool @synchronized"),
    defKeywords: words(cDefKeywords + " @interface @implementation @protocol @class"),
    dontIndentStatements: /^@.*$/,
    typeFirstDefinitions: true,
    atoms: words("YES NO NULL Nil nil true false nullptr"),
    isReservedIdentifier: cIsReservedIdentifier,
    hooks: {
      "#": cppHook,
      "*": pointerHook,
    },
    modeProps: {fold: ["brace", "include"]}
  });

  def("text/x-objectivec++", {
    name: "clike",
    keywords: words(cKeywords + " " + objCKeywords + " " + cppKeywords),
    types: objCTypes,
    builtin: words(objCBuiltins),
    blockKeywords: words(cBlockKeywords + " @synthesize @try @catch @finally @autoreleasepool @synchronized class try catch"),
    defKeywords: words(cDefKeywords + " @interface @implementation @protocol @class class namespace"),
    dontIndentStatements: /^@.*$|^template$/,
    typeFirstDefinitions: true,
    atoms: words("YES NO NULL Nil nil true false nullptr"),
    isReservedIdentifier: cIsReservedIdentifier,
    hooks: {
      "#": cppHook,
      "*": pointerHook,
      "u": cpp11StringHook,
      "U": cpp11StringHook,
      "L": cpp11StringHook,
      "R": cpp11StringHook,
      "0": cpp14Literal,
      "1": cpp14Literal,
      "2": cpp14Literal,
      "3": cpp14Literal,
      "4": cpp14Literal,
      "5": cpp14Literal,
      "6": cpp14Literal,
      "7": cpp14Literal,
      "8": cpp14Literal,
      "9": cpp14Literal,
      token: function(stream, state, style) {
        if (style == "variable" && stream.peek() == "(" &&
            (state.prevToken == ";" || state.prevToken == null ||
             state.prevToken == "}") &&
            cppLooksLikeConstructor(stream.current()))
          return "def";
      }
    },
    namespaceSeparator: "::",
    modeProps: {fold: ["brace", "include"]}
  });

  def("text/x-squirrel", {
    name: "clike",
    keywords: words("base break clone continue const default delete enum extends function in class" +
                    " foreach local resume return this throw typeof yield constructor instanceof static"),
    types: cTypes,
    blockKeywords: words("case catch class else for foreach if switch try while"),
    defKeywords: words("function local class"),
    typeFirstDefinitions: true,
    atoms: words("true false null"),
    hooks: {"#": cppHook},
    modeProps: {fold: ["brace", "include"]}
  });

  // Ceylon Strings need to deal with interpolation
  var stringTokenizer = null;
  function tokenCeylonString(type) {
    return function(stream, state) {
      var escaped = false, next, end = false;
      while (!stream.eol()) {
        if (!escaped && stream.match('"') &&
              (type == "single" || stream.match('""'))) {
          end = true;
          break;
        }
        if (!escaped && stream.match('``')) {
          stringTokenizer = tokenCeylonString(type);
          end = true;
          break;
        }
        next = stream.next();
        escaped = type == "single" && !escaped && next == "\\";
      }
      if (end)
          state.tokenize = null;
      return "string";
    }
  }

  def("text/x-ceylon", {
    name: "clike",
    keywords: words("abstracts alias assembly assert assign break case catch class continue dynamic else" +
                    " exists extends finally for function given if import in interface is let module new" +
                    " nonempty object of out outer package return satisfies super switch then this throw" +
                    " try value void while"),
    types: function(word) {
        // In Ceylon all identifiers that start with an uppercase are types
        var first = word.charAt(0);
        return (first === first.toUpperCase() && first !== first.toLowerCase());
    },
    blockKeywords: words("case catch class dynamic else finally for function if interface module new object switch try while"),
    defKeywords: words("class dynamic function interface module object package value"),
    builtin: words("abstract actual aliased annotation by default deprecated doc final formal late license" +
                   " native optional sealed see serializable shared suppressWarnings tagged throws variable"),
    isPunctuationChar: /[\[\]{}\(\),;\:\.`]/,
    isOperatorChar: /[+\-*&%=<>!?|^~:\/]/,
    numberStart: /[\d#$]/,
    number: /^(?:#[\da-fA-F_]+|\$[01_]+|[\d_]+[kMGTPmunpf]?|[\d_]+\.[\d_]+(?:[eE][-+]?\d+|[kMGTPmunpf]|)|)/i,
    multiLineStrings: true,
    typeFirstDefinitions: true,
    atoms: words("true false null larger smaller equal empty finished"),
    indentSwitch: false,
    styleDefs: false,
    hooks: {
      "@": function(stream) {
        stream.eatWhile(/[\w\$_]/);
        return "meta";
      },
      '"': function(stream, state) {
          state.tokenize = tokenCeylonString(stream.match('""') ? "triple" : "single");
          return state.tokenize(stream, state);
        },
      '`': function(stream, state) {
          if (!stringTokenizer || !stream.match('`')) return false;
          state.tokenize = stringTokenizer;
          stringTokenizer = null;
          return state.tokenize(stream, state);
        },
      "'": function(stream) {
        stream.eatWhile(/[\w\$_\xa1-\uffff]/);
        return "atom";
      },
      token: function(_stream, state, style) {
          if ((style == "variable" || style == "type") &&
              state.prevToken == ".") {
            return "variable-2";
          }
        }
    },
    modeProps: {
        fold: ["brace", "import"],
        closeBrackets: {triples: '"'}
    }
  });

});


/***/ }),

/***/ "./node_modules/codemirror/mode/css/css.js":
/*!*************************************************!*\
  !*** ./node_modules/codemirror/mode/css/css.js ***!
  \*************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE

(function(mod) {
  if (true) // CommonJS
    mod(__webpack_require__(/*! ../../lib/codemirror */ "./node_modules/codemirror/lib/codemirror.js"));
  else {}
})(function(CodeMirror) {
"use strict";

CodeMirror.defineMode("css", function(config, parserConfig) {
  var inline = parserConfig.inline
  if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");

  var indentUnit = config.indentUnit,
      tokenHooks = parserConfig.tokenHooks,
      documentTypes = parserConfig.documentTypes || {},
      mediaTypes = parserConfig.mediaTypes || {},
      mediaFeatures = parserConfig.mediaFeatures || {},
      mediaValueKeywords = parserConfig.mediaValueKeywords || {},
      propertyKeywords = parserConfig.propertyKeywords || {},
      nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
      fontProperties = parserConfig.fontProperties || {},
      counterDescriptors = parserConfig.counterDescriptors || {},
      colorKeywords = parserConfig.colorKeywords || {},
      valueKeywords = parserConfig.valueKeywords || {},
      allowNested = parserConfig.allowNested,
      lineComment = parserConfig.lineComment,
      supportsAtComponent = parserConfig.supportsAtComponent === true,
      highlightNonStandardPropertyKeywords = config.highlightNonStandardPropertyKeywords !== false;

  var type, override;
  function ret(style, tp) { type = tp; return style; }

  // Tokenizers

  function tokenBase(stream, state) {
    var ch = stream.next();
    if (tokenHooks[ch]) {
      var result = tokenHooks[ch](stream, state);
      if (result !== false) return result;
    }
    if (ch == "@") {
      stream.eatWhile(/[\w\\\-]/);
      return ret("def", stream.current());
    } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
      return ret(null, "compare");
    } else if (ch == "\"" || ch == "'") {
      state.tokenize = tokenString(ch);
      return state.tokenize(stream, state);
    } else if (ch == "#") {
      stream.eatWhile(/[\w\\\-]/);
      return ret("atom", "hash");
    } else if (ch == "!") {
      stream.match(/^\s*\w*/);
      return ret("keyword", "important");
    } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
      stream.eatWhile(/[\w.%]/);
      return ret("number", "unit");
    } else if (ch === "-") {
      if (/[\d.]/.test(stream.peek())) {
        stream.eatWhile(/[\w.%]/);
        return ret("number", "unit");
      } else if (stream.match(/^-[\w\\\-]*/)) {
        stream.eatWhile(/[\w\\\-]/);
        if (stream.match(/^\s*:/, false))
          return ret("variable-2", "variable-definition");
        return ret("variable-2", "variable");
      } else if (stream.match(/^\w+-/)) {
        return ret("meta", "meta");
      }
    } else if (/[,+>*\/]/.test(ch)) {
      return ret(null, "select-op");
    } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
      return ret("qualifier", "qualifier");
    } else if (/[:;{}\[\]\(\)]/.test(ch)) {
      return ret(null, ch);
    } else if (stream.match(/^[\w-.]+(?=\()/)) {
      if (/^(url(-prefix)?|domain|regexp)$/i.test(stream.current())) {
        state.tokenize = tokenParenthesized;
      }
      return ret("variable callee", "variable");
    } else if (/[\w\\\-]/.test(ch)) {
      stream.eatWhile(/[\w\\\-]/);
      return ret("property", "word");
    } else {
      return ret(null, null);
    }
  }

  function tokenString(quote) {
    return function(stream, state) {
      var escaped = false, ch;
      while ((ch = stream.next()) != null) {
        if (ch == quote && !escaped) {
          if (quote == ")") stream.backUp(1);
          break;
        }
        escaped = !escaped && ch == "\\";
      }
      if (ch == quote || !escaped && quote != ")") state.tokenize = null;
      return ret("string", "string");
    };
  }

  function tokenParenthesized(stream, state) {
    stream.next(); // Must be '('
    if (!stream.match(/^\s*[\"\')]/, false))
      state.tokenize = tokenString(")");
    else
      state.tokenize = null;
    return ret(null, "(");
  }

  // Context management

  function Context(type, indent, prev) {
    this.type = type;
    this.indent = indent;
    this.prev = prev;
  }

  function pushContext(state, stream, type, indent) {
    state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context);
    return type;
  }

  function popContext(state) {
    if (state.context.prev)
      state.context = state.context.prev;
    return state.context.type;
  }

  function pass(type, stream, state) {
    return states[state.context.type](type, stream, state);
  }
  function popAndPass(type, stream, state, n) {
    for (var i = n || 1; i > 0; i--)
      state.context = state.context.prev;
    return pass(type, stream, state);
  }

  // Parser

  function wordAsValue(stream) {
    var word = stream.current().toLowerCase();
    if (valueKeywords.hasOwnProperty(word))
      override = "atom";
    else if (colorKeywords.hasOwnProperty(word))
      override = "keyword";
    else
      override = "variable";
  }

  var states = {};

  states.top = function(type, stream, state) {
    if (type == "{") {
      return pushContext(state, stream, "block");
    } else if (type == "}" && state.context.prev) {
      return popContext(state);
    } else if (supportsAtComponent && /@component/i.test(type)) {
      return pushContext(state, stream, "atComponentBlock");
    } else if (/^@(-moz-)?document$/i.test(type)) {
      return pushContext(state, stream, "documentTypes");
    } else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) {
      return pushContext(state, stream, "atBlock");
    } else if (/^@(font-face|counter-style)/i.test(type)) {
      state.stateArg = type;
      return "restricted_atBlock_before";
    } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) {
      return "keyframes";
    } else if (type && type.charAt(0) == "@") {
      return pushContext(state, stream, "at");
    } else if (type == "hash") {
      override = "builtin";
    } else if (type == "word") {
      override = "tag";
    } else if (type == "variable-definition") {
      return "maybeprop";
    } else if (type == "interpolation") {
      return pushContext(state, stream, "interpolation");
    } else if (type == ":") {
      return "pseudo";
    } else if (allowNested && type == "(") {
      return pushContext(state, stream, "parens");
    }
    return state.context.type;
  };

  states.block = function(type, stream, state) {
    if (type == "word") {
      var word = stream.current().toLowerCase();
      if (propertyKeywords.hasOwnProperty(word)) {
        override = "property";
        return "maybeprop";
      } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
        override = highlightNonStandardPropertyKeywords ? "string-2" : "property";
        return "maybeprop";
      } else if (allowNested) {
        override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
        return "block";
      } else {
        override += " error";
        return "maybeprop";
      }
    } else if (type == "meta") {
      return "block";
    } else if (!allowNested && (type == "hash" || type == "qualifier")) {
      override = "error";
      return "block";
    } else {
      return states.top(type, stream, state);
    }
  };

  states.maybeprop = function(type, stream, state) {
    if (type == ":") return pushContext(state, stream, "prop");
    return pass(type, stream, state);
  };

  states.prop = function(type, stream, state) {
    if (type == ";") return popContext(state);
    if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
    if (type == "}" || type == "{") return popAndPass(type, stream, state);
    if (type == "(") return pushContext(state, stream, "parens");

    if (type == "hash" && !/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(stream.current())) {
      override += " error";
    } else if (type == "word") {
      wordAsValue(stream);
    } else if (type == "interpolation") {
      return pushContext(state, stream, "interpolation");
    }
    return "prop";
  };

  states.propBlock = function(type, _stream, state) {
    if (type == "}") return popContext(state);
    if (type == "word") { override = "property"; return "maybeprop"; }
    return state.context.type;
  };

  states.parens = function(type, stream, state) {
    if (type == "{" || type == "}") return popAndPass(type, stream, state);
    if (type == ")") return popContext(state);
    if (type == "(") return pushContext(state, stream, "parens");
    if (type == "interpolation") return pushContext(state, stream, "interpolation");
    if (type == "word") wordAsValue(stream);
    return "parens";
  };

  states.pseudo = function(type, stream, state) {
    if (type == "meta") return "pseudo";

    if (type == "word") {
      override = "variable-3";
      return state.context.type;
    }
    return pass(type, stream, state);
  };

  states.documentTypes = function(type, stream, state) {
    if (type == "word" && documentTypes.hasOwnProperty(stream.current())) {
      override = "tag";
      return state.context.type;
    } else {
      return states.atBlock(type, stream, state);
    }
  };

  states.atBlock = function(type, stream, state) {
    if (type == "(") return pushContext(state, stream, "atBlock_parens");
    if (type == "}" || type == ";") return popAndPass(type, stream, state);
    if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");

    if (type == "interpolation") return pushContext(state, stream, "interpolation");

    if (type == "word") {
      var word = stream.current().toLowerCase();
      if (word == "only" || word == "not" || word == "and" || word == "or")
        override = "keyword";
      else if (mediaTypes.hasOwnProperty(word))
        override = "attribute";
      else if (mediaFeatures.hasOwnProperty(word))
        override = "property";
      else if (mediaValueKeywords.hasOwnProperty(word))
        override = "keyword";
      else if (propertyKeywords.hasOwnProperty(word))
        override = "property";
      else if (nonStandardPropertyKeywords.hasOwnProperty(word))
        override = highlightNonStandardPropertyKeywords ? "string-2" : "property";
      else if (valueKeywords.hasOwnProperty(word))
        override = "atom";
      else if (colorKeywords.hasOwnProperty(word))
        override = "keyword";
      else
        override = "error";
    }
    return state.context.type;
  };

  states.atComponentBlock = function(type, stream, state) {
    if (type == "}")
      return popAndPass(type, stream, state);
    if (type == "{")
      return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top", false);
    if (type == "word")
      override = "error";
    return state.context.type;
  };

  states.atBlock_parens = function(type, stream, state) {
    if (type == ")") return popContext(state);
    if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
    return states.atBlock(type, stream, state);
  };

  states.restricted_atBlock_before = function(type, stream, state) {
    if (type == "{")
      return pushContext(state, stream, "restricted_atBlock");
    if (type == "word" && state.stateArg == "@counter-style") {
      override = "variable";
      return "restricted_atBlock_before";
    }
    return pass(type, stream, state);
  };

  states.restricted_atBlock = function(type, stream, state) {
    if (type == "}") {
      state.stateArg = null;
      return popContext(state);
    }
    if (type == "word") {
      if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) ||
          (state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase())))
        override = "error";
      else
        override = "property";
      return "maybeprop";
    }
    return "restricted_atBlock";
  };

  states.keyframes = function(type, stream, state) {
    if (type == "word") { override = "variable"; return "keyframes"; }
    if (type == "{") return pushContext(state, stream, "top");
    return pass(type, stream, state);
  };

  states.at = function(type, stream, state) {
    if (type == ";") return popContext(state);
    if (type == "{" || type == "}") return popAndPass(type, stream, state);
    if (type == "word") override = "tag";
    else if (type == "hash") override = "builtin";
    return "at";
  };

  states.interpolation = function(type, stream, state) {
    if (type == "}") return popContext(state);
    if (type == "{" || type == ";") return popAndPass(type, stream, state);
    if (type == "word") override = "variable";
    else if (type != "variable" && type != "(" && type != ")") override = "error";
    return "interpolation";
  };

  return {
    startState: function(base) {
      return {tokenize: null,
              state: inline ? "block" : "top",
              stateArg: null,
              context: new Context(inline ? "block" : "top", base || 0, null)};
    },

    token: function(stream, state) {
      if (!state.tokenize && stream.eatSpace()) return null;
      var style = (state.tokenize || tokenBase)(stream, state);
      if (style && typeof style == "object") {
        type = style[1];
        style = style[0];
      }
      override = style;
      if (type != "comment")
        state.state = states[state.state](type, stream, state);
      return override;
    },

    indent: function(state, textAfter) {
      var cx = state.context, ch = textAfter && textAfter.charAt(0);
      var indent = cx.indent;
      if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
      if (cx.prev) {
        if (ch == "}" && (cx.type == "block" || cx.type == "top" ||
                          cx.type == "interpolation" || cx.type == "restricted_atBlock")) {
          // Resume indentation from parent context.
          cx = cx.prev;
          indent = cx.indent;
        } else if (ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
            ch == "{" && (cx.type == "at" || cx.type == "atBlock")) {
          // Dedent relative to current context.
          indent = Math.max(0, cx.indent - indentUnit);
        }
      }
      return indent;
    },

    electricChars: "}",
    blockCommentStart: "/*",
    blockCommentEnd: "*/",
    blockCommentContinue: " * ",
    lineComment: lineComment,
    fold: "brace"
  };
});

  function keySet(array) {
    var keys = {};
    for (var i = 0; i < array.length; ++i) {
      keys[array[i].toLowerCase()] = true;
    }
    return keys;
  }

  var documentTypes_ = [
    "domain", "regexp", "url", "url-prefix"
  ], documentTypes = keySet(documentTypes_);

  var mediaTypes_ = [
    "all", "aural", "braille", "handheld", "print", "projection", "screen",
    "tty", "tv", "embossed"
  ], mediaTypes = keySet(mediaTypes_);

  var mediaFeatures_ = [
    "width", "min-width", "max-width", "height", "min-height", "max-height",
    "device-width", "min-device-width", "max-device-width", "device-height",
    "min-device-height", "max-device-height", "aspect-ratio",
    "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
    "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
    "max-color", "color-index", "min-color-index", "max-color-index",
    "monochrome", "min-monochrome", "max-monochrome", "resolution",
    "min-resolution", "max-resolution", "scan", "grid", "orientation",
    "device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio",
    "pointer", "any-pointer", "hover", "any-hover", "prefers-color-scheme"
  ], mediaFeatures = keySet(mediaFeatures_);

  var mediaValueKeywords_ = [
    "landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover",
    "interlace", "progressive",
    "dark", "light"
  ], mediaValueKeywords = keySet(mediaValueKeywords_);

  var propertyKeywords_ = [
    "align-content", "align-items", "align-self", "alignment-adjust",
    "alignment-baseline", "all", "anchor-point", "animation", "animation-delay",
    "animation-direction", "animation-duration", "animation-fill-mode",
    "animation-iteration-count", "animation-name", "animation-play-state",
    "animation-timing-function", "appearance", "azimuth", "backdrop-filter",
    "backface-visibility", "background", "background-attachment",
    "background-blend-mode", "background-clip", "background-color",
    "background-image", "background-origin", "background-position",
    "background-position-x", "background-position-y", "background-repeat",
    "background-size", "baseline-shift", "binding", "bleed", "block-size",
    "bookmark-label", "bookmark-level", "bookmark-state", "bookmark-target",
    "border", "border-bottom", "border-bottom-color", "border-bottom-left-radius",
    "border-bottom-right-radius", "border-bottom-style", "border-bottom-width",
    "border-collapse", "border-color", "border-image", "border-image-outset",
    "border-image-repeat", "border-image-slice", "border-image-source",
    "border-image-width", "border-left", "border-left-color", "border-left-style",
    "border-left-width", "border-radius", "border-right", "border-right-color",
    "border-right-style", "border-right-width", "border-spacing", "border-style",
    "border-top", "border-top-color", "border-top-left-radius",
    "border-top-right-radius", "border-top-style", "border-top-width",
    "border-width", "bottom", "box-decoration-break", "box-shadow", "box-sizing",
    "break-after", "break-before", "break-inside", "caption-side", "caret-color",
    "clear", "clip", "color", "color-profile", "column-count", "column-fill",
    "column-gap", "column-rule", "column-rule-color", "column-rule-style",
    "column-rule-width", "column-span", "column-width", "columns", "contain",
    "content", "counter-increment", "counter-reset", "crop", "cue", "cue-after",
    "cue-before", "cursor", "direction", "display", "dominant-baseline",
    "drop-initial-after-adjust", "drop-initial-after-align",
    "drop-initial-before-adjust", "drop-initial-before-align", "drop-initial-size",
    "drop-initial-value", "elevation", "empty-cells", "fit", "fit-position",
    "flex", "flex-basis", "flex-direction", "flex-flow", "flex-grow",
    "flex-shrink", "flex-wrap", "float", "float-offset", "flow-from", "flow-into",
    "font", "font-family", "font-feature-settings", "font-kerning",
    "font-language-override", "font-optical-sizing", "font-size",
    "font-size-adjust", "font-stretch", "font-style", "font-synthesis",
    "font-variant", "font-variant-alternates", "font-variant-caps",
    "font-variant-east-asian", "font-variant-ligatures", "font-variant-numeric",
    "font-variant-position", "font-variation-settings", "font-weight", "gap",
    "grid", "grid-area", "grid-auto-columns", "grid-auto-flow", "grid-auto-rows",
    "grid-column", "grid-column-end", "grid-column-gap", "grid-column-start",
    "grid-gap", "grid-row", "grid-row-end", "grid-row-gap", "grid-row-start",
    "grid-template", "grid-template-areas", "grid-template-columns",
    "grid-template-rows", "hanging-punctuation", "height", "hyphens", "icon",
    "image-orientation", "image-rendering", "image-resolution", "inline-box-align",
    "inset", "inset-block", "inset-block-end", "inset-block-start", "inset-inline",
    "inset-inline-end", "inset-inline-start", "isolation", "justify-content",
    "justify-items", "justify-self", "left", "letter-spacing", "line-break",
    "line-height", "line-height-step", "line-stacking", "line-stacking-ruby",
    "line-stacking-shift", "line-stacking-strategy", "list-style",
    "list-style-image", "list-style-position", "list-style-type", "margin",
    "margin-bottom", "margin-left", "margin-right", "margin-top", "marks",
    "marquee-direction", "marquee-loop", "marquee-play-count", "marquee-speed",
    "marquee-style", "mask-clip", "mask-composite", "mask-image", "mask-mode",
    "mask-origin", "mask-position", "mask-repeat", "mask-size","mask-type",
    "max-block-size", "max-height", "max-inline-size",
    "max-width", "min-block-size", "min-height", "min-inline-size", "min-width",
    "mix-blend-mode", "move-to", "nav-down", "nav-index", "nav-left", "nav-right",
    "nav-up", "object-fit", "object-position", "offset", "offset-anchor",
    "offset-distance", "offset-path", "offset-position", "offset-rotate",
    "opacity", "order", "orphans", "outline", "outline-color", "outline-offset",
    "outline-style", "outline-width", "overflow", "overflow-style",
    "overflow-wrap", "overflow-x", "overflow-y", "padding", "padding-bottom",
    "padding-left", "padding-right", "padding-top", "page", "page-break-after",
    "page-break-before", "page-break-inside", "page-policy", "pause",
    "pause-after", "pause-before", "perspective", "perspective-origin", "pitch",
    "pitch-range", "place-content", "place-items", "place-self", "play-during",
    "position", "presentation-level", "punctuation-trim", "quotes",
    "region-break-after", "region-break-before", "region-break-inside",
    "region-fragment", "rendering-intent", "resize", "rest", "rest-after",
    "rest-before", "richness", "right", "rotate", "rotation", "rotation-point",
    "row-gap", "ruby-align", "ruby-overhang", "ruby-position", "ruby-span",
    "scale", "scroll-behavior", "scroll-margin", "scroll-margin-block",
    "scroll-margin-block-end", "scroll-margin-block-start", "scroll-margin-bottom",
    "scroll-margin-inline", "scroll-margin-inline-end",
    "scroll-margin-inline-start", "scroll-margin-left", "scroll-margin-right",
    "scroll-margin-top", "scroll-padding", "scroll-padding-block",
    "scroll-padding-block-end", "scroll-padding-block-start",
    "scroll-padding-bottom", "scroll-padding-inline", "scroll-padding-inline-end",
    "scroll-padding-inline-start", "scroll-padding-left", "scroll-padding-right",
    "scroll-padding-top", "scroll-snap-align", "scroll-snap-type",
    "shape-image-threshold", "shape-inside", "shape-margin", "shape-outside",
    "size", "speak", "speak-as", "speak-header", "speak-numeral",
    "speak-punctuation", "speech-rate", "stress", "string-set", "tab-size",
    "table-layout", "target", "target-name", "target-new", "target-position",
    "text-align", "text-align-last", "text-combine-upright", "text-decoration",
    "text-decoration-color", "text-decoration-line", "text-decoration-skip",
    "text-decoration-skip-ink", "text-decoration-style", "text-emphasis",
    "text-emphasis-color", "text-emphasis-position", "text-emphasis-style",
    "text-height", "text-indent", "text-justify", "text-orientation",
    "text-outline", "text-overflow", "text-rendering", "text-shadow",
    "text-size-adjust", "text-space-collapse", "text-transform",
    "text-underline-position", "text-wrap", "top", "touch-action", "transform", "transform-origin",
    "transform-style", "transition", "transition-delay", "transition-duration",
    "transition-property", "transition-timing-function", "translate",
    "unicode-bidi", "user-select", "vertical-align", "visibility", "voice-balance",
    "voice-duration", "voice-family", "voice-pitch", "voice-range", "voice-rate",
    "voice-stress", "voice-volume", "volume", "white-space", "widows", "width",
    "will-change", "word-break", "word-spacing", "word-wrap", "writing-mode", "z-index",
    // SVG-specific
    "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
    "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
    "color-interpolation", "color-interpolation-filters",
    "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
    "marker", "marker-end", "marker-mid", "marker-start", "paint-order", "shape-rendering", "stroke",
    "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
    "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
    "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
    "glyph-orientation-vertical", "text-anchor", "writing-mode",
  ], propertyKeywords = keySet(propertyKeywords_);

  var nonStandardPropertyKeywords_ = [
    "border-block", "border-block-color", "border-block-end",
    "border-block-end-color", "border-block-end-style", "border-block-end-width",
    "border-block-start", "border-block-start-color", "border-block-start-style",
    "border-block-start-width", "border-block-style", "border-block-width",
    "border-inline", "border-inline-color", "border-inline-end",
    "border-inline-end-color", "border-inline-end-style",
    "border-inline-end-width", "border-inline-start", "border-inline-start-color",
    "border-inline-start-style", "border-inline-start-width",
    "border-inline-style", "border-inline-width", "margin-block",
    "margin-block-end", "margin-block-start", "margin-inline", "margin-inline-end",
    "margin-inline-start", "padding-block", "padding-block-end",
    "padding-block-start", "padding-inline", "padding-inline-end",
    "padding-inline-start", "scroll-snap-stop", "scrollbar-3d-light-color",
    "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
    "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
    "scrollbar-track-color", "searchfield-cancel-button", "searchfield-decoration",
    "searchfield-results-button", "searchfield-results-decoration", "shape-inside", "zoom"
  ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);

  var fontProperties_ = [
    "font-display", "font-family", "src", "unicode-range", "font-variant",
     "font-feature-settings", "font-stretch", "font-weight", "font-style"
  ], fontProperties = keySet(fontProperties_);

  var counterDescriptors_ = [
    "additive-symbols", "fallback", "negative", "pad", "prefix", "range",
    "speak-as", "suffix", "symbols", "system"
  ], counterDescriptors = keySet(counterDescriptors_);

  var colorKeywords_ = [
    "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
    "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
    "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
    "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
    "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
    "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
    "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
    "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
    "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
    "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
    "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
    "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
    "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
    "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
    "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
    "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
    "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
    "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
    "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
    "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
    "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
    "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
    "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
    "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
    "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
    "whitesmoke", "yellow", "yellowgreen"
  ], colorKeywords = keySet(colorKeywords_);

  var valueKeywords_ = [
    "above", "absolute", "activeborder", "additive", "activecaption", "afar",
    "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate",
    "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
    "arabic-indic", "armenian", "asterisks", "attr", "auto", "auto-flow", "avoid", "avoid-column", "avoid-page",
    "avoid-region", "axis-pan", "background", "backwards", "baseline", "below", "bidi-override", "binary",
    "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
    "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel",
    "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian",
    "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
    "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
    "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
    "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
    "compact", "condensed", "contain", "content", "contents",
    "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
    "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
    "decimal-leading-zero", "default", "default-button", "dense", "destination-atop",
    "destination-in", "destination-out", "destination-over", "devanagari", "difference",
    "disc", "discard", "disclosure-closed", "disclosure-open", "document",
    "dot-dash", "dot-dot-dash",
    "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
    "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
    "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
    "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
    "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
    "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
    "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
    "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
    "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
    "extra-expanded", "fantasy", "fast", "fill", "fill-box", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
    "forwards", "from", "geometricPrecision", "georgian", "graytext", "grid", "groove",
    "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
    "help", "hidden", "hide", "higher", "highlight", "highlighttext",
    "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
    "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
    "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
    "inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert",
    "italic", "japanese-formal", "japanese-informal", "justify", "kannada",
    "katakana", "katakana-iroha", "keep-all", "khmer",
    "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
    "landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten",
    "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem",
    "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
    "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
    "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "manipulation", "match", "matrix", "matrix3d",
    "media-controls-background", "media-current-time-display",
    "media-fullscreen-button", "media-mute-button", "media-play-button",
    "media-return-to-realtime-button", "media-rewind-button",
    "media-seek-back-button", "media-seek-forward-button", "media-slider",
    "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
    "media-volume-slider-container", "media-volume-sliderthumb", "medium",
    "menu", "menulist", "menulist-button", "menulist-text",
    "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
    "mix", "mongolian", "monospace", "move", "multiple", "multiple_mask_images", "multiply", "myanmar", "n-resize",
    "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
    "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
    "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "opacity", "open-quote",
    "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
    "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
    "painted", "page", "paused", "persian", "perspective", "pinch-zoom", "plus-darker", "plus-lighter",
    "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d",
    "progress", "push-button", "radial-gradient", "radio", "read-only",
    "read-write", "read-write-plaintext-only", "rectangle", "region",
    "relative", "repeat", "repeating-linear-gradient",
    "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse",
    "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
    "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
    "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
    "scroll", "scrollbar", "scroll-position", "se-resize", "searchfield",
    "searchfield-cancel-button", "searchfield-decoration",
    "searchfield-results-button", "searchfield-results-decoration", "self-start", "self-end",
    "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
    "simp-chinese-formal", "simp-chinese-informal", "single",
    "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
    "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
    "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
    "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "space-evenly", "spell-out", "square",
    "square-button", "start", "static", "status-bar", "stretch", "stroke", "stroke-box", "sub",
    "subpixel-antialiased", "svg_masks", "super", "sw-resize", "symbolic", "symbols", "system-ui", "table",
    "table-caption", "table-cell", "table-column", "table-column-group",
    "table-footer-group", "table-header-group", "table-row", "table-row-group",
    "tamil",
    "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
    "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
    "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
    "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
    "trad-chinese-formal", "trad-chinese-informal", "transform",
    "translate", "translate3d", "translateX", "translateY", "translateZ",
    "transparent", "ultra-condensed", "ultra-expanded", "underline", "unidirectional-pan", "unset", "up",
    "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
    "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
    "var", "vertical", "vertical-text", "view-box", "visible", "visibleFill", "visiblePainted",
    "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
    "window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor",
    "xx-large", "xx-small"
  ], valueKeywords = keySet(valueKeywords_);

  var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_)
    .concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_)
    .concat(valueKeywords_);
  CodeMirror.registerHelper("hintWords", "css", allWords);

  function tokenCComment(stream, state) {
    var maybeEnd = false, ch;
    while ((ch = stream.next()) != null) {
      if (maybeEnd && ch == "/") {
        state.tokenize = null;
        break;
      }
      maybeEnd = (ch == "*");
    }
    return ["comment", "comment"];
  }

  CodeMirror.defineMIME("text/css", {
    documentTypes: documentTypes,
    mediaTypes: mediaTypes,
    mediaFeatures: mediaFeatures,
    mediaValueKeywords: mediaValueKeywords,
    propertyKeywords: propertyKeywords,
    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
    fontProperties: fontProperties,
    counterDescriptors: counterDescriptors,
    colorKeywords: colorKeywords,
    valueKeywords: valueKeywords,
    tokenHooks: {
      "/": function(stream, state) {
        if (!stream.eat("*")) return false;
        state.tokenize = tokenCComment;
        return tokenCComment(stream, state);
      }
    },
    name: "css"
  });

  CodeMirror.defineMIME("text/x-scss", {
    mediaTypes: mediaTypes,
    mediaFeatures: mediaFeatures,
    mediaValueKeywords: mediaValueKeywords,
    propertyKeywords: propertyKeywords,
    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
    colorKeywords: colorKeywords,
    valueKeywords: valueKeywords,
    fontProperties: fontProperties,
    allowNested: true,
    lineComment: "//",
    tokenHooks: {
      "/": function(stream, state) {
        if (stream.eat("/")) {
          stream.skipToEnd();
          return ["comment", "comment"];
        } else if (stream.eat("*")) {
          state.tokenize = tokenCComment;
          return tokenCComment(stream, state);
        } else {
          return ["operator", "operator"];
        }
      },
      ":": function(stream) {
        if (stream.match(/^\s*\{/, false))
          return [null, null]
        return false;
      },
      "$": function(stream) {
        stream.match(/^[\w-]+/);
        if (stream.match(/^\s*:/, false))
          return ["variable-2", "variable-definition"];
        return ["variable-2", "variable"];
      },
      "#": function(stream) {
        if (!stream.eat("{")) return false;
        return [null, "interpolation"];
      }
    },
    name: "css",
    helperType: "scss"
  });

  CodeMirror.defineMIME("text/x-less", {
    mediaTypes: mediaTypes,
    mediaFeatures: mediaFeatures,
    mediaValueKeywords: mediaValueKeywords,
    propertyKeywords: propertyKeywords,
    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
    colorKeywords: colorKeywords,
    valueKeywords: valueKeywords,
    fontProperties: fontProperties,
    allowNested: true,
    lineComment: "//",
    tokenHooks: {
      "/": function(stream, state) {
        if (stream.eat("/")) {
          stream.skipToEnd();
          return ["comment", "comment"];
        } else if (stream.eat("*")) {
          state.tokenize = tokenCComment;
          return tokenCComment(stream, state);
        } else {
          return ["operator", "operator"];
        }
      },
      "@": function(stream) {
        if (stream.eat("{")) return [null, "interpolation"];
        if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i, false)) return false;
        stream.eatWhile(/[\w\\\-]/);
        if (stream.match(/^\s*:/, false))
          return ["variable-2", "variable-definition"];
        return ["variable-2", "variable"];
      },
      "&": function() {
        return ["atom", "atom"];
      }
    },
    name: "css",
    helperType: "less"
  });

  CodeMirror.defineMIME("text/x-gss", {
    documentTypes: documentTypes,
    mediaTypes: mediaTypes,
    mediaFeatures: mediaFeatures,
    propertyKeywords: propertyKeywords,
    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
    fontProperties: fontProperties,
    counterDescriptors: counterDescriptors,
    colorKeywords: colorKeywords,
    valueKeywords: valueKeywords,
    supportsAtComponent: true,
    tokenHooks: {
      "/": function(stream, state) {
        if (!stream.eat("*")) return false;
        state.tokenize = tokenCComment;
        return tokenCComment(stream, state);
      }
    },
    name: "css",
    helperType: "gss"
  });

});


/***/ }),

/***/ "./node_modules/codemirror/mode/gas/gas.js":
/*!*************************************************!*\
  !*** ./node_modules/codemirror/mode/gas/gas.js ***!
  \*************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE

(function(mod) {
  if (true) // CommonJS
    mod(__webpack_require__(/*! ../../lib/codemirror */ "./node_modules/codemirror/lib/codemirror.js"));
  else {}
})(function(CodeMirror) {
"use strict";

CodeMirror.defineMode("gas", function(_config, parserConfig) {
  'use strict';

  // If an architecture is specified, its initialization function may
  // populate this array with custom parsing functions which will be
  // tried in the event that the standard functions do not find a match.
  var custom = [];

  // The symbol used to start a line comment changes based on the target
  // architecture.
  // If no architecture is pased in "parserConfig" then only multiline
  // comments will have syntax support.
  var lineCommentStartSymbol = "";

  // These directives are architecture independent.
  // Machine specific directives should go in their respective
  // architecture initialization function.
  // Reference:
  // http://sourceware.org/binutils/docs/as/Pseudo-Ops.html#Pseudo-Ops
  var directives = {
    ".abort" : "builtin",
    ".align" : "builtin",
    ".altmacro" : "builtin",
    ".ascii" : "builtin",
    ".asciz" : "builtin",
    ".balign" : "builtin",
    ".balignw" : "builtin",
    ".balignl" : "builtin",
    ".bundle_align_mode" : "builtin",
    ".bundle_lock" : "builtin",
    ".bundle_unlock" : "builtin",
    ".byte" : "builtin",
    ".cfi_startproc" : "builtin",
    ".comm" : "builtin",
    ".data" : "builtin",
    ".def" : "builtin",
    ".desc" : "builtin",
    ".dim" : "builtin",
    ".double" : "builtin",
    ".eject" : "builtin",
    ".else" : "builtin",
    ".elseif" : "builtin",
    ".end" : "builtin",
    ".endef" : "builtin",
    ".endfunc" : "builtin",
    ".endif" : "builtin",
    ".equ" : "builtin",
    ".equiv" : "builtin",
    ".eqv" : "builtin",
    ".err" : "builtin",
    ".error" : "builtin",
    ".exitm" : "builtin",
    ".extern" : "builtin",
    ".fail" : "builtin",
    ".file" : "builtin",
    ".fill" : "builtin",
    ".float" : "builtin",
    ".func" : "builtin",
    ".global" : "builtin",
    ".gnu_attribute" : "builtin",
    ".hidden" : "builtin",
    ".hword" : "builtin",
    ".ident" : "builtin",
    ".if" : "builtin",
    ".incbin" : "builtin",
    ".include" : "builtin",
    ".int" : "builtin",
    ".internal" : "builtin",
    ".irp" : "builtin",
    ".irpc" : "builtin",
    ".lcomm" : "builtin",
    ".lflags" : "builtin",
    ".line" : "builtin",
    ".linkonce" : "builtin",
    ".list" : "builtin",
    ".ln" : "builtin",
    ".loc" : "builtin",
    ".loc_mark_labels" : "builtin",
    ".local" : "builtin",
    ".long" : "builtin",
    ".macro" : "builtin",
    ".mri" : "builtin",
    ".noaltmacro" : "builtin",
    ".nolist" : "builtin",
    ".octa" : "builtin",
    ".offset" : "builtin",
    ".org" : "builtin",
    ".p2align" : "builtin",
    ".popsection" : "builtin",
    ".previous" : "builtin",
    ".print" : "builtin",
    ".protected" : "builtin",
    ".psize" : "builtin",
    ".purgem" : "builtin",
    ".pushsection" : "builtin",
    ".quad" : "builtin",
    ".reloc" : "builtin",
    ".rept" : "builtin",
    ".sbttl" : "builtin",
    ".scl" : "builtin",
    ".section" : "builtin",
    ".set" : "builtin",
    ".short" : "builtin",
    ".single" : "builtin",
    ".size" : "builtin",
    ".skip" : "builtin",
    ".sleb128" : "builtin",
    ".space" : "builtin",
    ".stab" : "builtin",
    ".string" : "builtin",
    ".struct" : "builtin",
    ".subsection" : "builtin",
    ".symver" : "builtin",
    ".tag" : "builtin",
    ".text" : "builtin",
    ".title" : "builtin",
    ".type" : "builtin",
    ".uleb128" : "builtin",
    ".val" : "builtin",
    ".version" : "builtin",
    ".vtable_entry" : "builtin",
    ".vtable_inherit" : "builtin",
    ".warning" : "builtin",
    ".weak" : "builtin",
    ".weakref" : "builtin",
    ".word" : "builtin"
  };

  var registers = {};

  function x86(_parserConfig) {
    lineCommentStartSymbol = "#";

    registers.ax  = "variable";
    registers.eax = "variable-2";
    registers.rax = "variable-3";

    registers.bx  = "variable";
    registers.ebx = "variable-2";
    registers.rbx = "variable-3";

    registers.cx  = "variable";
    registers.ecx = "variable-2";
    registers.rcx = "variable-3";

    registers.dx  = "variable";
    registers.edx = "variable-2";
    registers.rdx = "variable-3";

    registers.si  = "variable";
    registers.esi = "variable-2";
    registers.rsi = "variable-3";

    registers.di  = "variable";
    registers.edi = "variable-2";
    registers.rdi = "variable-3";

    registers.sp  = "variable";
    registers.esp = "variable-2";
    registers.rsp = "variable-3";

    registers.bp  = "variable";
    registers.ebp = "variable-2";
    registers.rbp = "variable-3";

    registers.ip  = "variable";
    registers.eip = "variable-2";
    registers.rip = "variable-3";

    registers.cs  = "keyword";
    registers.ds  = "keyword";
    registers.ss  = "keyword";
    registers.es  = "keyword";
    registers.fs  = "keyword";
    registers.gs  = "keyword";
  }

  function armv6(_parserConfig) {
    // Reference:
    // http://infocenter.arm.com/help/topic/com.arm.doc.qrc0001l/QRC0001_UAL.pdf
    // http://infocenter.arm.com/help/topic/com.arm.doc.ddi0301h/DDI0301H_arm1176jzfs_r0p7_trm.pdf
    lineCommentStartSymbol = "@";
    directives.syntax = "builtin";

    registers.r0  = "variable";
    registers.r1  = "variable";
    registers.r2  = "variable";
    registers.r3  = "variable";
    registers.r4  = "variable";
    registers.r5  = "variable";
    registers.r6  = "variable";
    registers.r7  = "variable";
    registers.r8  = "variable";
    registers.r9  = "variable";
    registers.r10 = "variable";
    registers.r11 = "variable";
    registers.r12 = "variable";

    registers.sp  = "variable-2";
    registers.lr  = "variable-2";
    registers.pc  = "variable-2";
    registers.r13 = registers.sp;
    registers.r14 = registers.lr;
    registers.r15 = registers.pc;

    custom.push(function(ch, stream) {
      if (ch === '#') {
        stream.eatWhile(/\w/);
        return "number";
      }
    });
  }

  var arch = (parserConfig.architecture || "x86").toLowerCase();
  if (arch === "x86") {
    x86(parserConfig);
  } else if (arch === "arm" || arch === "armv6") {
    armv6(parserConfig);
  }

  function nextUntilUnescaped(stream, end) {
    var escaped = false, next;
    while ((next = stream.next()) != null) {
      if (next === end && !escaped) {
        return false;
      }
      escaped = !escaped && next === "\\";
    }
    return escaped;
  }

  function clikeComment(stream, state) {
    var maybeEnd = false, ch;
    while ((ch = stream.next()) != null) {
      if (ch === "/" && maybeEnd) {
        state.tokenize = null;
        break;
      }
      maybeEnd = (ch === "*");
    }
    return "comment";
  }

  return {
    startState: function() {
      return {
        tokenize: null
      };
    },

    token: function(stream, state) {
      if (state.tokenize) {
        return state.tokenize(stream, state);
      }

      if (stream.eatSpace()) {
        return null;
      }

      var style, cur, ch = stream.next();

      if (ch === "/") {
        if (stream.eat("*")) {
          state.tokenize = clikeComment;
          return clikeComment(stream, state);
        }
      }

      if (ch === lineCommentStartSymbol) {
        stream.skipToEnd();
        return "comment";
      }

      if (ch === '"') {
        nextUntilUnescaped(stream, '"');
        return "string";
      }

      if (ch === '.') {
        stream.eatWhile(/\w/);
        cur = stream.current().toLowerCase();
        style = directives[cur];
        return style || null;
      }

      if (ch === '=') {
        stream.eatWhile(/\w/);
        return "tag";
      }

      if (ch === '{') {
        return "bracket";
      }

      if (ch === '}') {
        return "bracket";
      }

      if (/\d/.test(ch)) {
        if (ch === "0" && stream.eat("x")) {
          stream.eatWhile(/[0-9a-fA-F]/);
          return "number";
        }
        stream.eatWhile(/\d/);
        return "number";
      }

      if (/\w/.test(ch)) {
        stream.eatWhile(/\w/);
        if (stream.eat(":")) {
          return 'tag';
        }
        cur = stream.current().toLowerCase();
        style = registers[cur];
        return style || null;
      }

      for (var i = 0; i < custom.length; i++) {
        style = custom[i](ch, stream, state);
        if (style) {
          return style;
        }
      }
    },

    lineComment: lineCommentStartSymbol,
    blockCommentStart: "/*",
    blockCommentEnd: "*/"
  };
});

});


/***/ }),

/***/ "./node_modules/codemirror/mode/htmlmixed/htmlmixed.js":
/*!*************************************************************!*\
  !*** ./node_modules/codemirror/mode/htmlmixed/htmlmixed.js ***!
  \*************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE

(function(mod) {
  if (true) // CommonJS
    mod(__webpack_require__(/*! ../../lib/codemirror */ "./node_modules/codemirror/lib/codemirror.js"), __webpack_require__(/*! ../xml/xml */ "./node_modules/codemirror/mode/xml/xml.js"), __webpack_require__(/*! ../javascript/javascript */ "./node_modules/codemirror/mode/javascript/javascript.js"), __webpack_require__(/*! ../css/css */ "./node_modules/codemirror/mode/css/css.js"));
  else {}
})(function(CodeMirror) {
  "use strict";

  var defaultTags = {
    script: [
      ["lang", /(javascript|babel)/i, "javascript"],
      ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i, "javascript"],
      ["type", /./, "text/plain"],
      [null, null, "javascript"]
    ],
    style:  [
      ["lang", /^css$/i, "css"],
      ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"],
      ["type", /./, "text/plain"],
      [null, null, "css"]
    ]
  };

  function maybeBackup(stream, pat, style) {
    var cur = stream.current(), close = cur.search(pat);
    if (close > -1) {
      stream.backUp(cur.length - close);
    } else if (cur.match(/<\/?$/)) {
      stream.backUp(cur.length);
      if (!stream.match(pat, false)) stream.match(cur);
    }
    return style;
  }

  var attrRegexpCache = {};
  function getAttrRegexp(attr) {
    var regexp = attrRegexpCache[attr];
    if (regexp) return regexp;
    return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
  }

  function getAttrValue(text, attr) {
    var match = text.match(getAttrRegexp(attr))
    return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : ""
  }

  function getTagRegexp(tagName, anchored) {
    return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i");
  }

  function addTags(from, to) {
    for (var tag in from) {
      var dest = to[tag] || (to[tag] = []);
      var source = from[tag];
      for (var i = source.length - 1; i >= 0; i--)
        dest.unshift(source[i])
    }
  }

  function findMatchingMode(tagInfo, tagText) {
    for (var i = 0; i < tagInfo.length; i++) {
      var spec = tagInfo[i];
      if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
    }
  }

  CodeMirror.defineMode("htmlmixed", function (config, parserConfig) {
    var htmlMode = CodeMirror.getMode(config, {
      name: "xml",
      htmlMode: true,
      multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
      multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag,
      allowMissingTagName: parserConfig.allowMissingTagName,
    });

    var tags = {};
    var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;
    addTags(defaultTags, tags);
    if (configTags) addTags(configTags, tags);
    if (configScript) for (var i = configScript.length - 1; i >= 0; i--)
      tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])

    function html(stream, state) {
      var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
      if (tag && !/[<>\s\/]/.test(stream.current()) &&
          (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
          tags.hasOwnProperty(tagName)) {
        state.inTag = tagName + " "
      } else if (state.inTag && tag && />$/.test(stream.current())) {
        var inTag = /^([\S]+) (.*)/.exec(state.inTag)
        state.inTag = null
        var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
        var mode = CodeMirror.getMode(config, modeSpec)
        var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
        state.token = function (stream, state) {
          if (stream.match(endTagA, false)) {
            state.token = html;
            state.localState = state.localMode = null;
            return null;
          }
          return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
        };
        state.localMode = mode;
        state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, "", ""));
      } else if (state.inTag) {
        state.inTag += stream.current()
        if (stream.eol()) state.inTag += " "
      }
      return style;
    };

    return {
      startState: function () {
        var state = CodeMirror.startState(htmlMode);
        return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
      },

      copyState: function (state) {
        var local;
        if (state.localState) {
          local = CodeMirror.copyState(state.localMode, state.localState);
        }
        return {token: state.token, inTag: state.inTag,
                localMode: state.localMode, localState: local,
                htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
      },

      token: function (stream, state) {
        return state.token(stream, state);
      },

      indent: function (state, textAfter, line) {
        if (!state.localMode || /^\s*<\//.test(textAfter))
          return htmlMode.indent(state.htmlState, textAfter, line);
        else if (state.localMode.indent)
          return state.localMode.indent(state.localState, textAfter, line);
        else
          return CodeMirror.Pass;
      },

      innerMode: function (state) {
        return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
      }
    };
  }, "xml", "javascript", "css");

  CodeMirror.defineMIME("text/html", "htmlmixed");
});


/***/ }),

/***/ "./node_modules/codemirror/mode/javascript/javascript.js":
/*!***************************************************************!*\
  !*** ./node_modules/codemirror/mode/javascript/javascript.js ***!
  \***************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE

(function(mod) {
  if (true) // CommonJS
    mod(__webpack_require__(/*! ../../lib/codemirror */ "./node_modules/codemirror/lib/codemirror.js"));
  else {}
})(function(CodeMirror) {
"use strict";

CodeMirror.defineMode("javascript", function(config, parserConfig) {
  var indentUnit = config.indentUnit;
  var statementIndent = parserConfig.statementIndent;
  var jsonldMode = parserConfig.jsonld;
  var jsonMode = parserConfig.json || jsonldMode;
  var isTS = parserConfig.typescript;
  var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;

  // Tokenizer

  var keywords = function(){
    function kw(type) {return {type: type, style: "keyword"};}
    var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d");
    var operator = kw("operator"), atom = {type: "atom", style: "atom"};

    return {
      "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
      "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C,
      "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"),
      "function": kw("function"), "catch": kw("catch"),
      "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
      "in": operator, "typeof": operator, "instanceof": operator,
      "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
      "this": kw("this"), "class": kw("class"), "super": kw("atom"),
      "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
      "await": C
    };
  }();

  var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
  var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;

  function readRegexp(stream) {
    var escaped = false, next, inSet = false;
    while ((next = stream.next()) != null) {
      if (!escaped) {
        if (next == "/" && !inSet) return;
        if (next == "[") inSet = true;
        else if (inSet && next == "]") inSet = false;
      }
      escaped = !escaped && next == "\\";
    }
  }

  // Used as scratch variables to communicate multiple values without
  // consing up tons of objects.
  var type, content;
  function ret(tp, style, cont) {
    type = tp; content = cont;
    return style;
  }
  function tokenBase(stream, state) {
    var ch = stream.next();
    if (ch == '"' || ch == "'") {
      state.tokenize = tokenString(ch);
      return state.tokenize(stream, state);
    } else if (ch == "." && stream.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/)) {
      return ret("number", "number");
    } else if (ch == "." && stream.match("..")) {
      return ret("spread", "meta");
    } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
      return ret(ch);
    } else if (ch == "=" && stream.eat(">")) {
      return ret("=>", "operator");
    } else if (ch == "0" && stream.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/)) {
      return ret("number", "number");
    } else if (/\d/.test(ch)) {
      stream.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/);
      return ret("number", "number");
    } else if (ch == "/") {
      if (stream.eat("*")) {
        state.tokenize = tokenComment;
        return tokenComment(stream, state);
      } else if (stream.eat("/")) {
        stream.skipToEnd();
        return ret("comment", "comment");
      } else if (expressionAllowed(stream, state, 1)) {
        readRegexp(stream);
        stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/);
        return ret("regexp", "string-2");
      } else {
        stream.eat("=");
        return ret("operator", "operator", stream.current());
      }
    } else if (ch == "`") {
      state.tokenize = tokenQuasi;
      return tokenQuasi(stream, state);
    } else if (ch == "#" && stream.peek() == "!") {
      stream.skipToEnd();
      return ret("meta", "meta");
    } else if (ch == "#" && stream.eatWhile(wordRE)) {
      return ret("variable", "property")
    } else if (ch == "<" && stream.match("!--") ||
               (ch == "-" && stream.match("->") && !/\S/.test(stream.string.slice(0, stream.start)))) {
      stream.skipToEnd()
      return ret("comment", "comment")
    } else if (isOperatorChar.test(ch)) {
      if (ch != ">" || !state.lexical || state.lexical.type != ">") {
        if (stream.eat("=")) {
          if (ch == "!" || ch == "=") stream.eat("=")
        } else if (/[<>*+\-|&?]/.test(ch)) {
          stream.eat(ch)
          if (ch == ">") stream.eat(ch)
        }
      }
      if (ch == "?" && stream.eat(".")) return ret(".")
      return ret("operator", "operator", stream.current());
    } else if (wordRE.test(ch)) {
      stream.eatWhile(wordRE);
      var word = stream.current()
      if (state.lastType != ".") {
        if (keywords.propertyIsEnumerable(word)) {
          var kw = keywords[word]
          return ret(kw.type, kw.style, word)
        }
        if (word == "async" && stream.match(/^(\s|\/\*([^*]|\*(?!\/))*?\*\/)*[\[\(\w]/, false))
          return ret("async", "keyword", word)
      }
      return ret("variable", "variable", word)
    }
  }

  function tokenString(quote) {
    return function(stream, state) {
      var escaped = false, next;
      if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
        state.tokenize = tokenBase;
        return ret("jsonld-keyword", "meta");
      }
      while ((next = stream.next()) != null) {
        if (next == quote && !escaped) break;
        escaped = !escaped && next == "\\";
      }
      if (!escaped) state.tokenize = tokenBase;
      return ret("string", "string");
    };
  }

  function tokenComment(stream, state) {
    var maybeEnd = false, ch;
    while (ch = stream.next()) {
      if (ch == "/" && maybeEnd) {
        state.tokenize = tokenBase;
        break;
      }
      maybeEnd = (ch == "*");
    }
    return ret("comment", "comment");
  }

  function tokenQuasi(stream, state) {
    var escaped = false, next;
    while ((next = stream.next()) != null) {
      if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
        state.tokenize = tokenBase;
        break;
      }
      escaped = !escaped && next == "\\";
    }
    return ret("quasi", "string-2", stream.current());
  }

  var brackets = "([{}])";
  // This is a crude lookahead trick to try and notice that we're
  // parsing the argument patterns for a fat-arrow function before we
  // actually hit the arrow token. It only works if the arrow is on
  // the same line as the arguments and there's no strange noise
  // (comments) in between. Fallback is to only notice when we hit the
  // arrow, and not declare the arguments as locals for the arrow
  // body.
  function findFatArrow(stream, state) {
    if (state.fatArrowAt) state.fatArrowAt = null;
    var arrow = stream.string.indexOf("=>", stream.start);
    if (arrow < 0) return;

    if (isTS) { // Try to skip TypeScript return type declarations after the arguments
      var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow))
      if (m) arrow = m.index
    }

    var depth = 0, sawSomething = false;
    for (var pos = arrow - 1; pos >= 0; --pos) {
      var ch = stream.string.charAt(pos);
      var bracket = brackets.indexOf(ch);
      if (bracket >= 0 && bracket < 3) {
        if (!depth) { ++pos; break; }
        if (--depth == 0) { if (ch == "(") sawSomething = true; break; }
      } else if (bracket >= 3 && bracket < 6) {
        ++depth;
      } else if (wordRE.test(ch)) {
        sawSomething = true;
      } else if (/["'\/`]/.test(ch)) {
        for (;; --pos) {
          if (pos == 0) return
          var next = stream.string.charAt(pos - 1)
          if (next == ch && stream.string.charAt(pos - 2) != "\\") { pos--; break }
        }
      } else if (sawSomething && !depth) {
        ++pos;
        break;
      }
    }
    if (sawSomething && !depth) state.fatArrowAt = pos;
  }

  // Parser

  var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};

  function JSLexical(indented, column, type, align, prev, info) {
    this.indented = indented;
    this.column = column;
    this.type = type;
    this.prev = prev;
    this.info = info;
    if (align != null) this.align = align;
  }

  function inScope(state, varname) {
    for (var v = state.localVars; v; v = v.next)
      if (v.name == varname) return true;
    for (var cx = state.context; cx; cx = cx.prev) {
      for (var v = cx.vars; v; v = v.next)
        if (v.name == varname) return true;
    }
  }

  function parseJS(state, style, type, content, stream) {
    var cc = state.cc;
    // Communicate our context to the combinators.
    // (Less wasteful than consing up a hundred closures on every call.)
    cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;

    if (!state.lexical.hasOwnProperty("align"))
      state.lexical.align = true;

    while(true) {
      var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
      if (combinator(type, content)) {
        while(cc.length && cc[cc.length - 1].lex)
          cc.pop()();
        if (cx.marked) return cx.marked;
        if (type == "variable" && inScope(state, content)) return "variable-2";
        return style;
      }
    }
  }

  // Combinator utils

  var cx = {state: null, column: null, marked: null, cc: null};
  function pass() {
    for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
  }
  function cont() {
    pass.apply(null, arguments);
    return true;
  }
  function inList(name, list) {
    for (var v = list; v; v = v.next) if (v.name == name) return true
    return false;
  }
  function register(varname) {
    var state = cx.state;
    cx.marked = "def";
    if (state.context) {
      if (state.lexical.info == "var" && state.context && state.context.block) {
        // FIXME function decls are also not block scoped
        var newContext = registerVarScoped(varname, state.context)
        if (newContext != null) {
          state.context = newContext
          return
        }
      } else if (!inList(varname, state.localVars)) {
        state.localVars = new Var(varname, state.localVars)
        return
      }
    }
    // Fall through means this is global
    if (parserConfig.globalVars && !inList(varname, state.globalVars))
      state.globalVars = new Var(varname, state.globalVars)
  }
  function registerVarScoped(varname, context) {
    if (!context) {
      return null
    } else if (context.block) {
      var inner = registerVarScoped(varname, context.prev)
      if (!inner) return null
      if (inner == context.prev) return context
      return new Context(inner, context.vars, true)
    } else if (inList(varname, context.vars)) {
      return context
    } else {
      return new Context(context.prev, new Var(varname, context.vars), false)
    }
  }

  function isModifier(name) {
    return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly"
  }

  // Combinators

  function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block }
  function Var(name, next) { this.name = name; this.next = next }

  var defaultVars = new Var("this", new Var("arguments", null))
  function pushcontext() {
    cx.state.context = new Context(cx.state.context, cx.state.localVars, false)
    cx.state.localVars = defaultVars
  }
  function pushblockcontext() {
    cx.state.context = new Context(cx.state.context, cx.state.localVars, true)
    cx.state.localVars = null
  }
  function popcontext() {
    cx.state.localVars = cx.state.context.vars
    cx.state.context = cx.state.context.prev
  }
  popcontext.lex = true
  function pushlex(type, info) {
    var result = function() {
      var state = cx.state, indent = state.indented;
      if (state.lexical.type == "stat") indent = state.lexical.indented;
      else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
        indent = outer.indented;
      state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
    };
    result.lex = true;
    return result;
  }
  function poplex() {
    var state = cx.state;
    if (state.lexical.prev) {
      if (state.lexical.type == ")")
        state.indented = state.lexical.indented;
      state.lexical = state.lexical.prev;
    }
  }
  poplex.lex = true;

  function expect(wanted) {
    function exp(type) {
      if (type == wanted) return cont();
      else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass();
      else return cont(exp);
    };
    return exp;
  }

  function statement(type, value) {
    if (type == "var") return cont(pushlex("vardef", value), vardef, expect(";"), poplex);
    if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
    if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
    if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex);
    if (type == "debugger") return cont(expect(";"));
    if (type == "{") return cont(pushlex("}"), pushblockcontext, block, poplex, popcontext);
    if (type == ";") return cont();
    if (type == "if") {
      if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
        cx.state.cc.pop()();
      return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
    }
    if (type == "function") return cont(functiondef);
    if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
    if (type == "class" || (isTS && value == "interface")) {
      cx.marked = "keyword"
      return cont(pushlex("form", type == "class" ? type : value), className, poplex)
    }
    if (type == "variable") {
      if (isTS && value == "declare") {
        cx.marked = "keyword"
        return cont(statement)
      } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) {
        cx.marked = "keyword"
        if (value == "enum") return cont(enumdef);
        else if (value == "type") return cont(typename, expect("operator"), typeexpr, expect(";"));
        else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
      } else if (isTS && value == "namespace") {
        cx.marked = "keyword"
        return cont(pushlex("form"), expression, statement, poplex)
      } else if (isTS && value == "abstract") {
        cx.marked = "keyword"
        return cont(statement)
      } else {
        return cont(pushlex("stat"), maybelabel);
      }
    }
    if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), pushblockcontext,
                                      block, poplex, poplex, popcontext);
    if (type == "case") return cont(expression, expect(":"));
    if (type == "default") return cont(expect(":"));
    if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext);
    if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
    if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
    if (type == "async") return cont(statement)
    if (value == "@") return cont(expression, statement)
    return pass(pushlex("stat"), expression, expect(";"), poplex);
  }
  function maybeCatchBinding(type) {
    if (type == "(") return cont(funarg, expect(")"))
  }
  function expression(type, value) {
    return expressionInner(type, value, false);
  }
  function expressionNoComma(type, value) {
    return expressionInner(type, value, true);
  }
  function parenExpr(type) {
    if (type != "(") return pass()
    return cont(pushlex(")"), maybeexpression, expect(")"), poplex)
  }
  function expressionInner(type, value, noComma) {
    if (cx.state.fatArrowAt == cx.stream.start) {
      var body = noComma ? arrowBodyNoComma : arrowBody;
      if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
      else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
    }

    var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
    if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
    if (type == "function") return cont(functiondef, maybeop);
    if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
    if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
    if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
    if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
    if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
    if (type == "{") return contCommasep(objprop, "}", null, maybeop);
    if (type == "quasi") return pass(quasi, maybeop);
    if (type == "new") return cont(maybeTarget(noComma));
    if (type == "import") return cont(expression);
    return cont();
  }
  function maybeexpression(type) {
    if (type.match(/[;\}\)\],]/)) return pass();
    return pass(expression);
  }

  function maybeoperatorComma(type, value) {
    if (type == ",") return cont(maybeexpression);
    return maybeoperatorNoComma(type, value, false);
  }
  function maybeoperatorNoComma(type, value, noComma) {
    var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
    var expr = noComma == false ? expression : expressionNoComma;
    if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
    if (type == "operator") {
      if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me);
      if (isTS && value == "<" && cx.stream.match(/^([^<>]|<[^<>]*>)*>\s*\(/, false))
        return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me);
      if (value == "?") return cont(expression, expect(":"), expr);
      return cont(expr);
    }
    if (type == "quasi") { return pass(quasi, me); }
    if (type == ";") return;
    if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
    if (type == ".") return cont(property, me);
    if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
    if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
    if (type == "regexp") {
      cx.state.lastType = cx.marked = "operator"
      cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)
      return cont(expr)
    }
  }
  function quasi(type, value) {
    if (type != "quasi") return pass();
    if (value.slice(value.length - 2) != "${") return cont(quasi);
    return cont(expression, continueQuasi);
  }
  function continueQuasi(type) {
    if (type == "}") {
      cx.marked = "string-2";
      cx.state.tokenize = tokenQuasi;
      return cont(quasi);
    }
  }
  function arrowBody(type) {
    findFatArrow(cx.stream, cx.state);
    return pass(type == "{" ? statement : expression);
  }
  function arrowBodyNoComma(type) {
    findFatArrow(cx.stream, cx.state);
    return pass(type == "{" ? statement : expressionNoComma);
  }
  function maybeTarget(noComma) {
    return function(type) {
      if (type == ".") return cont(noComma ? targetNoComma : target);
      else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma)
      else return pass(noComma ? expressionNoComma : expression);
    };
  }
  function target(_, value) {
    if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
  }
  function targetNoComma(_, value) {
    if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
  }
  function maybelabel(type) {
    if (type == ":") return cont(poplex, statement);
    return pass(maybeoperatorComma, expect(";"), poplex);
  }
  function property(type) {
    if (type == "variable") {cx.marked = "property"; return cont();}
  }
  function objprop(type, value) {
    if (type == "async") {
      cx.marked = "property";
      return cont(objprop);
    } else if (type == "variable" || cx.style == "keyword") {
      cx.marked = "property";
      if (value == "get" || value == "set") return cont(getterSetter);
      var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params
      if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
        cx.state.fatArrowAt = cx.stream.pos + m[0].length
      return cont(afterprop);
    } else if (type == "number" || type == "string") {
      cx.marked = jsonldMode ? "property" : (cx.style + " property");
      return cont(afterprop);
    } else if (type == "jsonld-keyword") {
      return cont(afterprop);
    } else if (isTS && isModifier(value)) {
      cx.marked = "keyword"
      return cont(objprop)
    } else if (type == "[") {
      return cont(expression, maybetype, expect("]"), afterprop);
    } else if (type == "spread") {
      return cont(expressionNoComma, afterprop);
    } else if (value == "*") {
      cx.marked = "keyword";
      return cont(objprop);
    } else if (type == ":") {
      return pass(afterprop)
    }
  }
  function getterSetter(type) {
    if (type != "variable") return pass(afterprop);
    cx.marked = "property";
    return cont(functiondef);
  }
  function afterprop(type) {
    if (type == ":") return cont(expressionNoComma);
    if (type == "(") return pass(functiondef);
  }
  function commasep(what, end, sep) {
    function proceed(type, value) {
      if (sep ? sep.indexOf(type) > -1 : type == ",") {
        var lex = cx.state.lexical;
        if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
        return cont(function(type, value) {
          if (type == end || value == end) return pass()
          return pass(what)
        }, proceed);
      }
      if (type == end || value == end) return cont();
      if (sep && sep.indexOf(";") > -1) return pass(what)
      return cont(expect(end));
    }
    return function(type, value) {
      if (type == end || value == end) return cont();
      return pass(what, proceed);
    };
  }
  function contCommasep(what, end, info) {
    for (var i = 3; i < arguments.length; i++)
      cx.cc.push(arguments[i]);
    return cont(pushlex(end, info), commasep(what, end), poplex);
  }
  function block(type) {
    if (type == "}") return cont();
    return pass(statement, block);
  }
  function maybetype(type, value) {
    if (isTS) {
      if (type == ":") return cont(typeexpr);
      if (value == "?") return cont(maybetype);
    }
  }
  function maybetypeOrIn(type, value) {
    if (isTS && (type == ":" || value == "in")) return cont(typeexpr)
  }
  function mayberettype(type) {
    if (isTS && type == ":") {
      if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr)
      else return cont(typeexpr)
    }
  }
  function isKW(_, value) {
    if (value == "is") {
      cx.marked = "keyword"
      return cont()
    }
  }
  function typeexpr(type, value) {
    if (value == "keyof" || value == "typeof" || value == "infer") {
      cx.marked = "keyword"
      return cont(value == "typeof" ? expressionNoComma : typeexpr)
    }
    if (type == "variable" || value == "void") {
      cx.marked = "type"
      return cont(afterType)
    }
    if (value == "|" || value == "&") return cont(typeexpr)
    if (type == "string" || type == "number" || type == "atom") return cont(afterType);
    if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType)
    if (type == "{") return cont(pushlex("}"), typeprops, poplex, afterType)
    if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType, afterType)
    if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr)
  }
  function maybeReturnType(type) {
    if (type == "=>") return cont(typeexpr)
  }
  function typeprops(type) {
    if (type.match(/[\}\)\]]/)) return cont()
    if (type == "," || type == ";") return cont(typeprops)
    return pass(typeprop, typeprops)
  }
  function typeprop(type, value) {
    if (type == "variable" || cx.style == "keyword") {
      cx.marked = "property"
      return cont(typeprop)
    } else if (value == "?" || type == "number" || type == "string") {
      return cont(typeprop)
    } else if (type == ":") {
      return cont(typeexpr)
    } else if (type == "[") {
      return cont(expect("variable"), maybetypeOrIn, expect("]"), typeprop)
    } else if (type == "(") {
      return pass(functiondecl, typeprop)
    } else if (!type.match(/[;\}\)\],]/)) {
      return cont()
    }
  }
  function typearg(type, value) {
    if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg)
    if (type == ":") return cont(typeexpr)
    if (type == "spread") return cont(typearg)
    return pass(typeexpr)
  }
  function afterType(type, value) {
    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
    if (value == "|" || type == "." || value == "&") return cont(typeexpr)
    if (type == "[") return cont(typeexpr, expect("]"), afterType)
    if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) }
    if (value == "?") return cont(typeexpr, expect(":"), typeexpr)
  }
  function maybeTypeArgs(_, value) {
    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
  }
  function typeparam() {
    return pass(typeexpr, maybeTypeDefault)
  }
  function maybeTypeDefault(_, value) {
    if (value == "=") return cont(typeexpr)
  }
  function vardef(_, value) {
    if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)}
    return pass(pattern, maybetype, maybeAssign, vardefCont);
  }
  function pattern(type, value) {
    if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) }
    if (type == "variable") { register(value); return cont(); }
    if (type == "spread") return cont(pattern);
    if (type == "[") return contCommasep(eltpattern, "]");
    if (type == "{") return contCommasep(proppattern, "}");
  }
  function proppattern(type, value) {
    if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
      register(value);
      return cont(maybeAssign);
    }
    if (type == "variable") cx.marked = "property";
    if (type == "spread") return cont(pattern);
    if (type == "}") return pass();
    if (type == "[") return cont(expression, expect(']'), expect(':'), proppattern);
    return cont(expect(":"), pattern, maybeAssign);
  }
  function eltpattern() {
    return pass(pattern, maybeAssign)
  }
  function maybeAssign(_type, value) {
    if (value == "=") return cont(expressionNoComma);
  }
  function vardefCont(type) {
    if (type == ",") return cont(vardef);
  }
  function maybeelse(type, value) {
    if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
  }
  function forspec(type, value) {
    if (value == "await") return cont(forspec);
    if (type == "(") return cont(pushlex(")"), forspec1, poplex);
  }
  function forspec1(type) {
    if (type == "var") return cont(vardef, forspec2);
    if (type == "variable") return cont(forspec2);
    return pass(forspec2)
  }
  function forspec2(type, value) {
    if (type == ")") return cont()
    if (type == ";") return cont(forspec2)
    if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression, forspec2) }
    return pass(expression, forspec2)
  }
  function functiondef(type, value) {
    if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
    if (type == "variable") {register(value); return cont(functiondef);}
    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext);
    if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
  }
  function functiondecl(type, value) {
    if (value == "*") {cx.marked = "keyword"; return cont(functiondecl);}
    if (type == "variable") {register(value); return cont(functiondecl);}
    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, popcontext);
    if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondecl)
  }
  function typename(type, value) {
    if (type == "keyword" || type == "variable") {
      cx.marked = "type"
      return cont(typename)
    } else if (value == "<") {
      return cont(pushlex(">"), commasep(typeparam, ">"), poplex)
    }
  }
  function funarg(type, value) {
    if (value == "@") cont(expression, funarg)
    if (type == "spread") return cont(funarg);
    if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); }
    if (isTS && type == "this") return cont(maybetype, maybeAssign)
    return pass(pattern, maybetype, maybeAssign);
  }
  function classExpression(type, value) {
    // Class expressions may have an optional name.
    if (type == "variable") return className(type, value);
    return classNameAfter(type, value);
  }
  function className(type, value) {
    if (type == "variable") {register(value); return cont(classNameAfter);}
  }
  function classNameAfter(type, value) {
    if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
    if (value == "extends" || value == "implements" || (isTS && type == ",")) {
      if (value == "implements") cx.marked = "keyword";
      return cont(isTS ? typeexpr : expression, classNameAfter);
    }
    if (type == "{") return cont(pushlex("}"), classBody, poplex);
  }
  function classBody(type, value) {
    if (type == "async" ||
        (type == "variable" &&
         (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
         cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
      cx.marked = "keyword";
      return cont(classBody);
    }
    if (type == "variable" || cx.style == "keyword") {
      cx.marked = "property";
      return cont(classfield, classBody);
    }
    if (type == "number" || type == "string") return cont(classfield, classBody);
    if (type == "[")
      return cont(expression, maybetype, expect("]"), classfield, classBody)
    if (value == "*") {
      cx.marked = "keyword";
      return cont(classBody);
    }
    if (isTS && type == "(") return pass(functiondecl, classBody)
    if (type == ";" || type == ",") return cont(classBody);
    if (type == "}") return cont();
    if (value == "@") return cont(expression, classBody)
  }
  function classfield(type, value) {
    if (value == "?") return cont(classfield)
    if (type == ":") return cont(typeexpr, maybeAssign)
    if (value == "=") return cont(expressionNoComma)
    var context = cx.state.lexical.prev, isInterface = context && context.info == "interface"
    return pass(isInterface ? functiondecl : functiondef)
  }
  function afterExport(type, value) {
    if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
    if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
    if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";"));
    return pass(statement);
  }
  function exportField(type, value) {
    if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); }
    if (type == "variable") return pass(expressionNoComma, exportField);
  }
  function afterImport(type) {
    if (type == "string") return cont();
    if (type == "(") return pass(expression);
    return pass(importSpec, maybeMoreImports, maybeFrom);
  }
  function importSpec(type, value) {
    if (type == "{") return contCommasep(importSpec, "}");
    if (type == "variable") register(value);
    if (value == "*") cx.marked = "keyword";
    return cont(maybeAs);
  }
  function maybeMoreImports(type) {
    if (type == ",") return cont(importSpec, maybeMoreImports)
  }
  function maybeAs(_type, value) {
    if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
  }
  function maybeFrom(_type, value) {
    if (value == "from") { cx.marked = "keyword"; return cont(expression); }
  }
  function arrayLiteral(type) {
    if (type == "]") return cont();
    return pass(commasep(expressionNoComma, "]"));
  }
  function enumdef() {
    return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex)
  }
  function enummember() {
    return pass(pattern, maybeAssign);
  }

  function isContinuedStatement(state, textAfter) {
    return state.lastType == "operator" || state.lastType == "," ||
      isOperatorChar.test(textAfter.charAt(0)) ||
      /[,.]/.test(textAfter.charAt(0));
  }

  function expressionAllowed(stream, state, backUp) {
    return state.tokenize == tokenBase &&
      /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
      (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
  }

  // Interface

  return {
    startState: function(basecolumn) {
      var state = {
        tokenize: tokenBase,
        lastType: "sof",
        cc: [],
        lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
        localVars: parserConfig.localVars,
        context: parserConfig.localVars && new Context(null, null, false),
        indented: basecolumn || 0
      };
      if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
        state.globalVars = parserConfig.globalVars;
      return state;
    },

    token: function(stream, state) {
      if (stream.sol()) {
        if (!state.lexical.hasOwnProperty("align"))
          state.lexical.align = false;
        state.indented = stream.indentation();
        findFatArrow(stream, state);
      }
      if (state.tokenize != tokenComment && stream.eatSpace()) return null;
      var style = state.tokenize(stream, state);
      if (type == "comment") return style;
      state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
      return parseJS(state, style, type, content, stream);
    },

    indent: function(state, textAfter) {
      if (state.tokenize == tokenComment || state.tokenize == tokenQuasi) return CodeMirror.Pass;
      if (state.tokenize != tokenBase) return 0;
      var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top
      // Kludge to prevent 'maybelse' from blocking lexical scope pops
      if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
        var c = state.cc[i];
        if (c == poplex) lexical = lexical.prev;
        else if (c != maybeelse) break;
      }
      while ((lexical.type == "stat" || lexical.type == "form") &&
             (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) &&
                                   (top == maybeoperatorComma || top == maybeoperatorNoComma) &&
                                   !/^[,\.=+\-*:?[\(]/.test(textAfter))))
        lexical = lexical.prev;
      if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
        lexical = lexical.prev;
      var type = lexical.type, closing = firstChar == type;

      if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info.length + 1 : 0);
      else if (type == "form" && firstChar == "{") return lexical.indented;
      else if (type == "form") return lexical.indented + indentUnit;
      else if (type == "stat")
        return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
      else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
        return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
      else if (lexical.align) return lexical.column + (closing ? 0 : 1);
      else return lexical.indented + (closing ? 0 : indentUnit);
    },

    electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
    blockCommentStart: jsonMode ? null : "/*",
    blockCommentEnd: jsonMode ? null : "*/",
    blockCommentContinue: jsonMode ? null : " * ",
    lineComment: jsonMode ? null : "//",
    fold: "brace",
    closeBrackets: "()[]{}''\"\"``",

    helperType: jsonMode ? "json" : "javascript",
    jsonldMode: jsonldMode,
    jsonMode: jsonMode,

    expressionAllowed: expressionAllowed,

    skipExpression: function(state) {
      var top = state.cc[state.cc.length - 1]
      if (top == expression || top == expressionNoComma) state.cc.pop()
    }
  };
});

CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);

CodeMirror.defineMIME("text/javascript", "javascript");
CodeMirror.defineMIME("text/ecmascript", "javascript");
CodeMirror.defineMIME("application/javascript", "javascript");
CodeMirror.defineMIME("application/x-javascript", "javascript");
CodeMirror.defineMIME("application/ecmascript", "javascript");
CodeMirror.defineMIME("application/json", { name: "javascript", json: true });
CodeMirror.defineMIME("application/x-json", { name: "javascript", json: true });
CodeMirror.defineMIME("application/manifest+json", { name: "javascript", json: true })
CodeMirror.defineMIME("application/ld+json", { name: "javascript", jsonld: true });
CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });

});


/***/ }),

/***/ "./node_modules/codemirror/mode/octave/octave.js":
/*!*******************************************************!*\
  !*** ./node_modules/codemirror/mode/octave/octave.js ***!
  \*******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE

(function(mod) {
  if (true) // CommonJS
    mod(__webpack_require__(/*! ../../lib/codemirror */ "./node_modules/codemirror/lib/codemirror.js"));
  else {}
})(function(CodeMirror) {
"use strict";

CodeMirror.defineMode("octave", function() {
  function wordRegexp(words) {
    return new RegExp("^((" + words.join(")|(") + "))\\b");
  }

  var singleOperators = new RegExp("^[\\+\\-\\*/&|\\^~<>!@'\\\\]");
  var singleDelimiters = new RegExp('^[\\(\\[\\{\\},:=;\\.]');
  var doubleOperators = new RegExp("^((==)|(~=)|(<=)|(>=)|(<<)|(>>)|(\\.[\\+\\-\\*/\\^\\\\]))");
  var doubleDelimiters = new RegExp("^((!=)|(\\+=)|(\\-=)|(\\*=)|(/=)|(&=)|(\\|=)|(\\^=))");
  var tripleDelimiters = new RegExp("^((>>=)|(<<=))");
  var expressionEnd = new RegExp("^[\\]\\)]");
  var identifiers = new RegExp("^[_A-Za-z\xa1-\uffff][_A-Za-z0-9\xa1-\uffff]*");

  var builtins = wordRegexp([
    'error', 'eval', 'function', 'abs', 'acos', 'atan', 'asin', 'cos',
    'cosh', 'exp', 'log', 'prod', 'sum', 'log10', 'max', 'min', 'sign', 'sin', 'sinh',
    'sqrt', 'tan', 'reshape', 'break', 'zeros', 'default', 'margin', 'round', 'ones',
    'rand', 'syn', 'ceil', 'floor', 'size', 'clear', 'zeros', 'eye', 'mean', 'std', 'cov',
    'det', 'eig', 'inv', 'norm', 'rank', 'trace', 'expm', 'logm', 'sqrtm', 'linspace', 'plot',
    'title', 'xlabel', 'ylabel', 'legend', 'text', 'grid', 'meshgrid', 'mesh', 'num2str',
    'fft', 'ifft', 'arrayfun', 'cellfun', 'input', 'fliplr', 'flipud', 'ismember'
  ]);

  var keywords = wordRegexp([
    'return', 'case', 'switch', 'else', 'elseif', 'end', 'endif', 'endfunction',
    'if', 'otherwise', 'do', 'for', 'while', 'try', 'catch', 'classdef', 'properties', 'events',
    'methods', 'global', 'persistent', 'endfor', 'endwhile', 'printf', 'sprintf', 'disp', 'until',
    'continue', 'pkg'
  ]);


  // tokenizers
  function tokenTranspose(stream, state) {
    if (!stream.sol() && stream.peek() === '\'') {
      stream.next();
      state.tokenize = tokenBase;
      return 'operator';
    }
    state.tokenize = tokenBase;
    return tokenBase(stream, state);
  }


  function tokenComment(stream, state) {
    if (stream.match(/^.*%}/)) {
      state.tokenize = tokenBase;
      return 'comment';
    };
    stream.skipToEnd();
    return 'comment';
  }

  function tokenBase(stream, state) {
    // whitespaces
    if (stream.eatSpace()) return null;

    // Handle one line Comments
    if (stream.match('%{')){
      state.tokenize = tokenComment;
      stream.skipToEnd();
      return 'comment';
    }

    if (stream.match(/^[%#]/)){
      stream.skipToEnd();
      return 'comment';
    }

    // Handle Number Literals
    if (stream.match(/^[0-9\.+-]/, false)) {
      if (stream.match(/^[+-]?0x[0-9a-fA-F]+[ij]?/)) {
        stream.tokenize = tokenBase;
        return 'number'; };
      if (stream.match(/^[+-]?\d*\.\d+([EeDd][+-]?\d+)?[ij]?/)) { return 'number'; };
      if (stream.match(/^[+-]?\d+([EeDd][+-]?\d+)?[ij]?/)) { return 'number'; };
    }
    if (stream.match(wordRegexp(['nan','NaN','inf','Inf']))) { return 'number'; };

    // Handle Strings
    var m = stream.match(/^"(?:[^"]|"")*("|$)/) || stream.match(/^'(?:[^']|'')*('|$)/)
    if (m) { return m[1] ? 'string' : "string error"; }

    // Handle words
    if (stream.match(keywords)) { return 'keyword'; } ;
    if (stream.match(builtins)) { return 'builtin'; } ;
    if (stream.match(identifiers)) { return 'variable'; } ;

    if (stream.match(singleOperators) || stream.match(doubleOperators)) { return 'operator'; };
    if (stream.match(singleDelimiters) || stream.match(doubleDelimiters) || stream.match(tripleDelimiters)) { return null; };

    if (stream.match(expressionEnd)) {
      state.tokenize = tokenTranspose;
      return null;
    };


    // Handle non-detected items
    stream.next();
    return 'error';
  };


  return {
    startState: function() {
      return {
        tokenize: tokenBase
      };
    },

    token: function(stream, state) {
      var style = state.tokenize(stream, state);
      if (style === 'number' || style === 'variable'){
        state.tokenize = tokenTranspose;
      }
      return style;
    },

    lineComment: '%',

    fold: 'indent'
  };
});

CodeMirror.defineMIME("text/x-octave", "octave");

});


/***/ }),

/***/ "./node_modules/codemirror/mode/python/python.js":
/*!*******************************************************!*\
  !*** ./node_modules/codemirror/mode/python/python.js ***!
  \*******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE

(function(mod) {
  if (true) // CommonJS
    mod(__webpack_require__(/*! ../../lib/codemirror */ "./node_modules/codemirror/lib/codemirror.js"));
  else {}
})(function(CodeMirror) {
  "use strict";

  function wordRegexp(words) {
    return new RegExp("^((" + words.join(")|(") + "))\\b");
  }

  var wordOperators = wordRegexp(["and", "or", "not", "is"]);
  var commonKeywords = ["as", "assert", "break", "class", "continue",
                        "def", "del", "elif", "else", "except", "finally",
                        "for", "from", "global", "if", "import",
                        "lambda", "pass", "raise", "return",
                        "try", "while", "with", "yield", "in"];
  var commonBuiltins = ["abs", "all", "any", "bin", "bool", "bytearray", "callable", "chr",
                        "classmethod", "compile", "complex", "delattr", "dict", "dir", "divmod",
                        "enumerate", "eval", "filter", "float", "format", "frozenset",
                        "getattr", "globals", "hasattr", "hash", "help", "hex", "id",
                        "input", "int", "isinstance", "issubclass", "iter", "len",
                        "list", "locals", "map", "max", "memoryview", "min", "next",
                        "object", "oct", "open", "ord", "pow", "property", "range",
                        "repr", "reversed", "round", "set", "setattr", "slice",
                        "sorted", "staticmethod", "str", "sum", "super", "tuple",
                        "type", "vars", "zip", "__import__", "NotImplemented",
                        "Ellipsis", "__debug__"];
  CodeMirror.registerHelper("hintWords", "python", commonKeywords.concat(commonBuiltins));

  function top(state) {
    return state.scopes[state.scopes.length - 1];
  }

  CodeMirror.defineMode("python", function(conf, parserConf) {
    var ERRORCLASS = "error";

    var delimiters = parserConf.delimiters || parserConf.singleDelimiters || /^[\(\)\[\]\{\}@,:`=;\.\\]/;
    //               (Backwards-compatibility with old, cumbersome config system)
    var operators = [parserConf.singleOperators, parserConf.doubleOperators, parserConf.doubleDelimiters, parserConf.tripleDelimiters,
                     parserConf.operators || /^([-+*/%\/&|^]=?|[<>=]+|\/\/=?|\*\*=?|!=|[~!@]|\.\.\.)/]
    for (var i = 0; i < operators.length; i++) if (!operators[i]) operators.splice(i--, 1)

    var hangingIndent = parserConf.hangingIndent || conf.indentUnit;

    var myKeywords = commonKeywords, myBuiltins = commonBuiltins;
    if (parserConf.extra_keywords != undefined)
      myKeywords = myKeywords.concat(parserConf.extra_keywords);

    if (parserConf.extra_builtins != undefined)
      myBuiltins = myBuiltins.concat(parserConf.extra_builtins);

    var py3 = !(parserConf.version && Number(parserConf.version) < 3)
    if (py3) {
      // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator
      var identifiers = parserConf.identifiers|| /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*/;
      myKeywords = myKeywords.concat(["nonlocal", "False", "True", "None", "async", "await"]);
      myBuiltins = myBuiltins.concat(["ascii", "bytes", "exec", "print"]);
      var stringPrefixes = new RegExp("^(([rbuf]|(br)|(fr))?('{3}|\"{3}|['\"]))", "i");
    } else {
      var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*/;
      myKeywords = myKeywords.concat(["exec", "print"]);
      myBuiltins = myBuiltins.concat(["apply", "basestring", "buffer", "cmp", "coerce", "execfile",
                                      "file", "intern", "long", "raw_input", "reduce", "reload",
                                      "unichr", "unicode", "xrange", "False", "True", "None"]);
      var stringPrefixes = new RegExp("^(([rubf]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
    }
    var keywords = wordRegexp(myKeywords);
    var builtins = wordRegexp(myBuiltins);

    // tokenizers
    function tokenBase(stream, state) {
      var sol = stream.sol() && state.lastToken != "\\"
      if (sol) state.indent = stream.indentation()
      // Handle scope changes
      if (sol && top(state).type == "py") {
        var scopeOffset = top(state).offset;
        if (stream.eatSpace()) {
          var lineOffset = stream.indentation();
          if (lineOffset > scopeOffset)
            pushPyScope(state);
          else if (lineOffset < scopeOffset && dedent(stream, state) && stream.peek() != "#")
            state.errorToken = true;
          return null;
        } else {
          var style = tokenBaseInner(stream, state);
          if (scopeOffset > 0 && dedent(stream, state))
            style += " " + ERRORCLASS;
          return style;
        }
      }
      return tokenBaseInner(stream, state);
    }

    function tokenBaseInner(stream, state, inFormat) {
      if (stream.eatSpace()) return null;

      // Handle Comments
      if (!inFormat && stream.match(/^#.*/)) return "comment";

      // Handle Number Literals
      if (stream.match(/^[0-9\.]/, false)) {
        var floatLiteral = false;
        // Floats
        if (stream.match(/^[\d_]*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; }
        if (stream.match(/^[\d_]+\.\d*/)) { floatLiteral = true; }
        if (stream.match(/^\.\d+/)) { floatLiteral = true; }
        if (floatLiteral) {
          // Float literals may be "imaginary"
          stream.eat(/J/i);
          return "number";
        }
        // Integers
        var intLiteral = false;
        // Hex
        if (stream.match(/^0x[0-9a-f_]+/i)) intLiteral = true;
        // Binary
        if (stream.match(/^0b[01_]+/i)) intLiteral = true;
        // Octal
        if (stream.match(/^0o[0-7_]+/i)) intLiteral = true;
        // Decimal
        if (stream.match(/^[1-9][\d_]*(e[\+\-]?[\d_]+)?/)) {
          // Decimal literals may be "imaginary"
          stream.eat(/J/i);
          // TODO - Can you have imaginary longs?
          intLiteral = true;
        }
        // Zero by itself with no other piece of number.
        if (stream.match(/^0(?![\dx])/i)) intLiteral = true;
        if (intLiteral) {
          // Integer literals may be "long"
          stream.eat(/L/i);
          return "number";
        }
      }

      // Handle Strings
      if (stream.match(stringPrefixes)) {
        var isFmtString = stream.current().toLowerCase().indexOf('f') !== -1;
        if (!isFmtString) {
          state.tokenize = tokenStringFactory(stream.current(), state.tokenize);
          return state.tokenize(stream, state);
        } else {
          state.tokenize = formatStringFactory(stream.current(), state.tokenize);
          return state.tokenize(stream, state);
        }
      }

      for (var i = 0; i < operators.length; i++)
        if (stream.match(operators[i])) return "operator"

      if (stream.match(delimiters)) return "punctuation";

      if (state.lastToken == "." && stream.match(identifiers))
        return "property";

      if (stream.match(keywords) || stream.match(wordOperators))
        return "keyword";

      if (stream.match(builtins))
        return "builtin";

      if (stream.match(/^(self|cls)\b/))
        return "variable-2";

      if (stream.match(identifiers)) {
        if (state.lastToken == "def" || state.lastToken == "class")
          return "def";
        return "variable";
      }

      // Handle non-detected items
      stream.next();
      return inFormat ? null :ERRORCLASS;
    }

    function formatStringFactory(delimiter, tokenOuter) {
      while ("rubf".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
        delimiter = delimiter.substr(1);

      var singleline = delimiter.length == 1;
      var OUTCLASS = "string";

      function tokenNestedExpr(depth) {
        return function(stream, state) {
          var inner = tokenBaseInner(stream, state, true)
          if (inner == "punctuation") {
            if (stream.current() == "{") {
              state.tokenize = tokenNestedExpr(depth + 1)
            } else if (stream.current() == "}") {
              if (depth > 1) state.tokenize = tokenNestedExpr(depth - 1)
              else state.tokenize = tokenString
            }
          }
          return inner
        }
      }

      function tokenString(stream, state) {
        while (!stream.eol()) {
          stream.eatWhile(/[^'"\{\}\\]/);
          if (stream.eat("\\")) {
            stream.next();
            if (singleline && stream.eol())
              return OUTCLASS;
          } else if (stream.match(delimiter)) {
            state.tokenize = tokenOuter;
            return OUTCLASS;
          } else if (stream.match('{{')) {
            // ignore {{ in f-str
            return OUTCLASS;
          } else if (stream.match('{', false)) {
            // switch to nested mode
            state.tokenize = tokenNestedExpr(0)
            if (stream.current()) return OUTCLASS;
            else return state.tokenize(stream, state)
          } else if (stream.match('}}')) {
            return OUTCLASS;
          } else if (stream.match('}')) {
            // single } in f-string is an error
            return ERRORCLASS;
          } else {
            stream.eat(/['"]/);
          }
        }
        if (singleline) {
          if (parserConf.singleLineStringErrors)
            return ERRORCLASS;
          else
            state.tokenize = tokenOuter;
        }
        return OUTCLASS;
      }
      tokenString.isString = true;
      return tokenString;
    }

    function tokenStringFactory(delimiter, tokenOuter) {
      while ("rubf".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
        delimiter = delimiter.substr(1);

      var singleline = delimiter.length == 1;
      var OUTCLASS = "string";

      function tokenString(stream, state) {
        while (!stream.eol()) {
          stream.eatWhile(/[^'"\\]/);
          if (stream.eat("\\")) {
            stream.next();
            if (singleline && stream.eol())
              return OUTCLASS;
          } else if (stream.match(delimiter)) {
            state.tokenize = tokenOuter;
            return OUTCLASS;
          } else {
            stream.eat(/['"]/);
          }
        }
        if (singleline) {
          if (parserConf.singleLineStringErrors)
            return ERRORCLASS;
          else
            state.tokenize = tokenOuter;
        }
        return OUTCLASS;
      }
      tokenString.isString = true;
      return tokenString;
    }

    function pushPyScope(state) {
      while (top(state).type != "py") state.scopes.pop()
      state.scopes.push({offset: top(state).offset + conf.indentUnit,
                         type: "py",
                         align: null})
    }

    function pushBracketScope(stream, state, type) {
      var align = stream.match(/^([\s\[\{\(]|#.*)*$/, false) ? null : stream.column() + 1
      state.scopes.push({offset: state.indent + hangingIndent,
                         type: type,
                         align: align})
    }

    function dedent(stream, state) {
      var indented = stream.indentation();
      while (state.scopes.length > 1 && top(state).offset > indented) {
        if (top(state).type != "py") return true;
        state.scopes.pop();
      }
      return top(state).offset != indented;
    }

    function tokenLexer(stream, state) {
      if (stream.sol()) state.beginningOfLine = true;

      var style = state.tokenize(stream, state);
      var current = stream.current();

      // Handle decorators
      if (state.beginningOfLine && current == "@")
        return stream.match(identifiers, false) ? "meta" : py3 ? "operator" : ERRORCLASS;

      if (/\S/.test(current)) state.beginningOfLine = false;

      if ((style == "variable" || style == "builtin")
          && state.lastToken == "meta")
        style = "meta";

      // Handle scope changes.
      if (current == "pass" || current == "return")
        state.dedent += 1;

      if (current == "lambda") state.lambda = true;
      if (current == ":" && !state.lambda && top(state).type == "py")
        pushPyScope(state);

      if (current.length == 1 && !/string|comment/.test(style)) {
        var delimiter_index = "[({".indexOf(current);
        if (delimiter_index != -1)
          pushBracketScope(stream, state, "])}".slice(delimiter_index, delimiter_index+1));

        delimiter_index = "])}".indexOf(current);
        if (delimiter_index != -1) {
          if (top(state).type == current) state.indent = state.scopes.pop().offset - hangingIndent
          else return ERRORCLASS;
        }
      }
      if (state.dedent > 0 && stream.eol() && top(state).type == "py") {
        if (state.scopes.length > 1) state.scopes.pop();
        state.dedent -= 1;
      }

      return style;
    }

    var external = {
      startState: function(basecolumn) {
        return {
          tokenize: tokenBase,
          scopes: [{offset: basecolumn || 0, type: "py", align: null}],
          indent: basecolumn || 0,
          lastToken: null,
          lambda: false,
          dedent: 0
        };
      },

      token: function(stream, state) {
        var addErr = state.errorToken;
        if (addErr) state.errorToken = false;
        var style = tokenLexer(stream, state);

        if (style && style != "comment")
          state.lastToken = (style == "keyword" || style == "punctuation") ? stream.current() : style;
        if (style == "punctuation") style = null;

        if (stream.eol() && state.lambda)
          state.lambda = false;
        return addErr ? style + " " + ERRORCLASS : style;
      },

      indent: function(state, textAfter) {
        if (state.tokenize != tokenBase)
          return state.tokenize.isString ? CodeMirror.Pass : 0;

        var scope = top(state), closing = scope.type == textAfter.charAt(0)
        if (scope.align != null)
          return scope.align - (closing ? 1 : 0)
        else
          return scope.offset - (closing ? hangingIndent : 0)
      },

      electricInput: /^\s*[\}\]\)]$/,
      closeBrackets: {triples: "'\""},
      lineComment: "#",
      fold: "indent"
    };
    return external;
  });

  CodeMirror.defineMIME("text/x-python", "python");

  var words = function(str) { return str.split(" "); };

  CodeMirror.defineMIME("text/x-cython", {
    name: "python",
    extra_keywords: words("by cdef cimport cpdef ctypedef enum except "+
                          "extern gil include nogil property public "+
                          "readonly struct union DEF IF ELIF ELSE")
  });

});


/***/ }),

/***/ "./node_modules/codemirror/mode/sql/sql.js":
/*!*************************************************!*\
  !*** ./node_modules/codemirror/mode/sql/sql.js ***!
  \*************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE

(function(mod) {
  if (true) // CommonJS
    mod(__webpack_require__(/*! ../../lib/codemirror */ "./node_modules/codemirror/lib/codemirror.js"));
  else {}
})(function(CodeMirror) {
"use strict";

CodeMirror.defineMode("sql", function(config, parserConfig) {
  var client         = parserConfig.client || {},
      atoms          = parserConfig.atoms || {"false": true, "true": true, "null": true},
      builtin        = parserConfig.builtin || set(defaultBuiltin),
      keywords       = parserConfig.keywords || set(sqlKeywords),
      operatorChars  = parserConfig.operatorChars || /^[*+\-%<>!=&|~^\/]/,
      support        = parserConfig.support || {},
      hooks          = parserConfig.hooks || {},
      dateSQL        = parserConfig.dateSQL || {"date" : true, "time" : true, "timestamp" : true},
      backslashStringEscapes = parserConfig.backslashStringEscapes !== false,
      brackets       = parserConfig.brackets || /^[\{}\(\)\[\]]/,
      punctuation    = parserConfig.punctuation || /^[;.,:]/

  function tokenBase(stream, state) {
    var ch = stream.next();

    // call hooks from the mime type
    if (hooks[ch]) {
      var result = hooks[ch](stream, state);
      if (result !== false) return result;
    }

    if (support.hexNumber &&
      ((ch == "0" && stream.match(/^[xX][0-9a-fA-F]+/))
      || (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]+'/))) {
      // hex
      // ref: http://dev.mysql.com/doc/refman/5.5/en/hexadecimal-literals.html
      return "number";
    } else if (support.binaryNumber &&
      (((ch == "b" || ch == "B") && stream.match(/^'[01]+'/))
      || (ch == "0" && stream.match(/^b[01]+/)))) {
      // bitstring
      // ref: http://dev.mysql.com/doc/refman/5.5/en/bit-field-literals.html
      return "number";
    } else if (ch.charCodeAt(0) > 47 && ch.charCodeAt(0) < 58) {
      // numbers
      // ref: http://dev.mysql.com/doc/refman/5.5/en/number-literals.html
      stream.match(/^[0-9]*(\.[0-9]+)?([eE][-+]?[0-9]+)?/);
      support.decimallessFloat && stream.match(/^\.(?!\.)/);
      return "number";
    } else if (ch == "?" && (stream.eatSpace() || stream.eol() || stream.eat(";"))) {
      // placeholders
      return "variable-3";
    } else if (ch == "'" || (ch == '"' && support.doubleQuote)) {
      // strings
      // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
      state.tokenize = tokenLiteral(ch);
      return state.tokenize(stream, state);
    } else if ((((support.nCharCast && (ch == "n" || ch == "N"))
        || (support.charsetCast && ch == "_" && stream.match(/[a-z][a-z0-9]*/i)))
        && (stream.peek() == "'" || stream.peek() == '"'))) {
      // charset casting: _utf8'str', N'str', n'str'
      // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
      return "keyword";
    } else if (support.escapeConstant && (ch == "e" || ch == "E")
        && (stream.peek() == "'" || (stream.peek() == '"' && support.doubleQuote))) {
      // escape constant: E'str', e'str'
      // ref: https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS-ESCAPE
      state.tokenize = function(stream, state) {
        return (state.tokenize = tokenLiteral(stream.next(), true))(stream, state);
      }
      return "keyword";
    } else if (support.commentSlashSlash && ch == "/" && stream.eat("/")) {
      // 1-line comment
      stream.skipToEnd();
      return "comment";
    } else if ((support.commentHash && ch == "#")
        || (ch == "-" && stream.eat("-") && (!support.commentSpaceRequired || stream.eat(" ")))) {
      // 1-line comments
      // ref: https://kb.askmonty.org/en/comment-syntax/
      stream.skipToEnd();
      return "comment";
    } else if (ch == "/" && stream.eat("*")) {
      // multi-line comments
      // ref: https://kb.askmonty.org/en/comment-syntax/
      state.tokenize = tokenComment(1);
      return state.tokenize(stream, state);
    } else if (ch == ".") {
      // .1 for 0.1
      if (support.zerolessFloat && stream.match(/^(?:\d+(?:e[+-]?\d+)?)/i))
        return "number";
      if (stream.match(/^\.+/))
        return null
      // .table_name (ODBC)
      // // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html
      if (support.ODBCdotTable && stream.match(/^[\w\d_$#]+/))
        return "variable-2";
    } else if (operatorChars.test(ch)) {
      // operators
      stream.eatWhile(operatorChars);
      return "operator";
    } else if (brackets.test(ch)) {
      // brackets
      return "bracket";
    } else if (punctuation.test(ch)) {
      // punctuation
      stream.eatWhile(punctuation);
      return "punctuation";
    } else if (ch == '{' &&
        (stream.match(/^( )*(d|D|t|T|ts|TS)( )*'[^']*'( )*}/) || stream.match(/^( )*(d|D|t|T|ts|TS)( )*"[^"]*"( )*}/))) {
      // dates (weird ODBC syntax)
      // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html
      return "number";
    } else {
      stream.eatWhile(/^[_\w\d]/);
      var word = stream.current().toLowerCase();
      // dates (standard SQL syntax)
      // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html
      if (dateSQL.hasOwnProperty(word) && (stream.match(/^( )+'[^']*'/) || stream.match(/^( )+"[^"]*"/)))
        return "number";
      if (atoms.hasOwnProperty(word)) return "atom";
      if (builtin.hasOwnProperty(word)) return "builtin";
      if (keywords.hasOwnProperty(word)) return "keyword";
      if (client.hasOwnProperty(word)) return "string-2";
      return null;
    }
  }

  // 'string', with char specified in quote escaped by '\'
  function tokenLiteral(quote, backslashEscapes) {
    return function(stream, state) {
      var escaped = false, ch;
      while ((ch = stream.next()) != null) {
        if (ch == quote && !escaped) {
          state.tokenize = tokenBase;
          break;
        }
        escaped = (backslashStringEscapes || backslashEscapes) && !escaped && ch == "\\";
      }
      return "string";
    };
  }
  function tokenComment(depth) {
    return function(stream, state) {
      var m = stream.match(/^.*?(\/\*|\*\/)/)
      if (!m) stream.skipToEnd()
      else if (m[1] == "/*") state.tokenize = tokenComment(depth + 1)
      else if (depth > 1) state.tokenize = tokenComment(depth - 1)
      else state.tokenize = tokenBase
      return "comment"
    }
  }

  function pushContext(stream, state, type) {
    state.context = {
      prev: state.context,
      indent: stream.indentation(),
      col: stream.column(),
      type: type
    };
  }

  function popContext(state) {
    state.indent = state.context.indent;
    state.context = state.context.prev;
  }

  return {
    startState: function() {
      return {tokenize: tokenBase, context: null};
    },

    token: function(stream, state) {
      if (stream.sol()) {
        if (state.context && state.context.align == null)
          state.context.align = false;
      }
      if (state.tokenize == tokenBase && stream.eatSpace()) return null;

      var style = state.tokenize(stream, state);
      if (style == "comment") return style;

      if (state.context && state.context.align == null)
        state.context.align = true;

      var tok = stream.current();
      if (tok == "(")
        pushContext(stream, state, ")");
      else if (tok == "[")
        pushContext(stream, state, "]");
      else if (state.context && state.context.type == tok)
        popContext(state);
      return style;
    },

    indent: function(state, textAfter) {
      var cx = state.context;
      if (!cx) return CodeMirror.Pass;
      var closing = textAfter.charAt(0) == cx.type;
      if (cx.align) return cx.col + (closing ? 0 : 1);
      else return cx.indent + (closing ? 0 : config.indentUnit);
    },

    blockCommentStart: "/*",
    blockCommentEnd: "*/",
    lineComment: support.commentSlashSlash ? "//" : support.commentHash ? "#" : "--",
    closeBrackets: "()[]{}''\"\"``"
  };
});

  // `identifier`
  function hookIdentifier(stream) {
    // MySQL/MariaDB identifiers
    // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html
    var ch;
    while ((ch = stream.next()) != null) {
      if (ch == "`" && !stream.eat("`")) return "variable-2";
    }
    stream.backUp(stream.current().length - 1);
    return stream.eatWhile(/\w/) ? "variable-2" : null;
  }

  // "identifier"
  function hookIdentifierDoublequote(stream) {
    // Standard SQL /SQLite identifiers
    // ref: http://web.archive.org/web/20160813185132/http://savage.net.au/SQL/sql-99.bnf.html#delimited%20identifier
    // ref: http://sqlite.org/lang_keywords.html
    var ch;
    while ((ch = stream.next()) != null) {
      if (ch == "\"" && !stream.eat("\"")) return "variable-2";
    }
    stream.backUp(stream.current().length - 1);
    return stream.eatWhile(/\w/) ? "variable-2" : null;
  }

  // variable token
  function hookVar(stream) {
    // variables
    // @@prefix.varName @varName
    // varName can be quoted with ` or ' or "
    // ref: http://dev.mysql.com/doc/refman/5.5/en/user-variables.html
    if (stream.eat("@")) {
      stream.match('session.');
      stream.match('local.');
      stream.match('global.');
    }

    if (stream.eat("'")) {
      stream.match(/^.*'/);
      return "variable-2";
    } else if (stream.eat('"')) {
      stream.match(/^.*"/);
      return "variable-2";
    } else if (stream.eat("`")) {
      stream.match(/^.*`/);
      return "variable-2";
    } else if (stream.match(/^[0-9a-zA-Z$\.\_]+/)) {
      return "variable-2";
    }
    return null;
  };

  // short client keyword token
  function hookClient(stream) {
    // \N means NULL
    // ref: http://dev.mysql.com/doc/refman/5.5/en/null-values.html
    if (stream.eat("N")) {
        return "atom";
    }
    // \g, etc
    // ref: http://dev.mysql.com/doc/refman/5.5/en/mysql-commands.html
    return stream.match(/^[a-zA-Z.#!?]/) ? "variable-2" : null;
  }

  // these keywords are used by all SQL dialects (however, a mode can still overwrite it)
  var sqlKeywords = "alter and as asc between by count create delete desc distinct drop from group having in insert into is join like not on or order select set table union update values where limit ";

  // turn a space-separated list into an array
  function set(str) {
    var obj = {}, words = str.split(" ");
    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
    return obj;
  }

  var defaultBuiltin = "bool boolean bit blob enum long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision real date datetime year unsigned signed decimal numeric"

  // A generic SQL Mode. It's not a standard, it just try to support what is generally supported
  CodeMirror.defineMIME("text/x-sql", {
    name: "sql",
    keywords: set(sqlKeywords + "begin"),
    builtin: set(defaultBuiltin),
    atoms: set("false true null unknown"),
    dateSQL: set("date time timestamp"),
    support: set("ODBCdotTable doubleQuote binaryNumber hexNumber")
  });

  CodeMirror.defineMIME("text/x-mssql", {
    name: "sql",
    client: set("$partition binary_checksum checksum connectionproperty context_info current_request_id error_line error_message error_number error_procedure error_severity error_state formatmessage get_filestream_transaction_context getansinull host_id host_name isnull isnumeric min_active_rowversion newid newsequentialid rowcount_big xact_state object_id"),
    keywords: set(sqlKeywords + "begin trigger proc view index for add constraint key primary foreign collate clustered nonclustered declare exec go if use index holdlock nolock nowait paglock readcommitted readcommittedlock readpast readuncommitted repeatableread rowlock serializable snapshot tablock tablockx updlock with"),
    builtin: set("bigint numeric bit smallint decimal smallmoney int tinyint money float real char varchar text nchar nvarchar ntext binary varbinary image cursor timestamp hierarchyid uniqueidentifier sql_variant xml table "),
    atoms: set("is not null like and or in left right between inner outer join all any some cross unpivot pivot exists"),
    operatorChars: /^[*+\-%<>!=^\&|\/]/,
    brackets: /^[\{}\(\)]/,
    punctuation: /^[;.,:/]/,
    backslashStringEscapes: false,
    dateSQL: set("date datetimeoffset datetime2 smalldatetime datetime time"),
    hooks: {
      "@":   hookVar
    }
  });

  CodeMirror.defineMIME("text/x-mysql", {
    name: "sql",
    client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),
    keywords: set(sqlKeywords + "accessible action add after algorithm all analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general get global grant grants group group_concat handler hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show signal slave slow smallint snapshot soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"),
    builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"),
    atoms: set("false true null unknown"),
    operatorChars: /^[*+\-%<>!=&|^]/,
    dateSQL: set("date time timestamp"),
    support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"),
    hooks: {
      "@":   hookVar,
      "`":   hookIdentifier,
      "\\":  hookClient
    }
  });

  CodeMirror.defineMIME("text/x-mariadb", {
    name: "sql",
    client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),
    keywords: set(sqlKeywords + "accessible action add after algorithm all always analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general generated get global grant grants group groupby_concat handler hard hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password persistent phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show shutdown signal slave slow smallint snapshot soft soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views virtual warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"),
    builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"),
    atoms: set("false true null unknown"),
    operatorChars: /^[*+\-%<>!=&|^]/,
    dateSQL: set("date time timestamp"),
    support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"),
    hooks: {
      "@":   hookVar,
      "`":   hookIdentifier,
      "\\":  hookClient
    }
  });

  // provided by the phpLiteAdmin project - phpliteadmin.org
  CodeMirror.defineMIME("text/x-sqlite", {
    name: "sql",
    // commands of the official SQLite client, ref: https://www.sqlite.org/cli.html#dotcmd
    client: set("auth backup bail binary changes check clone databases dbinfo dump echo eqp exit explain fullschema headers help import imposter indexes iotrace limit lint load log mode nullvalue once open output print prompt quit read restore save scanstats schema separator session shell show stats system tables testcase timeout timer trace vfsinfo vfslist vfsname width"),
    // ref: http://sqlite.org/lang_keywords.html
    keywords: set(sqlKeywords + "abort action add after all analyze attach autoincrement before begin cascade case cast check collate column commit conflict constraint cross current_date current_time current_timestamp database default deferrable deferred detach each else end escape except exclusive exists explain fail for foreign full glob if ignore immediate index indexed initially inner instead intersect isnull key left limit match natural no notnull null of offset outer plan pragma primary query raise recursive references regexp reindex release rename replace restrict right rollback row savepoint temp temporary then to transaction trigger unique using vacuum view virtual when with without"),
    // SQLite is weakly typed, ref: http://sqlite.org/datatype3.html. This is just a list of some common types.
    builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text clob bigint int int2 int8 integer float double char varchar date datetime year unsigned signed numeric real"),
    // ref: http://sqlite.org/syntax/literal-value.html
    atoms: set("null current_date current_time current_timestamp"),
    // ref: http://sqlite.org/lang_expr.html#binaryops
    operatorChars: /^[*+\-%<>!=&|/~]/,
    // SQLite is weakly typed, ref: http://sqlite.org/datatype3.html. This is just a list of some common types.
    dateSQL: set("date time timestamp datetime"),
    support: set("decimallessFloat zerolessFloat"),
    identifierQuote: "\"",  //ref: http://sqlite.org/lang_keywords.html
    hooks: {
      // bind-parameters ref:http://sqlite.org/lang_expr.html#varparam
      "@":   hookVar,
      ":":   hookVar,
      "?":   hookVar,
      "$":   hookVar,
      // The preferred way to escape Identifiers is using double quotes, ref: http://sqlite.org/lang_keywords.html
      "\"":   hookIdentifierDoublequote,
      // there is also support for backticks, ref: http://sqlite.org/lang_keywords.html
      "`":   hookIdentifier
    }
  });

  // the query language used by Apache Cassandra is called CQL, but this mime type
  // is called Cassandra to avoid confusion with Contextual Query Language
  CodeMirror.defineMIME("text/x-cassandra", {
    name: "sql",
    client: { },
    keywords: set("add all allow alter and any apply as asc authorize batch begin by clustering columnfamily compact consistency count create custom delete desc distinct drop each_quorum exists filtering from grant if in index insert into key keyspace keyspaces level limit local_one local_quorum modify nan norecursive nosuperuser not of on one order password permission permissions primary quorum rename revoke schema select set storage superuser table three to token truncate ttl two type unlogged update use user users using values where with writetime"),
    builtin: set("ascii bigint blob boolean counter decimal double float frozen inet int list map static text timestamp timeuuid tuple uuid varchar varint"),
    atoms: set("false true infinity NaN"),
    operatorChars: /^[<>=]/,
    dateSQL: { },
    support: set("commentSlashSlash decimallessFloat"),
    hooks: { }
  });

  // this is based on Peter Raganitsch's 'plsql' mode
  CodeMirror.defineMIME("text/x-plsql", {
    name:       "sql",
    client:     set("appinfo arraysize autocommit autoprint autorecovery autotrace blockterminator break btitle cmdsep colsep compatibility compute concat copycommit copytypecheck define describe echo editfile embedded escape exec execute feedback flagger flush heading headsep instance linesize lno loboffset logsource long longchunksize markup native newpage numformat numwidth pagesize pause pno recsep recsepchar release repfooter repheader serveroutput shiftinout show showmode size spool sqlblanklines sqlcase sqlcode sqlcontinue sqlnumber sqlpluscompatibility sqlprefix sqlprompt sqlterminator suffix tab term termout time timing trimout trimspool ttitle underline verify version wrap"),
    keywords:   set("abort accept access add all alter and any array arraylen as asc assert assign at attributes audit authorization avg base_table begin between binary_integer body boolean by case cast char char_base check close cluster clusters colauth column comment commit compress connect connected constant constraint crash create current currval cursor data_base database date dba deallocate debugoff debugon decimal declare default definition delay delete desc digits dispose distinct do drop else elseif elsif enable end entry escape exception exception_init exchange exclusive exists exit external fast fetch file for force form from function generic goto grant group having identified if immediate in increment index indexes indicator initial initrans insert interface intersect into is key level library like limited local lock log logging long loop master maxextents maxtrans member minextents minus mislabel mode modify multiset new next no noaudit nocompress nologging noparallel not nowait number_base object of off offline on online only open option or order out package parallel partition pctfree pctincrease pctused pls_integer positive positiven pragma primary prior private privileges procedure public raise range raw read rebuild record ref references refresh release rename replace resource restrict return returning returns reverse revoke rollback row rowid rowlabel rownum rows run savepoint schema segment select separate session set share snapshot some space split sql start statement storage subtype successful synonym tabauth table tables tablespace task terminate then to trigger truncate type union unique unlimited unrecoverable unusable update use using validate value values variable view views when whenever where while with work"),
    builtin:    set("abs acos add_months ascii asin atan atan2 average bfile bfilename bigserial bit blob ceil character chartorowid chr clob concat convert cos cosh count dec decode deref dual dump dup_val_on_index empty error exp false float floor found glb greatest hextoraw initcap instr instrb int integer isopen last_day least length lengthb ln lower lpad ltrim lub make_ref max min mlslabel mod months_between natural naturaln nchar nclob new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null number numeric nvarchar2 nvl others power rawtohex real reftohex round rowcount rowidtochar rowtype rpad rtrim serial sign signtype sin sinh smallint soundex sqlcode sqlerrm sqrt stddev string substr substrb sum sysdate tan tanh to_char text to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid unlogged upper user userenv varchar varchar2 variance varying vsize xml"),
    operatorChars: /^[*\/+\-%<>!=~]/,
    dateSQL:    set("date time timestamp"),
    support:    set("doubleQuote nCharCast zerolessFloat binaryNumber hexNumber")
  });

  // Created to support specific hive keywords
  CodeMirror.defineMIME("text/x-hive", {
    name: "sql",
    keywords: set("select alter $elem$ $key$ $value$ add after all analyze and archive as asc before between binary both bucket buckets by cascade case cast change cluster clustered clusterstatus collection column columns comment compute concatenate continue create cross cursor data database databases dbproperties deferred delete delimited desc describe directory disable distinct distribute drop else enable end escaped exclusive exists explain export extended external fetch fields fileformat first format formatted from full function functions grant group having hold_ddltime idxproperties if import in index indexes inpath inputdriver inputformat insert intersect into is items join keys lateral left like limit lines load local location lock locks mapjoin materialized minus msck no_drop nocompress not of offline on option or order out outer outputdriver outputformat overwrite partition partitioned partitions percent plus preserve procedure purge range rcfile read readonly reads rebuild recordreader recordwriter recover reduce regexp rename repair replace restrict revoke right rlike row schema schemas semi sequencefile serde serdeproperties set shared show show_database sort sorted ssl statistics stored streamtable table tables tablesample tblproperties temporary terminated textfile then tmp to touch transform trigger unarchive undo union uniquejoin unlock update use using utc utc_tmestamp view when where while with admin authorization char compact compactions conf cube current current_date current_timestamp day decimal defined dependency directories elem_type exchange file following for grouping hour ignore inner interval jar less logical macro minute month more none noscan over owner partialscan preceding pretty principals protection reload rewrite role roles rollup rows second server sets skewed transactions truncate unbounded unset uri user values window year"),
    builtin: set("bool boolean long timestamp tinyint smallint bigint int float double date datetime unsigned string array struct map uniontype key_type utctimestamp value_type varchar"),
    atoms: set("false true null unknown"),
    operatorChars: /^[*+\-%<>!=]/,
    dateSQL: set("date timestamp"),
    support: set("ODBCdotTable doubleQuote binaryNumber hexNumber")
  });

  CodeMirror.defineMIME("text/x-pgsql", {
    name: "sql",
    client: set("source"),
    // For PostgreSQL - https://www.postgresql.org/docs/11/sql-keywords-appendix.html
    // For pl/pgsql lang - https://github.com/postgres/postgres/blob/REL_11_2/src/pl/plpgsql/src/pl_scanner.c
    keywords: set(sqlKeywords + "a abort abs absent absolute access according action ada add admin after aggregate alias all allocate also alter always analyse analyze and any are array array_agg array_max_cardinality as asc asensitive assert assertion assignment asymmetric at atomic attach attribute attributes authorization avg backward base64 before begin begin_frame begin_partition bernoulli between bigint binary bit bit_length blob blocked bom boolean both breadth by c cache call called cardinality cascade cascaded case cast catalog catalog_name ceil ceiling chain char char_length character character_length character_set_catalog character_set_name character_set_schema characteristics characters check checkpoint class class_origin clob close cluster coalesce cobol collate collation collation_catalog collation_name collation_schema collect column column_name columns command_function command_function_code comment comments commit committed concurrently condition condition_number configuration conflict connect connection connection_name constant constraint constraint_catalog constraint_name constraint_schema constraints constructor contains content continue control conversion convert copy corr corresponding cost count covar_pop covar_samp create cross csv cube cume_dist current current_catalog current_date current_default_transform_group current_path current_role current_row current_schema current_time current_timestamp current_transform_group_for_type current_user cursor cursor_name cycle data database datalink datatype date datetime_interval_code datetime_interval_precision day db deallocate debug dec decimal declare default defaults deferrable deferred defined definer degree delete delimiter delimiters dense_rank depends depth deref derived desc describe descriptor detach detail deterministic diagnostics dictionary disable discard disconnect dispatch distinct dlnewcopy dlpreviouscopy dlurlcomplete dlurlcompleteonly dlurlcompletewrite dlurlpath dlurlpathonly dlurlpathwrite dlurlscheme dlurlserver dlvalue do document domain double drop dump dynamic dynamic_function dynamic_function_code each element else elseif elsif empty enable encoding encrypted end end_frame end_partition endexec enforced enum equals errcode error escape event every except exception exclude excluding exclusive exec execute exists exit exp explain expression extension external extract false family fetch file filter final first first_value flag float floor following for force foreach foreign fortran forward found frame_row free freeze from fs full function functions fusion g general generated get global go goto grant granted greatest group grouping groups handler having header hex hierarchy hint hold hour id identity if ignore ilike immediate immediately immutable implementation implicit import in include including increment indent index indexes indicator info inherit inherits initially inline inner inout input insensitive insert instance instantiable instead int integer integrity intersect intersection interval into invoker is isnull isolation join k key key_member key_type label lag language large last last_value lateral lead leading leakproof least left length level library like like_regex limit link listen ln load local localtime localtimestamp location locator lock locked log logged loop lower m map mapping match matched materialized max max_cardinality maxvalue member merge message message_length message_octet_length message_text method min minute minvalue mod mode modifies module month more move multiset mumps name names namespace national natural nchar nclob nesting new next nfc nfd nfkc nfkd nil no none normalize normalized not nothing notice notify notnull nowait nth_value ntile null nullable nullif nulls number numeric object occurrences_regex octet_length octets of off offset oids old on only open operator option options or order ordering ordinality others out outer output over overlaps overlay overriding owned owner p pad parallel parameter parameter_mode parameter_name parameter_ordinal_position parameter_specific_catalog parameter_specific_name parameter_specific_schema parser partial partition pascal passing passthrough password path percent percent_rank percentile_cont percentile_disc perform period permission pg_context pg_datatype_name pg_exception_context pg_exception_detail pg_exception_hint placing plans pli policy portion position position_regex power precedes preceding precision prepare prepared preserve primary print_strict_params prior privileges procedural procedure procedures program public publication query quote raise range rank read reads real reassign recheck recovery recursive ref references referencing refresh regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy reindex relative release rename repeatable replace replica requiring reset respect restart restore restrict result result_oid return returned_cardinality returned_length returned_octet_length returned_sqlstate returning returns reverse revoke right role rollback rollup routine routine_catalog routine_name routine_schema routines row row_count row_number rows rowtype rule savepoint scale schema schema_name schemas scope scope_catalog scope_name scope_schema scroll search second section security select selective self sensitive sequence sequences serializable server server_name session session_user set setof sets share show similar simple size skip slice smallint snapshot some source space specific specific_name specifictype sql sqlcode sqlerror sqlexception sqlstate sqlwarning sqrt stable stacked standalone start state statement static statistics stddev_pop stddev_samp stdin stdout storage strict strip structure style subclass_origin submultiset subscription substring substring_regex succeeds sum symmetric sysid system system_time system_user t table table_name tables tablesample tablespace temp template temporary text then ties time timestamp timezone_hour timezone_minute to token top_level_count trailing transaction transaction_active transactions_committed transactions_rolled_back transform transforms translate translate_regex translation treat trigger trigger_catalog trigger_name trigger_schema trim trim_array true truncate trusted type types uescape unbounded uncommitted under unencrypted union unique unknown unlink unlisten unlogged unnamed unnest until untyped update upper uri usage use_column use_variable user user_defined_type_catalog user_defined_type_code user_defined_type_name user_defined_type_schema using vacuum valid validate validator value value_of values var_pop var_samp varbinary varchar variable_conflict variadic varying verbose version versioning view views volatile warning when whenever where while whitespace width_bucket window with within without work wrapper write xml xmlagg xmlattributes xmlbinary xmlcast xmlcomment xmlconcat xmldeclaration xmldocument xmlelement xmlexists xmlforest xmliterate xmlnamespaces xmlparse xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltext xmlvalidate year yes zone"),
    // https://www.postgresql.org/docs/11/datatype.html
    builtin: set("bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float8 inet integer int int4 interval json jsonb line lseg macaddr macaddr8 money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time without zone with timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml"),
    atoms: set("false true null unknown"),
    operatorChars: /^[*\/+\-%<>!=&|^\/#@?~]/,
    backslashStringEscapes: false,
    dateSQL: set("date time timestamp"),
    support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber nCharCast charsetCast escapeConstant")
  });

  // Google's SQL-like query language, GQL
  CodeMirror.defineMIME("text/x-gql", {
    name: "sql",
    keywords: set("ancestor and asc by contains desc descendant distinct from group has in is limit offset on order select superset where"),
    atoms: set("false true"),
    builtin: set("blob datetime first key __key__ string integer double boolean null"),
    operatorChars: /^[*+\-%<>!=]/
  });

  // Greenplum
  CodeMirror.defineMIME("text/x-gpsql", {
    name: "sql",
    client: set("source"),
    //https://github.com/greenplum-db/gpdb/blob/master/src/include/parser/kwlist.h
    keywords: set("abort absolute access action active add admin after aggregate all also alter always analyse analyze and any array as asc assertion assignment asymmetric at authorization backward before begin between bigint binary bit boolean both by cache called cascade cascaded case cast chain char character characteristics check checkpoint class close cluster coalesce codegen collate column comment commit committed concurrency concurrently configuration connection constraint constraints contains content continue conversion copy cost cpu_rate_limit create createdb createexttable createrole createuser cross csv cube current current_catalog current_date current_role current_schema current_time current_timestamp current_user cursor cycle data database day deallocate dec decimal declare decode default defaults deferrable deferred definer delete delimiter delimiters deny desc dictionary disable discard distinct distributed do document domain double drop dxl each else enable encoding encrypted end enum errors escape every except exchange exclude excluding exclusive execute exists explain extension external extract false family fetch fields filespace fill filter first float following for force foreign format forward freeze from full function global grant granted greatest group group_id grouping handler hash having header hold host hour identity if ignore ilike immediate immutable implicit in including inclusive increment index indexes inherit inherits initially inline inner inout input insensitive insert instead int integer intersect interval into invoker is isnull isolation join key language large last leading least left level like limit list listen load local localtime localtimestamp location lock log login mapping master match maxvalue median merge minute minvalue missing mode modifies modify month move name names national natural nchar new newline next no nocreatedb nocreateexttable nocreaterole nocreateuser noinherit nologin none noovercommit nosuperuser not nothing notify notnull nowait null nullif nulls numeric object of off offset oids old on only operator option options or order ordered others out outer over overcommit overlaps overlay owned owner parser partial partition partitions passing password percent percentile_cont percentile_disc placing plans position preceding precision prepare prepared preserve primary prior privileges procedural procedure protocol queue quote randomly range read readable reads real reassign recheck recursive ref references reindex reject relative release rename repeatable replace replica reset resource restart restrict returning returns revoke right role rollback rollup rootpartition row rows rule savepoint scatter schema scroll search second security segment select sequence serializable session session_user set setof sets share show similar simple smallint some split sql stable standalone start statement statistics stdin stdout storage strict strip subpartition subpartitions substring superuser symmetric sysid system table tablespace temp template temporary text then threshold ties time timestamp to trailing transaction treat trigger trim true truncate trusted type unbounded uncommitted unencrypted union unique unknown unlisten until update user using vacuum valid validation validator value values varchar variadic varying verbose version view volatile web when where whitespace window with within without work writable write xml xmlattributes xmlconcat xmlelement xmlexists xmlforest xmlparse xmlpi xmlroot xmlserialize year yes zone"),
    builtin: set("bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float float8 inet integer int int4 interval json jsonb line lseg macaddr macaddr8 money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time without zone with timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml"),
    atoms: set("false true null unknown"),
    operatorChars: /^[*+\-%<>!=&|^\/#@?~]/,
    dateSQL: set("date time timestamp"),
    support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber nCharCast charsetCast")
  });

  // Spark SQL
  CodeMirror.defineMIME("text/x-sparksql", {
    name: "sql",
    keywords: set("add after all alter analyze and anti archive array as asc at between bucket buckets by cache cascade case cast change clear cluster clustered codegen collection column columns comment commit compact compactions compute concatenate cost create cross cube current current_date current_timestamp database databases data dbproperties defined delete delimited deny desc describe dfs directories distinct distribute drop else end escaped except exchange exists explain export extended external false fields fileformat first following for format formatted from full function functions global grant group grouping having if ignore import in index indexes inner inpath inputformat insert intersect interval into is items join keys last lateral lazy left like limit lines list load local location lock locks logical macro map minus msck natural no not null nulls of on optimize option options or order out outer outputformat over overwrite partition partitioned partitions percent preceding principals purge range recordreader recordwriter recover reduce refresh regexp rename repair replace reset restrict revoke right rlike role roles rollback rollup row rows schema schemas select semi separated serde serdeproperties set sets show skewed sort sorted start statistics stored stratify struct table tables tablesample tblproperties temp temporary terminated then to touch transaction transactions transform true truncate unarchive unbounded uncache union unlock unset use using values view when where window with"),
    builtin: set("tinyint smallint int bigint boolean float double string binary timestamp decimal array map struct uniontype delimited serde sequencefile textfile rcfile inputformat outputformat"),
    atoms: set("false true null"),
    operatorChars: /^[*\/+\-%<>!=~&|^]/,
    dateSQL: set("date time timestamp"),
    support: set("ODBCdotTable doubleQuote zerolessFloat")
  });

  // Esper
  CodeMirror.defineMIME("text/x-esper", {
    name: "sql",
    client: set("source"),
    // http://www.espertech.com/esper/release-5.5.0/esper-reference/html/appendix_keywords.html
    keywords: set("alter and as asc between by count create delete desc distinct drop from group having in insert into is join like not on or order select set table union update values where limit after all and as at asc avedev avg between by case cast coalesce count create current_timestamp day days delete define desc distinct else end escape events every exists false first from full group having hour hours in inner insert instanceof into irstream is istream join last lastweekday left limit like max match_recognize matches median measures metadatasql min minute minutes msec millisecond milliseconds not null offset on or order outer output partition pattern prev prior regexp retain-union retain-intersection right rstream sec second seconds select set some snapshot sql stddev sum then true unidirectional until update variable weekday when where window"),
    builtin: {},
    atoms: set("false true null"),
    operatorChars: /^[*+\-%<>!=&|^\/#@?~]/,
    dateSQL: set("time"),
    support: set("decimallessFloat zerolessFloat binaryNumber hexNumber")
  });
});

/*
  How Properties of Mime Types are used by SQL Mode
  =================================================

  keywords:
    A list of keywords you want to be highlighted.
  builtin:
    A list of builtin types you want to be highlighted (if you want types to be of class "builtin" instead of "keyword").
  operatorChars:
    All characters that must be handled as operators.
  client:
    Commands parsed and executed by the client (not the server).
  support:
    A list of supported syntaxes which are not common, but are supported by more than 1 DBMS.
    * ODBCdotTable: .tableName
    * zerolessFloat: .1
    * doubleQuote
    * nCharCast: N'string'
    * charsetCast: _utf8'string'
    * commentHash: use # char for comments
    * commentSlashSlash: use // for comments
    * commentSpaceRequired: require a space after -- for comments
  atoms:
    Keywords that must be highlighted as atoms,. Some DBMS's support more atoms than others:
    UNKNOWN, INFINITY, UNDERFLOW, NaN...
  dateSQL:
    Used for date/time SQL standard syntax, because not all DBMS's support same temporal types.
*/


/***/ }),

/***/ "./node_modules/codemirror/mode/xml/xml.js":
/*!*************************************************!*\
  !*** ./node_modules/codemirror/mode/xml/xml.js ***!
  \*************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE

(function(mod) {
  if (true) // CommonJS
    mod(__webpack_require__(/*! ../../lib/codemirror */ "./node_modules/codemirror/lib/codemirror.js"));
  else {}
})(function(CodeMirror) {
"use strict";

var htmlConfig = {
  autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
                    'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
                    'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
                    'track': true, 'wbr': true, 'menuitem': true},
  implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
                     'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
                     'th': true, 'tr': true},
  contextGrabbers: {
    'dd': {'dd': true, 'dt': true},
    'dt': {'dd': true, 'dt': true},
    'li': {'li': true},
    'option': {'option': true, 'optgroup': true},
    'optgroup': {'optgroup': true},
    'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
          'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
          'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
          'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
          'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
    'rp': {'rp': true, 'rt': true},
    'rt': {'rp': true, 'rt': true},
    'tbody': {'tbody': true, 'tfoot': true},
    'td': {'td': true, 'th': true},
    'tfoot': {'tbody': true},
    'th': {'td': true, 'th': true},
    'thead': {'tbody': true, 'tfoot': true},
    'tr': {'tr': true}
  },
  doNotIndent: {"pre": true},
  allowUnquoted: true,
  allowMissing: true,
  caseFold: true
}

var xmlConfig = {
  autoSelfClosers: {},
  implicitlyClosed: {},
  contextGrabbers: {},
  doNotIndent: {},
  allowUnquoted: false,
  allowMissing: false,
  allowMissingTagName: false,
  caseFold: false
}

CodeMirror.defineMode("xml", function(editorConf, config_) {
  var indentUnit = editorConf.indentUnit
  var config = {}
  var defaults = config_.htmlMode ? htmlConfig : xmlConfig
  for (var prop in defaults) config[prop] = defaults[prop]
  for (var prop in config_) config[prop] = config_[prop]

  // Return variables for tokenizers
  var type, setStyle;

  function inText(stream, state) {
    function chain(parser) {
      state.tokenize = parser;
      return parser(stream, state);
    }

    var ch = stream.next();
    if (ch == "<") {
      if (stream.eat("!")) {
        if (stream.eat("[")) {
          if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
          else return null;
        } else if (stream.match("--")) {
          return chain(inBlock("comment", "-->"));
        } else if (stream.match("DOCTYPE", true, true)) {
          stream.eatWhile(/[\w\._\-]/);
          return chain(doctype(1));
        } else {
          return null;
        }
      } else if (stream.eat("?")) {
        stream.eatWhile(/[\w\._\-]/);
        state.tokenize = inBlock("meta", "?>");
        return "meta";
      } else {
        type = stream.eat("/") ? "closeTag" : "openTag";
        state.tokenize = inTag;
        return "tag bracket";
      }
    } else if (ch == "&") {
      var ok;
      if (stream.eat("#")) {
        if (stream.eat("x")) {
          ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
        } else {
          ok = stream.eatWhile(/[\d]/) && stream.eat(";");
        }
      } else {
        ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
      }
      return ok ? "atom" : "error";
    } else {
      stream.eatWhile(/[^&<]/);
      return null;
    }
  }
  inText.isInText = true;

  function inTag(stream, state) {
    var ch = stream.next();
    if (ch == ">" || (ch == "/" && stream.eat(">"))) {
      state.tokenize = inText;
      type = ch == ">" ? "endTag" : "selfcloseTag";
      return "tag bracket";
    } else if (ch == "=") {
      type = "equals";
      return null;
    } else if (ch == "<") {
      state.tokenize = inText;
      state.state = baseState;
      state.tagName = state.tagStart = null;
      var next = state.tokenize(stream, state);
      return next ? next + " tag error" : "tag error";
    } else if (/[\'\"]/.test(ch)) {
      state.tokenize = inAttribute(ch);
      state.stringStartCol = stream.column();
      return state.tokenize(stream, state);
    } else {
      stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
      return "word";
    }
  }

  function inAttribute(quote) {
    var closure = function(stream, state) {
      while (!stream.eol()) {
        if (stream.next() == quote) {
          state.tokenize = inTag;
          break;
        }
      }
      return "string";
    };
    closure.isInAttribute = true;
    return closure;
  }

  function inBlock(style, terminator) {
    return function(stream, state) {
      while (!stream.eol()) {
        if (stream.match(terminator)) {
          state.tokenize = inText;
          break;
        }
        stream.next();
      }
      return style;
    }
  }

  function doctype(depth) {
    return function(stream, state) {
      var ch;
      while ((ch = stream.next()) != null) {
        if (ch == "<") {
          state.tokenize = doctype(depth + 1);
          return state.tokenize(stream, state);
        } else if (ch == ">") {
          if (depth == 1) {
            state.tokenize = inText;
            break;
          } else {
            state.tokenize = doctype(depth - 1);
            return state.tokenize(stream, state);
          }
        }
      }
      return "meta";
    };
  }

  function Context(state, tagName, startOfLine) {
    this.prev = state.context;
    this.tagName = tagName || "";
    this.indent = state.indented;
    this.startOfLine = startOfLine;
    if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
      this.noIndent = true;
  }
  function popContext(state) {
    if (state.context) state.context = state.context.prev;
  }
  function maybePopContext(state, nextTagName) {
    var parentTagName;
    while (true) {
      if (!state.context) {
        return;
      }
      parentTagName = state.context.tagName;
      if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
          !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
        return;
      }
      popContext(state);
    }
  }

  function baseState(type, stream, state) {
    if (type == "openTag") {
      state.tagStart = stream.column();
      return tagNameState;
    } else if (type == "closeTag") {
      return closeTagNameState;
    } else {
      return baseState;
    }
  }
  function tagNameState(type, stream, state) {
    if (type == "word") {
      state.tagName = stream.current();
      setStyle = "tag";
      return attrState;
    } else if (config.allowMissingTagName && type == "endTag") {
      setStyle = "tag bracket";
      return attrState(type, stream, state);
    } else {
      setStyle = "error";
      return tagNameState;
    }
  }
  function closeTagNameState(type, stream, state) {
    if (type == "word") {
      var tagName = stream.current();
      if (state.context && state.context.tagName != tagName &&
          config.implicitlyClosed.hasOwnProperty(state.context.tagName))
        popContext(state);
      if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
        setStyle = "tag";
        return closeState;
      } else {
        setStyle = "tag error";
        return closeStateErr;
      }
    } else if (config.allowMissingTagName && type == "endTag") {
      setStyle = "tag bracket";
      return closeState(type, stream, state);
    } else {
      setStyle = "error";
      return closeStateErr;
    }
  }

  function closeState(type, _stream, state) {
    if (type != "endTag") {
      setStyle = "error";
      return closeState;
    }
    popContext(state);
    return baseState;
  }
  function closeStateErr(type, stream, state) {
    setStyle = "error";
    return closeState(type, stream, state);
  }

  function attrState(type, _stream, state) {
    if (type == "word") {
      setStyle = "attribute";
      return attrEqState;
    } else if (type == "endTag" || type == "selfcloseTag") {
      var tagName = state.tagName, tagStart = state.tagStart;
      state.tagName = state.tagStart = null;
      if (type == "selfcloseTag" ||
          config.autoSelfClosers.hasOwnProperty(tagName)) {
        maybePopContext(state, tagName);
      } else {
        maybePopContext(state, tagName);
        state.context = new Context(state, tagName, tagStart == state.indented);
      }
      return baseState;
    }
    setStyle = "error";
    return attrState;
  }
  function attrEqState(type, stream, state) {
    if (type == "equals") return attrValueState;
    if (!config.allowMissing) setStyle = "error";
    return attrState(type, stream, state);
  }
  function attrValueState(type, stream, state) {
    if (type == "string") return attrContinuedState;
    if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
    setStyle = "error";
    return attrState(type, stream, state);
  }
  function attrContinuedState(type, stream, state) {
    if (type == "string") return attrContinuedState;
    return attrState(type, stream, state);
  }

  return {
    startState: function(baseIndent) {
      var state = {tokenize: inText,
                   state: baseState,
                   indented: baseIndent || 0,
                   tagName: null, tagStart: null,
                   context: null}
      if (baseIndent != null) state.baseIndent = baseIndent
      return state
    },

    token: function(stream, state) {
      if (!state.tagName && stream.sol())
        state.indented = stream.indentation();

      if (stream.eatSpace()) return null;
      type = null;
      var style = state.tokenize(stream, state);
      if ((style || type) && style != "comment") {
        setStyle = null;
        state.state = state.state(type || style, stream, state);
        if (setStyle)
          style = setStyle == "error" ? style + " error" : setStyle;
      }
      return style;
    },

    indent: function(state, textAfter, fullLine) {
      var context = state.context;
      // Indent multi-line strings (e.g. css).
      if (state.tokenize.isInAttribute) {
        if (state.tagStart == state.indented)
          return state.stringStartCol + 1;
        else
          return state.indented + indentUnit;
      }
      if (context && context.noIndent) return CodeMirror.Pass;
      if (state.tokenize != inTag && state.tokenize != inText)
        return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
      // Indent the starts of attribute names.
      if (state.tagName) {
        if (config.multilineTagIndentPastTag !== false)
          return state.tagStart + state.tagName.length + 2;
        else
          return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
      }
      if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
      var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
      if (tagAfter && tagAfter[1]) { // Closing tag spotted
        while (context) {
          if (context.tagName == tagAfter[2]) {
            context = context.prev;
            break;
          } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) {
            context = context.prev;
          } else {
            break;
          }
        }
      } else if (tagAfter) { // Opening tag spotted
        while (context) {
          var grabbers = config.contextGrabbers[context.tagName];
          if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
            context = context.prev;
          else
            break;
        }
      }
      while (context && context.prev && !context.startOfLine)
        context = context.prev;
      if (context) return context.indent + indentUnit;
      else return state.baseIndent || 0;
    },

    electricInput: /<\/[\s\w:]+>$/,
    blockCommentStart: "<!--",
    blockCommentEnd: "-->",

    configuration: config.htmlMode ? "html" : "xml",
    helperType: config.htmlMode ? "html" : "xml",

    skipAttribute: function(state) {
      if (state.state == attrValueState)
        state.state = attrState
    },

    xmlCurrentTag: function(state) {
      return state.tagName ? {name: state.tagName, close: state.type == "closeTag"} : null
    },

    xmlCurrentContext: function(state) {
      var context = []
      for (var cx = state.context; cx; cx = cx.prev)
        context.push(cx.tagName)
      return context.reverse()
    }
  };
});

CodeMirror.defineMIME("text/xml", "xml");
CodeMirror.defineMIME("application/xml", "xml");
if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
  CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});

});


/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./node_modules/codemirror/lib/codemirror.css":
/*!******************************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./node_modules/codemirror/lib/codemirror.css ***!
  \******************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Imports
var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
exports = ___CSS_LOADER_API_IMPORT___(false);
// Module
exports.push([module.i, "/* BASICS */\n\n.CodeMirror {\n  /* Set height, width, borders, and global font properties here */\n  font-family: monospace;\n  height: 300px;\n  color: black;\n  direction: ltr;\n}\n\n/* PADDING */\n\n.CodeMirror-lines {\n  padding: 4px 0; /* Vertical padding around content */\n}\n.CodeMirror pre.CodeMirror-line,\n.CodeMirror pre.CodeMirror-line-like {\n  padding: 0 4px; /* Horizontal padding of content */\n}\n\n.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {\n  background-color: transparent; /* The little square between H and V scrollbars */\n}\n\n/* GUTTER */\n\n.CodeMirror-gutters {\n  border-right: 1px solid #ddd;\n  background-color: #f7f7f7;\n  white-space: nowrap;\n}\n.CodeMirror-linenumbers {}\n.CodeMirror-linenumber {\n  padding: 0 3px 0 5px;\n  min-width: 20px;\n  text-align: right;\n  color: #999;\n  white-space: nowrap;\n}\n\n.CodeMirror-guttermarker { color: black; }\n.CodeMirror-guttermarker-subtle { color: #999; }\n\n/* CURSOR */\n\n.CodeMirror-cursor {\n  border-left: 1px solid black;\n  border-right: none;\n  width: 0;\n}\n/* Shown when moving in bi-directional text */\n.CodeMirror div.CodeMirror-secondarycursor {\n  border-left: 1px solid silver;\n}\n.cm-fat-cursor .CodeMirror-cursor {\n  width: auto;\n  border: 0 !important;\n  background: #7e7;\n}\n.cm-fat-cursor div.CodeMirror-cursors {\n  z-index: 1;\n}\n.cm-fat-cursor-mark {\n  background-color: rgba(20, 255, 20, 0.5);\n  -webkit-animation: blink 1.06s steps(1) infinite;\n  -moz-animation: blink 1.06s steps(1) infinite;\n  animation: blink 1.06s steps(1) infinite;\n}\n.cm-animate-fat-cursor {\n  width: auto;\n  border: 0;\n  -webkit-animation: blink 1.06s steps(1) infinite;\n  -moz-animation: blink 1.06s steps(1) infinite;\n  animation: blink 1.06s steps(1) infinite;\n  background-color: #7e7;\n}\n@-moz-keyframes blink {\n  0% {}\n  50% { background-color: transparent; }\n  100% {}\n}\n@-webkit-keyframes blink {\n  0% {}\n  50% { background-color: transparent; }\n  100% {}\n}\n@keyframes blink {\n  0% {}\n  50% { background-color: transparent; }\n  100% {}\n}\n\n/* Can style cursor different in overwrite (non-insert) mode */\n.CodeMirror-overwrite .CodeMirror-cursor {}\n\n.cm-tab { display: inline-block; text-decoration: inherit; }\n\n.CodeMirror-rulers {\n  position: absolute;\n  left: 0; right: 0; top: -50px; bottom: 0;\n  overflow: hidden;\n}\n.CodeMirror-ruler {\n  border-left: 1px solid #ccc;\n  top: 0; bottom: 0;\n  position: absolute;\n}\n\n/* DEFAULT THEME */\n\n.cm-s-default .cm-header {color: blue;}\n.cm-s-default .cm-quote {color: #090;}\n.cm-negative {color: #d44;}\n.cm-positive {color: #292;}\n.cm-header, .cm-strong {font-weight: bold;}\n.cm-em {font-style: italic;}\n.cm-link {text-decoration: underline;}\n.cm-strikethrough {text-decoration: line-through;}\n\n.cm-s-default .cm-keyword {color: #708;}\n.cm-s-default .cm-atom {color: #219;}\n.cm-s-default .cm-number {color: #164;}\n.cm-s-default .cm-def {color: #00f;}\n.cm-s-default .cm-variable,\n.cm-s-default .cm-punctuation,\n.cm-s-default .cm-property,\n.cm-s-default .cm-operator {}\n.cm-s-default .cm-variable-2 {color: #05a;}\n.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}\n.cm-s-default .cm-comment {color: #a50;}\n.cm-s-default .cm-string {color: #a11;}\n.cm-s-default .cm-string-2 {color: #f50;}\n.cm-s-default .cm-meta {color: #555;}\n.cm-s-default .cm-qualifier {color: #555;}\n.cm-s-default .cm-builtin {color: #30a;}\n.cm-s-default .cm-bracket {color: #997;}\n.cm-s-default .cm-tag {color: #170;}\n.cm-s-default .cm-attribute {color: #00c;}\n.cm-s-default .cm-hr {color: #999;}\n.cm-s-default .cm-link {color: #00c;}\n\n.cm-s-default .cm-error {color: #f00;}\n.cm-invalidchar {color: #f00;}\n\n.CodeMirror-composing { border-bottom: 2px solid; }\n\n/* Default styles for common addons */\n\ndiv.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}\ndiv.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}\n.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }\n.CodeMirror-activeline-background {background: #e8f2ff;}\n\n/* STOP */\n\n/* The rest of this file contains styles related to the mechanics of\n   the editor. You probably shouldn't touch them. */\n\n.CodeMirror {\n  position: relative;\n  overflow: hidden;\n  background: white;\n}\n\n.CodeMirror-scroll {\n  overflow: scroll !important; /* Things will break if this is overridden */\n  /* 50px is the magic margin used to hide the element's real scrollbars */\n  /* See overflow: hidden in .CodeMirror */\n  margin-bottom: -50px; margin-right: -50px;\n  padding-bottom: 50px;\n  height: 100%;\n  outline: none; /* Prevent dragging from highlighting the element */\n  position: relative;\n}\n.CodeMirror-sizer {\n  position: relative;\n  border-right: 50px solid transparent;\n}\n\n/* The fake, visible scrollbars. Used to force redraw during scrolling\n   before actual scrolling happens, thus preventing shaking and\n   flickering artifacts. */\n.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {\n  position: absolute;\n  z-index: 6;\n  display: none;\n  outline: none;\n}\n.CodeMirror-vscrollbar {\n  right: 0; top: 0;\n  overflow-x: hidden;\n  overflow-y: scroll;\n}\n.CodeMirror-hscrollbar {\n  bottom: 0; left: 0;\n  overflow-y: hidden;\n  overflow-x: scroll;\n}\n.CodeMirror-scrollbar-filler {\n  right: 0; bottom: 0;\n}\n.CodeMirror-gutter-filler {\n  left: 0; bottom: 0;\n}\n\n.CodeMirror-gutters {\n  position: absolute; left: 0; top: 0;\n  min-height: 100%;\n  z-index: 3;\n}\n.CodeMirror-gutter {\n  white-space: normal;\n  height: 100%;\n  display: inline-block;\n  vertical-align: top;\n  margin-bottom: -50px;\n}\n.CodeMirror-gutter-wrapper {\n  position: absolute;\n  z-index: 4;\n  background: none !important;\n  border: none !important;\n}\n.CodeMirror-gutter-background {\n  position: absolute;\n  top: 0; bottom: 0;\n  z-index: 4;\n}\n.CodeMirror-gutter-elt {\n  position: absolute;\n  cursor: default;\n  z-index: 4;\n}\n.CodeMirror-gutter-wrapper ::selection { background-color: transparent }\n.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }\n\n.CodeMirror-lines {\n  cursor: text;\n  min-height: 1px; /* prevents collapsing before first draw */\n}\n.CodeMirror pre.CodeMirror-line,\n.CodeMirror pre.CodeMirror-line-like {\n  /* Reset some styles that the rest of the page might have set */\n  -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;\n  border-width: 0;\n  background: transparent;\n  font-family: inherit;\n  font-size: inherit;\n  margin: 0;\n  white-space: pre;\n  word-wrap: normal;\n  line-height: inherit;\n  color: inherit;\n  z-index: 2;\n  position: relative;\n  overflow: visible;\n  -webkit-tap-highlight-color: transparent;\n  -webkit-font-variant-ligatures: contextual;\n  font-variant-ligatures: contextual;\n}\n.CodeMirror-wrap pre.CodeMirror-line,\n.CodeMirror-wrap pre.CodeMirror-line-like {\n  word-wrap: break-word;\n  white-space: pre-wrap;\n  word-break: normal;\n}\n\n.CodeMirror-linebackground {\n  position: absolute;\n  left: 0; right: 0; top: 0; bottom: 0;\n  z-index: 0;\n}\n\n.CodeMirror-linewidget {\n  position: relative;\n  z-index: 2;\n  padding: 0.1px; /* Force widget margins to stay inside of the container */\n}\n\n.CodeMirror-widget {}\n\n.CodeMirror-rtl pre { direction: rtl; }\n\n.CodeMirror-code {\n  outline: none;\n}\n\n/* Force content-box sizing for the elements where we expect it */\n.CodeMirror-scroll,\n.CodeMirror-sizer,\n.CodeMirror-gutter,\n.CodeMirror-gutters,\n.CodeMirror-linenumber {\n  -moz-box-sizing: content-box;\n  box-sizing: content-box;\n}\n\n.CodeMirror-measure {\n  position: absolute;\n  width: 100%;\n  height: 0;\n  overflow: hidden;\n  visibility: hidden;\n}\n\n.CodeMirror-cursor {\n  position: absolute;\n  pointer-events: none;\n}\n.CodeMirror-measure pre { position: static; }\n\ndiv.CodeMirror-cursors {\n  visibility: hidden;\n  position: relative;\n  z-index: 3;\n}\ndiv.CodeMirror-dragcursors {\n  visibility: visible;\n}\n\n.CodeMirror-focused div.CodeMirror-cursors {\n  visibility: visible;\n}\n\n.CodeMirror-selected { background: #d9d9d9; }\n.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }\n.CodeMirror-crosshair { cursor: crosshair; }\n.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }\n.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }\n\n.cm-searching {\n  background-color: #ffa;\n  background-color: rgba(255, 255, 0, .4);\n}\n\n/* Used to force a border model for a node */\n.cm-force-border { padding-right: .1px; }\n\n@media print {\n  /* Hide the cursor when printing */\n  .CodeMirror div.CodeMirror-cursors {\n    visibility: hidden;\n  }\n}\n\n/* See issue #2901 */\n.cm-tab-wrap-hack:after { content: ''; }\n\n/* Help users use markselection to safely style text background */\nspan.CodeMirror-selectedtext { background: none; }\n", ""]);
// Exports
module.exports = exports;


/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./node_modules/jexcel/dist/jexcel.css":
/*!***********************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./node_modules/jexcel/dist/jexcel.css ***!
  \***********************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Imports
var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
exports = ___CSS_LOADER_API_IMPORT___(false);
// Module
exports.push([module.i, ":root {\n    --jexcel-border-color:#000;\n}\n\n.jexcel_container {\n    display:inline-block;\n    padding-right:2px;\n    box-sizing: border-box;\n    overscroll-behavior: contain;\n}\n\n.jexcel_container.fullscreen {\n    position:fixed;\n    top:0px;\n    left:0px;\n    width:100%;\n    height:100%;\n    z-index:21;\n}\n\n.jexcel_container.fullscreen .jexcel_content {\n    overflow:auto;\n    width:100%;\n    height:100%;\n    background-color:#ffffff;\n}\n\n.jexcel_container.fullscreen.with-toolbar {\n    height: calc(100% - 46px);\n}\n\n.jexcel_content {\n    display:inline-block;\n    box-sizing: border-box;\n    padding-right:3px;\n    padding-bottom:3px;\n    position:relative;\n    scrollbar-width: thin;\n    scrollbar-color: #666 transparent;\n}\n\n@supports (-moz-appearance:none) {\n    .jexcel_content { padding-right:10px; } \n}\n\n.jexcel_content::-webkit-scrollbar {\n    width: 5px;\n    height: 5px;\n}\n\n.jexcel_content::-webkit-scrollbar-track {\n    background: #eee;\n}\n \n.jexcel_content::-webkit-scrollbar-thumb {\n  background: #666; \n}\n\n.jexcel {\n    border-collapse:separate;\n    table-layout:fixed;\n    white-space: nowrap;\n    empty-cells:show;\n    border:0px;\n    background-color:#fff;\n    width:0;\n\n    border-top:1px solid transparent;\n    border-left:1px solid transparent;\n    border-right:1px solid #ccc;\n    border-bottom:1px solid #ccc;\n}\n\n.jexcel > thead > tr > td\n{\n    border-top:1px solid #ccc;\n    border-left:1px solid #ccc;\n    border-right:1px solid transparent;\n    border-bottom:1px solid transparent;\n    background-color:#f3f3f3;\n    padding:2px;\n    cursor:pointer;\n    box-sizing: border-box;\n    overflow: hidden;\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index:20;\n}\n\n.with-toolbar .jexcel > thead > tr > td\n{\n    top:42px;\n}\n\n.jexcel > thead.draggable > tr > td::before\n{\n    content:'\\00a0';\n    width:100%;\n    height:3px;\n    position:absolute;\n    bottom:0px;\n    left:0px;\n    cursor:move;\n}\n\n.jexcel > thead.resizable > tr > td::after\n{\n    content:'\\00a0';\n    width:3px;\n    height:100%;\n    position:absolute;\n    top:0px;\n    right:0px;\n    cursor:col-resize;\n}\n\n.jexcel > thead > tr > td.dragging\n{\n    background-color:#fff;\n    opacity:0.5;\n}\n\n.jexcel > thead > tr > td:first-child:after,\n.jexcel > thead > tr.jexcel_nested > td::before,\n.jexcel > thead > tr.jexcel_nested > td::after\n{\n    cursor:default;\n}\n\n.jexcel > thead > tr > td.selected\n{\n    background-color:#dcdcdc;\n}\n\n.jexcel > thead > tr > td.arrow-up\n{\n    background-repeat:no-repeat;\n    background-position:center right 5px;\n    background-image: url(\"data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 14l5-5 5 5H7z' fill='gray'/%3E%3C/svg%3E\");\n    text-decoration:underline;\n}\n\n.jexcel > thead > tr > td.arrow-down\n{\n    background-repeat:no-repeat;\n    background-position:center right 5px;\n    background-image: url(\"data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 10l5 5 5-5H7z' fill='gray'/%3E%3C/svg%3E\");\n    text-decoration:underline;\n}\n\n.jexcel > tbody > tr > td:first-child\n{\n    position:relative;\n    background-color:#f3f3f3;\n    text-align:center;\n}\n\n.jexcel > tbody.resizable > tr > td:first-child::before\n{\n    content:'\\00a0';\n    width:100%;\n    height:3px;\n    position:absolute;\n    bottom:0px;\n    left:0px;\n    cursor:row-resize;\n}\n\n.jexcel > tbody.draggable > tr > td:first-child::after\n{\n    content:'\\00a0';\n    width:3px;\n    height:100%;\n    position:absolute;\n    top:0px;\n    right:0px;\n    cursor:move;\n}\n\n.jexcel > tbody > tr.dragging > td\n{\n    background-color:#eee;\n    opacity:0.5;\n}\n\n.jexcel > tbody > tr > td\n{\n    border-top:1px solid #ccc;\n    border-left:1px solid #ccc;\n    border-right:1px solid transparent;\n    border-bottom:1px solid transparent;\n    padding:4px;\n    white-space: nowrap;\n    box-sizing: border-box;\n    line-height:1em;\n}\n\n.jexcel > tbody > tr > td:last-child\n{\n    overflow:hidden;\n}\n\n.jexcel > tbody > tr > td > img\n{\n    display:inline-block;\n    max-width:100px;\n}\n\n.jexcel > tbody > tr > td.readonly\n{\n    color:rgba(0,0,0,0.3)\n}\n.jexcel > tbody > tr.selected > td:first-child\n{\n    background-color:#dcdcdc;\n}\n.jexcel > tbody > tr > td > select,\n.jexcel > tbody > tr > td > input,\n.jexcel > tbody > tr > td > textarea\n{\n    border:0px;\n    border-radius:0px;\n    outline:0px;\n    width:100%;\n    margin:0px;\n    padding:0px;\n    background-color:transparent;\n    box-sizing: border-box;\n}\n\n.jexcel > tbody > tr > td > textarea\n{\n    resize: none;\n    padding-top:6px !important;\n}\n\n.jexcel > tbody > tr > td > input[type=checkbox]\n{\n    width:12px;\n    margin-top:2px;\n}\n.jexcel > tbody > tr > td > input[type=radio]\n{\n    width:12px;\n    margin-top:2px;\n}\n\n.jexcel > tbody > tr > td > select\n{\n    -webkit-appearance: none;\n    -moz-appearance: none;\n    appearance: none;\n    background-repeat: no-repeat;\n    background-position-x: 100%;\n    background-position-y: 40%;\n    background-image: url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSdibGFjaycgaGVpZ2h0PScyNCcgdmlld0JveD0nMCAwIDI0IDI0JyB3aWR0aD0nMjQnIHhtbG5zPSdodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2Zyc+PHBhdGggZD0nTTcgMTBsNSA1IDUtNXonLz48cGF0aCBkPSdNMCAwaDI0djI0SDB6JyBmaWxsPSdub25lJy8+PC9zdmc+);\n}\n\n.jexcel > tbody > tr > td.jexcel_dropdown\n{\n    background-repeat: no-repeat;\n    background-position:top 50% right 5px;\n    background-image: url(\"data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 10l5 5 5-5H7z' fill='lightgray'/%3E%3C/svg%3E\");\n    text-overflow: ellipsis;\n    overflow-x:hidden;\n}\n\n.jexcel > tbody > tr > td.jexcel_dropdown.jexcel_comments\n{\n    background:url(\"data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 10l5 5 5-5H7z' fill='lightgray'/%3E%3C/svg%3E\") top 50% right 5px no-repeat, url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFuGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHhtcDpNb2RpZnlEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDphMTlhZDJmOC1kMDI2LTI1NDItODhjOS1iZTRkYjkyMmQ0MmQiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDpkOGI5NDUyMS00ZjEwLWQ5NDktYjUwNC0wZmU1N2I3Nzk1MDEiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIHN0RXZ0OndoZW49IjIwMTktMDEtMzFUMTg6NTU6MDhaIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmExOWFkMmY4LWQwMjYtMjU0Mi04OGM5LWJlNGRiOTIyZDQyZCIgc3RFdnQ6d2hlbj0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4En6MDAAAAX0lEQVQYlX3KOw6AIBBAwS32RpJADXfx0pTET+ERZJ8F8RODFtONsG0QAoh0CSDM82dqodaBdQXnfoLZQM7gPai+wjNNE8R4pTuAYNZSKZASqL7CMy0LxNgJp30fKYUDi3+vIqb/+rUAAAAASUVORK5CYII=') top right no-repeat;\n}\n\n.jexcel > tbody > tr > td > .color\n{\n    width:90%;\n    height:10px;\n    margin:auto;\n}\n\n.jexcel > tbody > tr > td > a {\n    text-decoration: underline;\n}\n\n.jexcel > tbody > tr > td.highlight > a {\n    color: blue;\n    cursor: pointer;\n}\n\n.jexcel .highlight {\n    background-color:rgba(0,0,0,0.05);\n}\n\n.jexcel .highlight-top {\n    border-top:1px solid #000; /* var(--jexcel-border-color);*/\n    box-shadow: 0px -1px #ccc;\n}\n\n.jexcel .highlight-left {\n    border-left:1px solid #000; /* var(--jexcel-border-color);*/\n    box-shadow: -1px 0px #ccc;\n}\n\n.jexcel .highlight-right {\n    border-right:1px solid #000; /* var(--jexcel-border-color);*/\n}\n\n.jexcel .highlight-bottom {\n    border-bottom:1px solid #000; /* var(--jexcel-border-color);*/\n}\n\n.jexcel .highlight-top.highlight-left {\n    box-shadow: -1px -1px #ccc;\n    -webkit-box-shadow: -1px -1px #ccc;\n    -moz-box-shadow: -1px -1px #ccc;\n}\n\n.jexcel .highlight-selected\n{\n    background-color:rgba(0,0,0,0.0);\n}\n.jexcel .selection\n{\n    background-color:rgba(0,0,0,0.05);\n}\n.jexcel .selection-left\n{\n    border-left:1px dotted #000;\n}\n.jexcel .selection-right\n{\n    border-right:1px dotted #000;\n}\n.jexcel .selection-top\n{\n    border-top:1px dotted #000;\n}\n.jexcel .selection-bottom\n{\n    border-bottom:1px dotted #000;\n}\n.jexcel_corner\n{\n    position:absolute;\n    background-color: rgb(0, 0, 0);\n    height: 1px;\n    width: 1px;\n    border: 1px solid rgb(255, 255, 255);\n    top:-2000px;\n    left:-2000px;\n    cursor:crosshair;\n    box-sizing: initial;\n    z-index:30;\n    padding: 2px;\n}\n\n.jexcel .editor\n{\n    outline:0px solid transparent;\n    overflow:visible;\n    white-space: nowrap;\n    text-align:left;\n    padding:0px;\n    box-sizing: border-box;\n    overflow:visible !important;\n}\n\n.jexcel .editor > input\n{\n    padding-left:4px;\n}\n\n.jexcel .editor .jupload\n{\n    position:fixed;\n    top:100%;\n    z-index:40;\n    user-select:none;\n    -webkit-font-smoothing: antialiased;\n    font-size: .875rem;\n    letter-spacing: .2px;\n    -webkit-border-radius: 4px;\n    border-radius: 4px;\n    -webkit-box-shadow: 0 8px 10px 1px rgba(0,0,0,0.14), 0 3px 14px 2px rgba(0,0,0,0.12), 0 5px 5px -3px rgba(0,0,0,0.2);\n    box-shadow: 0 8px 10px 1px rgba(0,0,0,0.14), 0 3px 14px 2px rgba(0,0,0,0.12), 0 5px 5px -3px rgba(0,0,0,0.2);\n    padding:10px;\n    background-color:#fff;\n    width:300px;\n    min-height:225px;\n    margin-top:2px;\n}\n\n.jexcel .editor .jupload img\n{\n    width:100%;\n    height:auto;\n}\n\n.jexcel .editor .jclose:after\n{\n    position:absolute;\n    top:0;\n    right:0;\n    margin:10px;\n    content:'close';\n    font-family:'Material icons';\n    font-size:24px;\n    width:24px;\n    height:24px;\n    line-height:24px;\n    cursor:pointer;\n    text-shadow: 0px 0px 5px #fff;\n}\n\n.jexcel, .jexcel td, .jexcel_corner\n{\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n  -khtml-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  -webkit-user-drag: none;\n  -khtml-user-drag: none;\n  -moz-user-drag: none;\n  -o-user-drag: none;\n  user-drag: none;\n}\n\n.jexcel_textarea\n{\n    position:absolute;\n    top:-999px;\n    left:-999px;\n    width:1px;\n    height:1px;\n}\n.jexcel .dragline\n{\n    position:absolute;\n}\n.jexcel .dragline div\n{\n    position:relative;\n    top:-6px;\n    height:5px;\n    width:22px;\n}\n.jexcel .dragline div:hover\n{\n    cursor:move;\n}\n\n.jexcel .onDrag\n{\n    background-color:rgba(0,0,0,0.6);\n}\n\n.jexcel .error\n{\n    border:1px solid red;\n}\n\n.jexcel thead td.resizing\n{\n    border-right-style:dotted !important;\n    border-right-color:red !important;\n}\n\n.jexcel tbody tr.resizing > td\n{\n    border-bottom-style:dotted !important;\n    border-bottom-color:red !important;\n}\n\n.jexcel tbody td.resizing\n{\n    border-right-style:dotted !important;\n    border-right-color:red !important;\n}\n\n.jexcel .jdropdown-header\n{\n    border:0px !important;\n    outline:none !important;\n    width:100% !important;\n    height:100% !important;\n    padding:0px !important;\n    padding-left:8px !important;\n}\n\n.jexcel .jdropdown-container\n{\n    margin-top:1px;\n}\n\n.jexcel .jdropdown-container-header {\n    padding: 0px;\n    margin: 0px;\n    height: inherit;\n}\n\n.jexcel .jdropdown-picker\n{\n    border:0px !important;\n    padding:0px !important;\n    width:inherit;\n    height:inherit;\n}\n\n.jexcel .jexcel_comments\n{\n    background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFuGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHhtcDpNb2RpZnlEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDphMTlhZDJmOC1kMDI2LTI1NDItODhjOS1iZTRkYjkyMmQ0MmQiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDpkOGI5NDUyMS00ZjEwLWQ5NDktYjUwNC0wZmU1N2I3Nzk1MDEiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIHN0RXZ0OndoZW49IjIwMTktMDEtMzFUMTg6NTU6MDhaIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmExOWFkMmY4LWQwMjYtMjU0Mi04OGM5LWJlNGRiOTIyZDQyZCIgc3RFdnQ6d2hlbj0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4En6MDAAAAX0lEQVQYlX3KOw6AIBBAwS32RpJADXfx0pTET+ERZJ8F8RODFtONsG0QAoh0CSDM82dqodaBdQXnfoLZQM7gPai+wjNNE8R4pTuAYNZSKZASqL7CMy0LxNgJp30fKYUDi3+vIqb/+rUAAAAASUVORK5CYII=');\n    background-repeat: no-repeat;\n    background-position: top right;\n}\n\n.jexcel .sp-replacer\n{\n    margin: 2px;\n    border:0px;\n}\n\n.jexcel > thead > tr.jexcel_filter > td > input\n{\n    border:0px;\n    width:100%;\n    outline:none;\n}\n\n.jexcel_about {\n    float: right;\n    font-size: 0.7em;\n    padding: 2px;\n    text-transform: uppercase;\n    letter-spacing: 1px;\n    display: none;\n}\n.jexcel_about a {\n    color: #ccc;\n    text-decoration: none;\n}\n\n.jexcel_about img {\n    display: none;\n}\n\n.jexcel_filter\n{\n    display:flex;\n    justify-content:space-between;\n    margin-bottom:4px;\n}\n\n.jexcel_filter > div\n{\n    padding:8px;\n    align-items:center;\n}\n\n.jexcel_pagination\n{\n    display:flex;\n    justify-content:space-between;\n    align-items:center;\n}\n\n.jexcel_pagination > div\n{\n    display:flex;\n    padding:10px;\n}\n\n.jexcel_pagination > div:last-child\n{\n    padding-right:10px;\n    padding-top:10px;\n}\n\n.jexcel_pagination > div > div\n{\n    text-align:center;\n    width:36px;\n    height:36px;\n    line-height:34px;\n    border:1px solid #ccc;\n    box-sizing: border-box;\n    margin-left:2px;\n    cursor:pointer;\n}\n\n.jexcel_page\n{\n    font-size:0.8em;\n}\n\n.jexcel_page_selected\n{\n    font-weight:bold;\n    background-color:#f3f3f3;\n}\n\n.jexcel_toolbar\n{\n    display:flex;\n    background-color:#f3f3f3;\n    border:1px solid #ccc;\n    padding:4px;\n    margin:0px 2px 4px 1px;\n    position:sticky;\n    top:0px;\n    z-index:21;\n}\n\n.jexcel_toolbar:empty\n{\n    display:none;\n}\n\n.jexcel_toolbar i.jexcel_toolbar_item \n{\n    width:24px;\n    height:24px;\n    padding:4px;\n    cursor:pointer;\n    display:inline-block;\n}\n\n.jexcel_toolbar i.jexcel_toolbar_item:hover \n{\n    background-color:#ddd;\n}\n\n.jexcel_toolbar select.jexcel_toolbar_item \n{\n    margin-left:2px;\n    margin-right:2px;\n    display:inline-block;\n    border:0px;\n    background-color:transparent;\n    padding-right:10px;\n}\n\n.jexcel .dragging-left\n{\n    background-repeat: no-repeat;\n    background-position:top 50% left 0px;\n    background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M14 7l-5 5 5 5V7z'/%3E%3Cpath fill='none' d='M24 0v24H0V0h24z'/%3E%3C/svg%3E\");\n}\n\n.jexcel .dragging-right\n{\n    background-repeat: no-repeat;\n    background-position:top 50% right 0px;\n    background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M10 17l5-5-5-5v10z'/%3E%3Cpath fill='none' d='M0 24V0h24v24H0z'/%3E%3C/svg%3E\");\n}\n\n.jexcel_tabs > .jexcel_tab\n{\n    display:none;\n}\n\n.jexcel_tabs > .jexcel_tab_link\n{\n    display:inline-block;\n    padding:10px;\n    padding-left:20px;\n    padding-right:20px;\n    margin-right:5px;\n    margin-bottom:5px;\n    background-color:#f3f3f3;\n    cursor:pointer;\n}\n\n.jexcel_tabs > .jexcel_tab_link.selected\n{\n    background-color:#ddd;\n}\n\n.jexcel_hidden_index > tbody > tr > td:first-child,\n.jexcel_hidden_index > thead > tr > td:first-child, \n.jexcel_hidden_index > colgroup > col:first-child\n{\n    display:none;\n}\n", ""]);
// Exports
module.exports = exports;


/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./node_modules/jsuites/dist/jsuites.css":
/*!*************************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./node_modules/jsuites/dist/jsuites.css ***!
  \*************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Imports
var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
exports = ___CSS_LOADER_API_IMPORT___(false);
// Module
exports.push([module.i, "\r\n/**\r\n * (c) jSuites Javascript Web Components\r\n *\r\n * Author: Paul Hodel <paul.hodel@gmail.com>\r\n * Website: https://bossanova.uk/jsuites/\r\n * Description: Create amazing web based applications.\r\n *\r\n * MIT License\r\n *\r\n */\r\n\r\n/** General **/\r\n\r\n:root {\r\n    --button-color: #298BA8; \r\n    --active-color: #007aff;\r\n}\r\n\r\n.jdragging {\r\n    opacity:0.2;\r\n    filter: alpha(opacity=20);\r\n}\r\n\r\n.jupload {\r\n    background-image: url(data:image/png;base64,[encoded data, 22716 characters]);\r\n    background-repeat: no-repeat;\r\n    background-size: 100px;\r\n    background-position: center;\r\n    background-color: rgba(230, 230, 230, 0.1);\r\n    border: 1px dotted #eee;\r\n    cursor: pointer;\r\n    box-sizing: border-box;\r\n    width:100%;\r\n    min-height:180px;\r\n}\r\n\r\n.jremove {\r\n    opacity: 0.2;\r\n    filter: alpha(opacity=20);\r\n}\r\n\r\n/** Animations **/\r\n.fade-in {\r\n    animation: fade-in 2s forwards;\r\n}\r\n\r\n.fade-out {\r\n    animation: fade-out 1s forwards;\r\n}\r\n\r\n.slide-left-in {\r\n    animation: slide-left-in 0.4s forwards;\r\n}\r\n\r\n.slide-left-out {\r\n    animation: slide-left-out 0.4s forwards;\r\n}\r\n\r\n.slide-right-in {\r\n    animation: slide-right-in 0.4s forwards;\r\n}\r\n\r\n.slide-right-out {\r\n    animation: slide-right-out 0.4s forwards;\r\n}\r\n\r\n.slide-top-in {\r\n    animation: slide-top-in 0.4s forwards;\r\n}\r\n\r\n.slide-top-out {\r\n    animation: slide-top-out 0.2s forwards;\r\n}\r\n\r\n.slide-bottom-in {\r\n    animation: slide-bottom-in 0.4s forwards;\r\n}\r\n\r\n.slide-bottom-out {\r\n    animation: slide-bottom-out 0.1s forwards;\r\n}\r\n\r\n/** Fadein and Fadeout **/\r\n@keyframes fade-in {\r\n    0% { opacity: 0; }\r\n    100% { opacity: 100; }\r\n}\r\n\r\n@-webkit-keyframes fade-in {\r\n    0% { opacity: 0; }\r\n    100% { opacity: 100; }\r\n}\r\n\r\n@keyframes fade-out {\r\n    0% { opacity: 100; }\r\n    100% { opacity: 0; }\r\n}\r\n\r\n@-webkit-keyframes fade-out {\r\n    0% { opacity: 100; }\r\n    100% { opacity: 0; }\r\n}\r\n\r\n/** Keyframes Left to Right **/\r\n@keyframes slide-left-in {\r\n    0% { transform: translateX(-100%); }\r\n    100% { transform: translateX(0%); }\r\n}\r\n\r\n@-webkit-keyframes slide-left-in {\r\n    0% { transform: translateX(-100%); }\r\n    100% { -webkit-transform: translateX(0%); }\r\n}\r\n    \r\n@keyframes slide-left-out {\r\n    0% { transform: translateX(0%); }\r\n    100% { transform: translateX(-100%); }\r\n}\r\n\r\n@-webkit-keyframes slide-left-out {\r\n    0% { -webkit-transform: translateX(0%); }\r\n    100% { -webkit-transform: translateX(-100%); }\r\n}\r\n\r\n/** Keyframes Right to Left **/\r\n@keyframes slide-right-in {\r\n    0% { transform: translateX(100%); }\r\n    100% { transform: translateX(0%); }\r\n}\r\n\r\n@-webkit-keyframes slide-right-in\r\n{\r\n    0% { transform: translateX(100%); }\r\n    100% { -webkit-transform: translateX(0%); }\r\n}\r\n    \r\n@keyframes slide-right-out {\r\n    0% { transform: translateX(0%); }\r\n    100% { transform: translateX(100%); }\r\n}\r\n\r\n@-webkit-keyframes slide-right-out {\r\n    0% { -webkit-transform: translateX(0%); }\r\n    100% { -webkit-transform: translateX(100%); }\r\n}\r\n\r\n/** Keyframes Top to Bottom **/\r\n@keyframes slide-top-in {\r\n    0% { transform: translateY(-100%); }\r\n    100% { transform: translateY(0%); }\r\n}\r\n\r\n@-webkit-keyframes slide-top-in {\r\n    0% { transform: translateY(-100%); }\r\n    100% { -webkit-transform: translateY(0%); }\r\n}\r\n    \r\n@keyframes slide-top-out {\r\n    0% { transform: translateY(0%); }\r\n    100% { transform: translateY(-100%); }\r\n}\r\n\r\n@-webkit-keyframes slide-top-out {\r\n    0% { -webkit-transform: translateY(0%); }\r\n    100% { -webkit-transform: translateY(-100%); }\r\n}\r\n\r\n/** Keyframes Bottom to Top **/\r\n@keyframes slide-bottom-in {\r\n    0% { transform: translateY(100%); }\r\n    100% { transform: translateY(0%); }\r\n}\r\n\r\n@-webkit-keyframes slide-bottom-in {\r\n    0% { transform: translateY(100%); }\r\n    100% { -webkit-transform: translateY(0%); }\r\n}\r\n    \r\n@keyframes slide-bottom-out {\r\n    0% { transform: translateY(0%); }\r\n    100% { transform: translateY(100%); }\r\n}\r\n\r\n@-webkit-keyframes slide-bottom-out {\r\n    0% { -webkit-transform: translateY(0%); }\r\n    100% { -webkit-transform: translateY(100%); }\r\n}\r\n\r\n\r\n@supports (-webkit-overflow-scrolling: touch) {\r\n    .app .options input:checked:before {\r\n        top:-12px;\r\n    }\r\n}\r\n\r\n@-webkit-keyframes spin {\r\n    from {\r\n        -webkit-transform:rotate(0deg);\r\n    }\r\n    to {\r\n        -webkit-transform:rotate(360deg);\r\n    }\r\n}\r\n\r\n@keyframes spin {\r\n    from {\r\n        transform:rotate(0deg);\r\n    }\r\n    to {\r\n        transform:rotate(360deg);\r\n    }\r\n}\r\n\r\n.unselectable {\r\n    -webkit-touch-callout: none;\r\n    -webkit-user-select: none;\r\n    -khtml-user-select: none;\r\n    -moz-user-select: none;\r\n    -ms-user-select: none;\r\n    user-select: none;\r\n}\r\n\r\n.jbutton {\r\n    padding:4px;\r\n    padding-left:10px;\r\n    padding-right:20px;\r\n    border:1px solid #ddd;\r\n    display:inline-flex;\r\n    border-radius:4px;\r\n    cursor:pointer;\r\n    line-height: 24px;\r\n    -webkit-font-smoothing: antialiased;\r\n}\r\n\r\n.jbutton.blue {\r\n    border:1px solid transparent;\r\n    color:#fff;\r\n    background-color:#1a73e8;\r\n}\r\n\r\n.jbutton::before {\r\n    content: attr(data-icon);\r\n    width: 24px;\r\n    height: 24px;\r\n    font-size: 24px;\r\n    line-height: 24px;\r\n    font-family: 'Material icons';\r\n    color: #999;\r\n    margin-right:2px;\r\n}\r\n\r\n.jbutton.blue::before {\r\n    color: #fff;\r\n}\r\n\r\n.jbutton:hover {\r\n    background-color: #eee;\r\n}\r\n\r\n.jbutton.blue:hover {\r\n    background-color: #1a73e8;\r\n    opacity: 0.8;\r\n}\r\n\r\n/**\n * Date & Datetime picker v1.0.1\n * Author: paul.hodel@gmail.com\n * https://github.com/paulhodel/jtools\n */\n \n.jcalendar {\n    position:relative;\n    z-index:9000;\n    display:none;\n    box-sizing:border-box;\n    -webkit-touch-callout: none;\n    -webkit-user-select: none;\n    -khtml-user-select: none;\n    -moz-user-select: none;\n    -ms-user-select: none;\n    user-select: none;\n    -webkit-tap-highlight-color: rgba(0,0,0,0);\n    -webkit-tap-highlight-color: transparent;\n    min-width:280px;\n}\n\n.jcalendar-focus {\n    display:block;\n}\n\n.jcalendar-backdrop {\n    position:fixed;\n    top:0px;\n    left:0px;\n    z-index:9000;\n    min-width:100%;\n    min-height:100%;\n    background-color:rgba(0,0,0,0.5);\n    border:0px;\n    padding:0px;\n    display:none;\n}\n\n.jcalendar-container {\n    position:relative;\n    box-sizing:border-box;\n}\n\n.jcalendar-content {\n    position:absolute;\n    z-index:9001;\n    -webkit-box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.39);\n    -moz-box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.39);\n    box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.39);\n    background-color:#fff;\n}\n\n.jcalendar-table > table {\n    width:100%;\n    background-color:#fff;\n}\n\n.jcalendar-table > table > tbody td {\n    box-sizing:border-box;\n    cursor:pointer;\n    padding:9px;\n    font-size:0.9em;\n}\n\n.jcalendar-table > table > thead {\n    cursor:pointer;\n}\n\n.jcalendar-header {\n    text-align:center;\n}\n\n.jcalendar-header span {\n    margin-right:4px;\n    font-size:1.1em;\n    font-weight:bold;\n}\n\n.jcalendar-prev {\n    cursor:pointer;\n    background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M15.41 16.59L10.83 12l4.58-4.59L14 6l-6 6 6 6 1.41-1.41z' fill='%23000' /%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3C/svg%3E\");\n    background-position:center;\n    background-repeat:no-repeat;\n}\n\n.jcalendar-next {\n    cursor:pointer;\n    background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z' fill='%23000' /%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3C/svg%3E\");\n    background-position:center;\n    background-repeat:no-repeat;\n}\n\n.jcalendar-weekday {\n    font-weight: 600;\n    background-color: #fcfcfc;\n    padding: 14px;\n}\n\n.jcalendar thead td {\n    padding:10px;\n    height:40px;\n}\n\n.jcalendar tfoot td {\n    padding:10px;\n}\n\n.jcalendar-months td, .jcalendar-years td {\n    height:24px;\n} \n\n.jcalendar-input {\n    padding-right:18px;\n    background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='gray'%3E%3Cpath d='M20 3h-1V1h-2v2H7V1H5v2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 18H4V8h16v13z'/%3E%3Cpath fill='none' d='M0 0h24v24H0z'/%3E%3C/svg%3E\");\n    background-position:top 50% right 5px;\n    background-repeat:no-repeat;\n    box-sizing: border-box;\n}\n\n.jcalendar-done {\n    -webkit-box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.39);\n    -moz-box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.39);\n    box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.39);\n    background-color:#fff;\n}\n\n.jcalendar-update {\n    border:1px solid #ccc;\n    background-color:#fff;\n    border-radius:4px;\n    padding:5px;\n    width:100%;\n}\n\n.jcalendar select {\n    width:55px;\n    display:inline-block;\n    border:0px;\n    padding:4px;\n    text-align:center;\n    font-size:1.1em;\n    user-select:none;\n    margin-right:10px;\n}\n\n.jcalendar select:first-child\n{\n    margin-right:2px;\n}\n\n.jcalendar-selected {\n    background-color:#eee;\n}\n\n.jcalendar-reset, .jcalendar-confirm {\n    text-transform:uppercase;\n    cursor:pointer;\n    color: var(--active-color);\n}\n\n.jcalendar-controls {\n    padding:15px;\n\n    -webkit-box-sizing: border-box;\n    box-sizing: border-box;\n    vertical-align:middle;\n\n    display: -webkit-box;\n    display: -moz-box;\n    display: -ms-flexbox;\n    display: -webkit-flex;\n    display: flex;\n\n    -webkit-flex-flow: row wrap;\n    justify-content: space-between;\n    align-items:center;\n\n    border-bottom:1px solid #ddd;\n}\n\n.jcalendar-controls div {\n    font-weight:bold;\n}\n\n.jcalendar-fullsize  {\n    position:fixed;\n    width:100%;\n    top:0px;\n    left:0px;\n}\n\n.jcalendar-fullsize .jcalendar-content\n{\n    position:fixed;\n    width:100%;\n    left:0px;\n    bottom:0px;\n}\n\n.jcalendar-focus.jcalendar-fullsize .jcalendar-backdrop {\n    display:block;\n}\n\n.jcalendar-sunday {\n    color: red;\n}\n.jcalendar-disabled {\n    color: #ccc;\n}\r\n\r\n.jcolor {\r\n    position: absolute;\r\n    display: none;\r\n    outline: none;\r\n}\r\n\r\n.jcolor-content {\r\n    position: absolute;\r\n    left: 0px;\r\n    z-index: 9000;\r\n    user-select: none;\r\n    -webkit-font-smoothing: antialiased;\r\n    font-size: .875rem;\r\n    letter-spacing: .2px;\r\n    -webkit-border-radius: 4px;\r\n    border-radius: 4px;\r\n    -webkit-box-shadow: 0 8px 10px 1px rgba(0,0,0,0.14), 0 3px 14px 2px rgba(0,0,0,0.12), 0 5px 5px -3px rgba(0,0,0,0.2);\r\n    box-shadow: 0 8px 10px 1px rgba(0,0,0,0.14), 0 3px 14px 2px rgba(0,0,0,0.12), 0 5px 5px -3px rgba(0,0,0,0.2);\r\n    background-color:#fff;\r\n    box-sizing: border-box;\r\n}\r\n\r\n.jcolor-content table {\r\n    padding: 7px;\r\n    box-sizing: border-box;\r\n}\r\n\r\n.jcolor-focus {\r\n    display:block;\r\n}\r\n\r\n.jcolor table {\r\n    width:100%;\r\n    height:100%;\r\n}\r\n\r\n.jcolor td {\r\n    border:2px solid #fff;\r\n}\r\n\r\n.jcolor-selected {\r\n    border:2px solid #000 !important;\r\n    background-repeat:no-repeat;\r\n    background-size: cover;\r\n    background-position:0 0;\r\n    background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3Cpath d='M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z' fill='white'/%3E%3C/svg%3E\");\r\n}\r\n\r\n.jcolor-fullscreen {\r\n    position: fixed;\r\n    bottom: 0px;\r\n    width:100%;\r\n    max-height:250px;\r\n    border-radius: 0px;\r\n    box-sizing: border-box;\r\n}\r\n\r\n.jcolor-close {\r\n    display: none;\r\n    font-size: 1em;\r\n    color: var(--active-color);\r\n    text-transform: uppercase;\r\n    text-align: right;\r\n    padding: 15px;\r\n    font-weight: bold;\r\n    -webkit-box-shadow: 1px 0px 1px 0px rgba(0,0,0,0.39);\r\n    -moz-box-shadow: 1px 0px 1px 0px rgba(0,0,0,0.39);\r\n    box-shadow: 1px 0px 1px 0px rgba(0,0,0,0.39);\r\n    width: 100%;\r\n    box-sizing: border-box;\r\n}\r\n\r\n.jcolor-fullscreen .jcolor-close {\r\n    display: block;\r\n}\r\n\r\n.jcolor-backdrop {\r\n    position: fixed;\r\n    top: 0px;\r\n    left: 0px;\r\n    min-width: 100%;\r\n    min-height: 100%;\r\n    background-color: rgba(0,0,0,0.5);\r\n    border: 0px;\r\n    padding: 0px;\r\n    z-index: 8000;\r\n    display: none;\r\n    \r\n  -webkit-touch-callout: none; /* iOS Safari */\r\n    -webkit-user-select: none; /* Safari */\r\n     -khtml-user-select: none; /* Konqueror HTML */\r\n       -moz-user-select: none; /* Firefox */\r\n        -ms-user-select: none; /* Internet Explorer/Edge */\r\n            user-select: none; /* Non-prefixed version, currently\r\n                                  supported by Chrome and Opera */\r\n}\r\n\r\n/**\r\n * Contextmenu v1.0.1\r\n * Author: paul.hodel@gmail.com\r\n * https://github.com/paulhodel/jsuites\r\n */\r\n \r\n.jcontextmenu {\r\n    position:fixed;\r\n    z-index:10000;\r\n    background:#fff;\r\n    color: #555;\r\n    font-size: 11px;\r\n    -webkit-user-select: none;\r\n    -moz-user-select: none;\r\n    user-select: none;\r\n    -webkit-box-shadow: 2px 2px 2px 0px rgba(143, 144, 145, 1);\r\n    -moz-box-shadow: 2px 2px 2px 0px rgba(143, 144, 145, 1);\r\n    box-shadow: 2px 2px 2px 0px rgba(143, 144, 145, 1);\r\n    border: 1px solid #C6C6C6;\r\n    padding: 0px;\r\n    padding-top:4px;\r\n    padding-bottom:4px;\r\n    margin:0px;\r\n    outline:none;\r\n    display:none;\r\n}\r\n\r\n.jcontextmenu.jcontextmenu-focus {\r\n    display:inline-block;\r\n}\r\n\r\n.jcontextmenu > div {\r\n    box-sizing: border-box;\r\n    display: block;\r\n    padding: 8px 8px 8px 28px;\r\n    width: 250px;\r\n    position: relative;\r\n    cursor: default;\r\n    font-size: 11px;\r\n    font-family:sans-serif;\r\n}\r\n\r\n.jcontextmenu > div a {\r\n    color: #555;\r\n    text-decoration: none;\r\n}\r\n\r\n.jcontextmenu > div span {\r\n    float: right;\r\n    margin-right:10px;\r\n}\r\n\r\n.jcontextmenu .contextmenu-disabled {\r\n    color: #a1a192;\r\n}\r\n\r\n.jcontextmenu > div:not(.contextmenu-line):hover {\r\n    background: #ebebeb;\r\n}\r\n\r\n.jcontextmenu > div.contextmenu-line {\r\n    border-top: 1px solid #e9e9e9;\r\n    margin-top:5px;\r\n    padding:2px;\r\n}\r\n\r\n.jcontextmenu hr {\r\n    border: 1px solid #e9e9e9;\r\n    border-bottom: 0;\r\n    margin-top:5px;\r\n    margin-bottom:5px;\r\n}\r\n\r\n/**\r\n * Dialog v1.0.1\r\n * Author: paul.hodel@gmail.com\r\n * https://github.com/paulhodel/jtools\r\n */\r\n \r\n.jdialog\r\n{\r\n    position:fixed;\r\n    left: 0;\r\n    top: 0;\r\n    width: 100%;\r\n    min-height:100%;\r\n    z-index:10000;\r\n\r\n    background-color:rgba(0,0,0,0.5);\r\n    -webkit-transition-duration: .1s;\r\n    transition-duration: .1s;\r\n    display: flex;\r\n    -ms-flex-align: center;\r\n    -webkit-align-items: center;\r\n    -webkit-box-align: center;\r\n\r\n    align-items: center;\r\n}\r\n\r\n.jdialog .jdialog-container\r\n{\r\n    width:100%;\r\n    max-width:280px;\r\n    box-sizing: border-box;\r\n    margin:0 auto;\r\n    vertical-align:middle;\r\n    background-color:#fff;\r\n    border-radius:10px;\r\n\r\n    opacity: 0;\r\n    transition: opacity .2s;\r\n    -webkit-transition: opacity .2s;\r\n}\r\n\r\n.jdialog .jdialog-header\r\n{\r\n    padding:10px;\r\n    text-align:center;\r\n}\r\n\r\n.jdialog .jdialog-header .jdialog-title\r\n{\r\n    font-size:1em;\r\n    font-weight:bold;\r\n    padding:8px;\r\n}\r\n\r\n.jdialog .jdialog-header .jdialog-message\r\n{\r\n    padding: 4px;\r\n}\r\n\r\n.jdialog .jdialog-footer\r\n{\r\n    border-top: 1px solid #ccc;\r\n    display: flex;\r\n    flex-wrap: wrap;\r\n    align-content: stretch;\r\n    align-items: center;\r\n    text-align: center;\r\n}\r\n\r\n.jdialog .jdialog-footer > div\r\n{\r\n    padding:4px;\r\n    border-left:1px solid #ccc;\r\n    text-align:center;\r\n    width:100%;\r\n}\r\n.jdialog .jdialog-footer > div:first-child\r\n{\r\n    border-left:0px;\r\n}\r\n\r\n.jdialog .jdialog-footer input\r\n{\r\n    border: 0px;\r\n    margin: 0px;\r\n    background-color: transparent;\r\n    color: var(--active-color);\r\n    width: 100%;\r\n    outline: none;\r\n}\r\n\r\n/**\n * (c) 2013 jDropdown\n * http://www.github.com/paulhodel/jdropdown\n *\n * @author: Paul Hodel <paul.hodel@gmail.com>\n * @description: Custom dropdowns\n */\n\n.jdropdown\n{\n    cursor:pointer;\n    -webkit-touch-callout: none;\n    -webkit-user-select: none;\n    -khtml-user-select: none;\n    -moz-user-select: none;\n    -ms-user-select: none;\n    user-select: none;\n    box-sizing: border-box;\n    background:#fff;\n    -webkit-tap-highlight-color: transparent;\n    display: inline-block;\n}\n\n.jdropdown-header::placeholder\n{\n    color:#000;\n}\n\n.jdropdown-backdrop\n{\n    position:fixed;\n    top:0px;\n    left:0px;\n    min-width:100%;\n    min-height:100%;\n    background-color:rgba(0,0,0,0.5);\n    border:0px;\n    padding:0px;\n    z-index:8000;\n    display:none;\n}\n\n.jdropdown-focus\n{\n    position:relative;\n}\n\n.jdropdown-focus .jdropdown-container\n{\n    transform: translate3d(0,0,0);\n}\n\n.jdropdown-focus .jdropdown-header\n{\n    outline:auto 5px -webkit-focus-ring-color;\n}\n\n.jdropdown-container-header\n{\n    padding:0px;\n    margin:0px;\n    position:relative;\n}\n\n.jdropdown-header\n{\n    width:100%;\n    appearance: none;\n    background-repeat: no-repeat;\n    background-position:top 50% right 5px;\n    background-image: url(\"data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 10l5 5 5-5H7z' fill='gray'/%3E%3C/svg%3E\");\n    text-overflow: ellipsis;\n    cursor:pointer;\n    box-sizing: border-box;\n    -webkit-appearance: none;\n    -moz-appearance: none;\n    padding-right:24px;\n}\n\n.jdropdown-insert-button\n{\n    font-size: 1.4em;\n    text-transform: uppercase;\n    position:absolute;\n    right: 30px;\n    top: 4px;\n    display:none;\n}\n\n.jdropdown-container\n{\n    min-width: inherit;\n    transform: translate3d(-10000px,0,0);\n    position:absolute;\n    z-index:9001;\n}\n\n.jdropdown-close\n{\n    display:none;\n    font-size:1em;\n    color: var(--active-color);\n    text-transform:uppercase;\n    text-align:right;\n    padding:15px;\n    font-weight:bold;\n}\n\n.jdropdown-content\n{\n    min-width:inherit;\n    margin:0px;\n    box-sizing:border-box;\n}\n\n.jdropdown-content:empty\n{\n}\n\n.jdropdown-item\n{\n    white-space: nowrap;\n    text-align: left;\n    text-overflow: ellipsis;\n    overflow-x: hidden;\n    color: #000;\n    display: flex;\n    align-items: center;\n}\n\n.jdropdown-image\n{\n    margin-right:10px;\n    width: 32px;\n    height: 32px;\n    border-radius:20px;\n}\n\n.jdropdown-image-small\n{\n    width:24px;\n    height:24px;\n}\n\n.jdropdown-title\n{\n    font-size: 0.7em;\n    text-overflow: ellipsis;\n    overflow-x: hidden;\n    display: block;\n}\n\n/** Default visual **/\n\n.jdropdown-default .jdropdown-header\n{\n    border:1px solid #ccc;\n    padding:5px;\n    padding-left:10px;\n    padding-right:16px;\n}\n\n.jdropdown-default .jdropdown-container\n{\n    background-color:#fff;\n}\n\n.jdropdown-default.jdropdown-focus.jdropdown-insert .jdropdown-header {\n    padding-right:50px;\n}\n\n.jdropdown-default.jdropdown-focus.jdropdown-insert .jdropdown-insert-button {\n    display:block;\n}\n\n.jdropdown-default .jdropdown-content\n{\n    min-width:inherit;\n    border:1px solid #8fb1e3;\n    margin:0px;\n    background-color:#fff;\n    box-sizing:border-box;\n    min-height:10px;\n    max-height:215px;\n    overflow-y:auto;\n}\n\n.jdropdown-default .jdropdown-item\n{\n    padding:4px;\n    padding-left:8px;\n    padding-right:40px;\n}\n\n.jdropdown-default .jdropdown-item:hover\n{\n    background-color:#1f93ff;\n    color:#fff;\n}\n\n.jdropdown-default .jdropdown-cursor\n{\n    background-color:#1f93ff;\n    color:#fff;\n}\n\n.jdropdown-default .jdropdown-selected\n{\n    background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIiAvPjxwYXRoIGQ9Ik05IDE2LjE3TDQuODMgMTJsLTEuNDIgMS40MUw5IDE5IDIxIDdsLTEuNDEtMS40MXoiIGZpbGw9IndoaXRlIiAvPjwvc3ZnPgo=');\n    background-repeat:no-repeat;\n    background-position:top 50% right 5px;\n    background-color:#1f93ff;\n    color:#fff;\n}\n\n.jdropdown-default .jdropdown-group\n{\n    margin-top:5px;\n}\n\n.jdropdown-default .jdropdown-group .jdropdown-item\n{\n    padding-left:16px;\n}\n\n.jdropdown-default .jdropdown-group-name\n{\n    padding-left:8px;\n    font-weight:bold;\n}\n\n\n/** Default render for mobile **/\n\n.jdropdown-picker.jdropdown-focus .jdropdown-header {\n    outline: none;\n}\n\n.jdropdown-picker.jdropdown-focus .jdropdown-backdrop {\n    display:block;\n}\n\n.jdropdown-picker .jdropdown-container\n{\n    position:fixed;\n    bottom:0px;\n    left:0px;\n    border-bottom:1px solid #e6e6e8;\n    width:100%;\n    background-color:#fff;\n    box-sizing: border-box;\n}\n\n.jdropdown-picker .jdropdown-close\n{\n    -webkit-box-shadow: 0px -1px 5px 0px rgba(0,0,0,0.39);\n    -moz-box-shadow: 0px -1px 5px 0px rgba(0,0,0,0.39);\n    box-shadow: 0px -1px 5px 0px rgba(0,0,0,0.39);\n    background-color:#fff;\n    display:block;\n}\n\n.jdropdown-picker .jdropdown-content\n{\n    overflow-y:scroll;\n    height:280px;\n    background-color:#fafafa;\n    border-top:1px solid #e6e6e8;\n}\n\n.jdropdown-picker .jdropdown-group-name\n{\n    font-size: 1em;\n    text-transform: uppercase;\n    padding-top:10px;\n    padding-bottom:10px;\n    display: block;\n    border-bottom: 1px solid #e6e6e8;\n    padding-left:20px;\n    padding-right:20px;\n    text-align:center;\n    font-weight:bold;\n}\n\n.jdropdown-picker .jdropdown-item\n{\n    font-size: 1em;\n    text-transform: uppercase;\n    padding-top:10px;\n    padding-bottom:10px;\n    border-bottom: 1px solid #e6e6e8;\n    padding-left:20px;\n    padding-right:20px;\n}\n\n.jdropdown-picker .jdropdown-selected\n{\n    background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIiAvPjxwYXRoIGQ9Ik05IDE2LjE3TDQuODMgMTJsLTEuNDIgMS40MUw5IDE5IDIxIDdsLTEuNDEtMS40MXoiIGZpbGw9IndoaXRlIiAvPjwvc3ZnPgo=');\n    background-repeat:no-repeat;\n    background-position:top 50% right 15px;\n    background-color:#1f93ff;\n    color:#fff;\n}\n\n.jdropdown-picker .jdropdown-cursor\n{\n    background-color:#1f93ff;\n    color:#fff;\n}\n\n/** Default render for mobile searchbar **/\n\n.jdropdown-searchbar.jdropdown-focus\n{\n    position:fixed;\n    top:0px;\n    left:0px;\n    width:100%;\n    height:100%;\n    background-color:#fafafa;\n    padding:0px;\n    z-index:9001;\n    overflow-y:scroll;\n}\n\n.jdropdown-searchbar.jdropdown-focus .jdropdown-container-header\n{\n    padding:10px;\n    background-color:#fff;\n    box-shadow: 0 1px 2px rgba(0,0,0,.1);\n}\n\n.jdropdown-searchbar.jdropdown-focus .jdropdown-header\n{\n    border: 0px;\n    background-repeat: no-repeat;\n    background-position-x: 0%;\n    background-position-y: 40%;\n    background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTUuNSAxNGgtLjc5bC0uMjgtLjI3QzE1LjQxIDEyLjU5IDE2IDExLjExIDE2IDkuNSAxNiA1LjkxIDEzLjA5IDMgOS41IDNTMyA1LjkxIDMgOS41IDUuOTEgMTYgOS41IDE2YzEuNjEgMCAzLjA5LS41OSA0LjIzLTEuNTdsLjI3LjI4di43OWw1IDQuOTlMMjAuNDkgMTlsLTQuOTktNXptLTYgMEM3LjAxIDE0IDUgMTEuOTkgNSA5LjVTNy4wMSA1IDkuNSA1IDE0IDcuMDEgMTQgOS41IDExLjk5IDE0IDkuNSAxNHoiIGZpbGw9IiNlNmU2ZTgiLz48cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+PC9zdmc+);\n    padding-left: 30px !important;\n    padding-right: 60px !important;\n    outline: none;\n}\n\n.jdropdown-searchbar.jdropdown-focus .jdropdown-close\n{\n    display:block;\n}\n\n.jdropdown-searchbar .jdropdown-container\n{\n    width:100%;\n}\n\n.jdropdown-searchbar .jdropdown-close\n{\n    position:fixed;\n    top:0px;\n    right:0px;\n}\n\n.jdropdown-searchbar .jdropdown-content\n{\n    margin-top:10px;\n}\n\n.jdropdown-searchbar .jdropdown-group\n{\n    margin-top:10px;\n    margin-bottom:15px;\n    background-color:#fff;\n}\n\n.jdropdown-searchbar .jdropdown-group-name\n{\n    border-top: 1px solid #e6e6e8;\n    border-bottom: 1px solid #e6e6e8;\n    padding:10px;\n    padding-left:12px;\n    font-weight:bold;\n}\n\n.jdropdown-searchbar .jdropdown-group-arrow\n{\n    float:right;\n    width:24px;\n    height:24px;\n    background-repeat:no-repeat;\n}\n\n.jdropdown-searchbar .jdropdown-group-arrow-down\n{\n    background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNNy40MSA4LjU5TDEyIDEzLjE3bDQuNTktNC41OEwxOCAxMGwtNiA2LTYtNiAxLjQxLTEuNDF6Ii8+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgyNHYyNEgwVjB6Ii8+PC9zdmc+);\n}\n\n.jdropdown-searchbar .jdropdown-group-arrow-up\n{\n    background-image: url(data:image/svg+xml;base64,CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTcuNDEgMTUuNDFMMTIgMTAuODNsNC41OSA0LjU4TDE4IDE0bC02LTYtNiA2eiIvPjxwYXRoIGQ9Ik0wIDBoMjR2MjRIMHoiIGZpbGw9Im5vbmUiLz48L3N2Zz4=);\n}\n\n.jdropdown-searchbar .jdropdown-item\n{\n    padding-top:10px;\n    padding-bottom:10px;\n    border-bottom: 1px solid #e6e6e8;\n    padding-left:15px;\n    padding-right:40px;\n    background-color:#fff;\n    font-size:0.9em;\n}\n\n.jdropdown-searchbar .jdropdown-description {\n    text-overflow: ellipsis;\n    overflow: hidden;\n    max-width: calc(100% - 20px);\n}\n\n.jdropdown-searchbar .jdropdown-content > .jdropdown-item:first-child\n{\n    border-top: 1px solid #e6e6e8;\n}\n\n.jdropdown-searchbar .jdropdown-selected\n{\n    background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTkgMTYuMTdMNC44MyAxMmwtMS40MiAxLjQxTDkgMTkgMjEgN2wtMS40MS0xLjQxeiIgZmlsbD0iIzAwN2FmZiIvPjwvc3ZnPg==');\n    background-repeat:no-repeat;\n    background-position:top 50% right 15px;\n}\n\n/** List render **/\n\n.jdropdown-list\n{\n}\n\n.jdropdown-list .jdropdown-container\n{\n    display:block;\n}\n\n.jdropdown-list .jdropdown-header\n{\n    display:none;\n}\n\n.jdropdown-list .jdropdown-group\n{\n    background-color:#fff;\n}\n\n.jdropdown-list .jdropdown-group-name\n{\n    border-bottom: 1px solid #e6e6e8;\n    padding-top:10px;\n    padding-bottom:10px;\n    font-weight:bold;\n}\n\n.jdropdown-list .jdropdown-item\n{\n    padding-top:10px;\n    padding-bottom:10px;\n    border-bottom: 1px solid #e6e6e8;\n    padding-left:10px;\n    padding-right:40px;\n    background-color:#fff;\n}\n\n.jdropdown-list .jdropdown-selected\n{\n    background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTkgMTYuMTdMNC44MyAxMmwtMS40MiAxLjQxTDkgMTkgMjEgN2wtMS40MS0xLjQxeiIgZmlsbD0iIzAwN2FmZiIvPjwvc3ZnPg==');\n    background-repeat:no-repeat;\n    background-position:top 50% right 10px;\n}\n\n@media only screen and (max-device-width : 800px)\n{\n    .jdropdown-list\n    {\n        width:100% !important;\n        border:0px;\n        padding:0px;\n    }\n\n    .jdropdown-list .jdropdown-container\n    {\n        min-width:100%;\n    }\n}\n\n.app .jdropdown-item {\n    text-transform:uppercase;\n}\r\n\r\n/**\r\n * (c) jTools Text Editor\r\n * https://github.com/paulhodel/jtools\r\n *\r\n * @author: Paul Hodel <paul.hodel@gmail.com>\r\n * @description: Inline richtext editor\r\n */\r\n\r\n .jeditor-container {\r\n    border:1px solid #ccc;\r\n}\r\n\r\n.jeditor-dragging {\r\n     border:1px dashed #000;\r\n}\r\n\r\n.jeditor-container.jeditor-padding {\r\n    padding:10px;\r\n}\r\n\r\n.jeditor {\r\n    outline:none;\r\n    word-break: break-word;\r\n\r\n}\r\n\r\n.jeditor-container.jeditor-padding .jeditor {\r\n    min-height:120px;\r\n    margin-bottom:10px;\r\n    padding:10px;\r\n}\r\n\r\n.jeditor-toolbar {\r\n    cursor: pointer;\r\n    display: -webkit-inline-box;\r\n    display: -webkit-inline-flex;\r\n    display: -ms-inline-flexbox;\r\n    display: inline-flex;\r\n    -webkit-align-items: center;\r\n    align-items: center;\r\n    border: none;\r\n    -webkit-border-radius: 2px;\r\n    border-radius: 2px;\r\n    -webkit-box-shadow: 0 4px 5px 0 rgba(0,0,0,0.14), 0 1px 10px 0 rgba(0,0,0,0.12), 0 2px 4px -1px rgba(0,0,0,0.2);\r\n    box-shadow: 0 4px 5px 0 rgba(0,0,0,0.14), 0 1px 10px 0 rgba(0,0,0,0.12), 0 2px 4px -1px rgba(0,0,0,0.2);\r\n    margin: 0px;\r\n    margin-bottom:5px;\r\n    padding: 5px;\r\n    white-space: nowrap;\r\n    border-radius:2px;\r\n}\r\n\r\n.jeditor-toolbar .jeditor-toolbar-group {\r\n    background-color:#f3f3f3;\r\n}\r\n\r\n.jeditor-toolbar div {\r\n    padding:4px;\r\n}\r\n\r\n.jeditor-toolbar div.jeditor-toolbar-button {\r\n    padding-left:20px;\r\n    padding-right:20px;\r\n}\r\n\r\n.jeditor-toolbar div {\r\n    -webkit-user-select: none;  /* Chrome all / Safari all */\r\n    -moz-user-select: none;     /* Firefox all */\r\n    -ms-user-select: none;      /* IE 10+ */\r\n    user-select: none;          /* Likely future */   \r\n}\r\n\r\n.jeditor-toolbar div i {\r\n    display:block;\r\n    font-size:20px;\r\n    width:20px;\r\n    height:20px;\r\n    line-height:20px;\r\n    color:#777;\r\n}\r\n\r\n.jeditor-toolbar select {\r\n    border:0px;\r\n    background-color:transparent;\r\n    padding:0px;\r\n    padding-left:5px;\r\n    padding-right:5px;\r\n    display:block;\r\n    height:32px;\r\n    outline:none;\r\n    border-radius:4px;\r\n    margin:1px;\r\n}\r\n\r\n.jeditor-toolbar .jtoolbar-divisor {\r\n    width: 2px;\r\n    height: 28px;\r\n    padding: 0px;\r\n    margin: 4px;\r\n    background-color: #f2f2f2;\r\n}\r\n\r\n.jeditor-toolbar select option {\r\n    background-color:#fff;\r\n    padding:20px;\r\n}\r\n\r\n.jeditor-toolbar div:hover, .toolbar select:hover {\r\n    background-color:#f2f2f2;\r\n    border-radius:2px;\r\n}\r\n\r\n.jeditor-toolbar div:hover i {\r\n    color:#222;\r\n}\r\n\r\n.jeditor-toolbar div.selected {\r\n    background-color:#cecece;\r\n}\r\n\r\n.jeditor-thumbs {\r\n    display:inline-block;\r\n    margin-right:5px;\r\n}\r\n\r\n.jeditor-thumbs-container {\r\n    margin-top:15px;\r\n}\r\n\r\n.jeditor-thumbs .close {\r\n    position:absolute;\r\n    height:1px;\r\n}\r\n\r\n.jeditor-thumbs .close i {\r\n    color:#fff;\r\n    text-shadow: 0px 0px 2px #000;\r\n    position:relative;\r\n    top:5px;\r\n    left:72px;\r\n}\r\n\r\n.jeditor-thumbs img {\r\n    border:1px solid #eee;\r\n    width:100px;\r\n    height:100px;\r\n}\r\n\r\n.jeditor-users {\r\n    position:absolute;\r\n    border:1px solid #ccc;\r\n    background-color:#fff;\r\n    padding-top:5px;\r\n    padding-bottom:5px;\r\n    max-height:220px;\r\n    overflow-y:scroll;\r\n}\r\n\r\n.jeditor-users > div\r\n{\r\n    display:flex;\r\n    align-items:center;\r\n    min-width:220px;\r\n    padding:10px;\r\n    padding-top:5px;\r\n    padding-bottom:5px;\r\n}\r\n\r\n.jeditor-users > div:hover\r\n{\r\n    background-color:#efefef;\r\n}\r\n\r\n.jeditor-users > div img\r\n{\r\n    width:24px;\r\n    height:24px;\r\n    border-radius:12px;\r\n    margin-right:5px;\r\n}\r\n\r\n/** Snippet **/\r\n\r\n.snippet {\r\n    margin-top:15px;\r\n    cursor:pointer;\r\n    border: 1px solid #eee;\r\n    position:relative;\r\n}\r\n\r\n.snippet:focus {\r\n    outline: none;\r\n}\r\n\r\n.snippet img {\r\n    width:25%;\r\n    max-width:120px;\r\n    float:left;\r\n    margin-right:10px;\r\n    margin-bottom:10px;\r\n}\r\n\r\n.snippet .snippet-title {\r\n    margin-top:15px;\r\n    font-size:1.4em;\r\n}\r\n\r\n.snippet .snippet-description {\r\n    margin-top:5px;\r\n    font-size:1em;\r\n}\r\n\r\n.snippet .snippet-host {\r\n    margin-top:10px;\r\n    margin-bottom:10px;\r\n    text-transform:uppercase;\r\n    font-size:0.8em;\r\n    color:#777;\r\n    text-align:right;\r\n}\r\n\r\n.snippet .snippet-url {\r\n    display:none;\r\n}\r\n\r\n.jeditor .snippet:after {\r\n    content:'';\r\n    background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E\");\r\n    position:absolute;\r\n    top:0;\r\n    right:0;\r\n    margin:14px;\r\n    font-size:24px;\r\n    width:24px;\r\n    height:24px;\r\n    cursor:pointer;\r\n    text-shadow: 0px 0px 5px #fff;\r\n}\r\n\r\n.snippet div, .snippet img {\r\n    -webkit-user-select: none;\r\n    -khtml-user-select: none;\r\n    -moz-user-select: none;\r\n    -o-user-select: none;\r\n    user-select: none;\r\n\r\n    -webkit-user-drag: none;\r\n    -khtml-user-drag: none;\r\n    -moz-user-drag: none;\r\n    -o-user-drag: none;\r\n}\r\n\r\n.jeditor img {\r\n    border:2px solid transparent;\r\n    box-sizing: border-box;\r\n}\r\n\r\n.jeditor img:focus {\r\n    border:2px solid #0096FD;\r\n}\r\n\r\n.jeditor .pdf {\r\n    background-image: url(\"data:image/svg+xml,%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 512 512' style='enable-background:new 0 0 512 512;' xml:space='preserve'%3E%3Cpath style='fill:%23C30B15;' d='M511.344,274.266C511.77,268.231,512,262.143,512,256C512,114.615,397.385,0,256,0S0,114.615,0,256 c0,117.769,79.53,216.949,187.809,246.801L511.344,274.266z'/%3E%3Cpath style='fill:%2385080E;' d='M511.344,274.266L314.991,77.913L119.096,434.087l68.714,68.714C209.522,508.787,232.385,512,256,512 C391.243,512,501.976,407.125,511.344,274.266z'/%3E%3Cpolygon style='fill:%23FFFFFF;' points='278.328,333.913 255.711,77.913 119.096,77.913 119.096,311.652 '/%3E%3Cpolygon style='fill:%23E8E6E6;' points='392.904,311.652 392.904,155.826 337.252,133.565 314.991,77.913 255.711,77.913 256.067,333.913 '/%3E%3Cpolygon style='fill:%23FFFFFF;' points='314.991,155.826 314.991,77.913 392.904,155.826 '/%3E%3Crect x='119.096' y='311.652' style='fill:%23FC0F1A;' width='273.809' height='122.435'/%3E%3Cg%3E%3Cpath style='fill:%23FFFFFF;' d='M204.871,346.387c13.547,0,21.341,6.659,21.341,18.465c0,12.412-7.795,19.601-21.341,19.601h-9.611 v14.909h-13.471v-52.975L204.871,346.387L204.871,346.387z M195.26,373.858h8.93c5.904,0,9.308-2.952,9.308-8.552 c0-5.525-3.406-8.324-9.308-8.324h-8.93V373.858z'/%3E%3Cpath style='fill:%23FFFFFF;' d='M257.928,346.387c16.649,0,28.152,10.746,28.152,26.487c0,15.666-11.655,26.488-28.683,26.488 h-22.25v-52.975H257.928z M248.619,388.615h9.611c8.249,0,14.151-6.357,14.151-15.665c0-9.384-6.205-15.817-14.757-15.817h-9.006 V388.615z'/%3E%3Cpath style='fill:%23FFFFFF;' d='M308.563,356.982v12.26h23.763v10.596h-23.763v19.525h-13.471v-52.975h39.277v10.595h-25.806 V356.982z'/%3E%3C/g%3E%3C/svg%3E%0A\");\r\n    background-repeat: no-repeat;\r\n    background-size: cover;\r\n    width:60px;\r\n    height:60px;\r\n}\r\n\r\n\r\n\r\n/**\r\n * (c) jLoading\r\n * https://github.com/paulhodel/jtools\r\n *\r\n * @author: Paul Hodel <paul.hodel@gmail.com>\r\n * @description: Page loading spin\r\n */\r\n\r\n.jloading {\r\n    position:fixed;\r\n    z-index:10001;\r\n    width:100%;\r\n    left:0;\r\n    right:0;\r\n    top:0;\r\n    bottom:0;\r\n    background-color: rgba(0,0,0,0.7);\r\n}\r\n\r\n.jloading::after {\r\n    content:'';\r\n    display:block;\r\n    margin:0 auto;\r\n    margin-top:50vh;\r\n    width:40px;\r\n    height:40px;\r\n    border-style:solid;\r\n    border-color:white;\r\n    border-top-color:transparent;\r\n    border-width:4px;\r\n    border-radius:50%;\r\n    -webkit-animation: spin .8s linear infinite;\r\n    animation: spin .8s linear infinite;\r\n}\r\n\r\n.jloading.spin {\r\n    background-color:transparent;\r\n}\r\n\r\n.jloading.spin::after {\r\n    margin:0 auto;\r\n    margin-top:80px;\r\n    border-color:#aaa;\r\n    border-top-color:transparent;\r\n}\r\n\r\n\r\n.jlogin {\r\n    width: 100%;\r\n    box-sizing: border-box;\r\n    margin:0 auto;\r\n    vertical-align:middle;\r\n    border-radius: 5px;\r\n    -webkit-border-radius: 5px;\r\n    -moz-border-radius: 5px;\r\n    font-size:1em;\r\n}\r\n\r\n.jlogin-fullscreen {\r\n    position:absolute;\r\n    top:0px;\r\n    left:0px;\r\n    width:100%;\r\n    height:100%;\r\n    background-color:#fff;\r\n    z-index:9000;\r\n}\r\n\r\n.jlogin > form {\r\n    max-width:480px;\r\n    padding:20px;\r\n    margin: 0 auto;\r\n}\r\n\r\n.jlogin > form > div {\r\n    margin-bottom:10px;\r\n}\r\n\r\n.jlogin label {\r\n    display:block;\r\n}\r\n\r\n.jlogin > form > div > input {\r\n    width:100%;\r\n    outline:none;\r\n    padding:15px;\r\n    margin:0px;\r\n}\r\n\r\n.jlogin input[type='checkbox'] {\r\n    float:left;\r\n}\r\n\r\n.jlogin input[type='button'] {\r\n    padding:10px;\r\n    background-color: var(--button-color);\r\n    border: 1px solid var(--button-color);\r\n    color: #fff;\r\n    cursor:pointer;\r\n    -webkit-appearance: none;\r\n    -moz-appearance: none;\r\n    appearance: none;\r\n}\r\n\r\n.jlogin span\r\n{\r\n    margin:0 auto;\r\n}\r\n.jlogin img\r\n{\r\n    text-align:center;\r\n    max-width:220px;\r\n}\r\n\r\n.jlogin .jlogin-logo\r\n{\r\n    text-align:center;\r\n    padding:20px;\r\n}\r\n\r\n.jlogin .captcha\r\n{\r\n    width:100%;\r\n    margin-top:4px;\r\n    margin-bottom:4px;\r\n    border:1px solid #ccc;\r\n    display:block;\r\n}\r\n\r\n.jlogin .facebookButton\r\n{\r\n    padding:10px;\r\n    background-color: var(--button-color);\r\n    border: 1px solid var(--button-color);\r\n    color: #fff;\r\n    text-align:center;\r\n    background-repeat:no-repeat;\r\n    background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24px' height='24px'%3E%3Cpath d='M19,3H5C3.895,3,3,3.895,3,5v14c0,1.105,0.895,2,2,2h7.621v-6.961h-2.343v-2.725h2.343V9.309 c0-2.324,1.421-3.591,3.495-3.591c0.699-0.002,1.397,0.034,2.092,0.105v2.43h-1.428c-1.13,0-1.35,0.534-1.35,1.322v1.735h2.7 l-0.351,2.725h-2.365V21H19c1.105,0,2-0.895,2-2V5C21,3.895,20.105,3,19,3z' fill='white'/%3E%3C/svg%3E%0A\");\r\n    background-position:10px 40%;\r\n    cursor:pointer;\r\n}\r\n\r\n.jlogin .rememberButton\r\n{\r\n    padding:10px;\r\n    display:none;\r\n}\r\n\r\n.jlogin .requestButton, .jlogin .cancelButton, .jlogin .newProfileButton\r\n{\r\n    padding:20px;\r\n    padding-bottom:0px;\r\n    text-align:center;\r\n    cursor:pointer;\r\n}\r\n\r\n.jlogin-captcha img {\r\n    min-width:280px;\r\n}\r\n\r\n.jlogin-loading:before {\r\n    content: \"\";\r\n    display: block;\r\n    position: fixed;\r\n    top: 0px;\r\n    left: 0px;\r\n    width: 200px;\r\n    height: 4px;\r\n    background-color: var(--button-color);\r\n    animation: loading 2s linear infinite;\r\n}\r\n\r\n@keyframes loading {\r\n    from { left: -200px; width: 30%; }\r\n    50% { width: 30%; }\r\n    70% { width: 70%; }\r\n    80% { left: 50%; }\r\n    95% { left: 120%; }\r\n    to { left: 100%; }\r\n}\r\n\r\n@media only screen and (max-device-width : 800px)\r\n{\r\n    .jsuites * {\r\n        -webkit-tap-highlight-color: transparent;\r\n        -webkit-touch-callout: none;\r\n    }\r\n\r\n    html.jsuites {\r\n        height:100%;\r\n    }\r\n\r\n    .jsuites body {\r\n        margin:0px;\r\n        padding:0px;\r\n        width:100%;\r\n        height:100%;\r\n        min-height:100%;\r\n        line-height:1.4;\r\n        font-family:-apple-system,SF UI Text,Helvetica Neue,Helvetica,Arial,sans-serif !important;\r\n        background-color:#fff;\r\n    }\r\n\r\n    .jsuites input, .jsuites select, .jsuites textarea, .jsuites div {\r\n        font-family:-apple-system,SF UI Text,Helvetica Neue,Helvetica,Arial,sans-serif !important;\r\n    }\r\n}\r\n\r\n.japp\r\n{\r\n    height:100%;\r\n    box-sizing: border-box;\r\n    overflow:scroll;\r\n    background-color:#fafafa;\r\n}\r\n\r\n.jwarning::before {\r\n    content:'No internet connection';\r\n    background-color:red;\r\n    color:#fff;\r\n    padding:2px;\r\n    position:fixed;\r\n    top:0px;\r\n    left:0px;\r\n    z-index:20000;\r\n    width:100%;\r\n    font-size:0.55em;\r\n    text-align:center;\r\n}\r\n\r\n/*\r\n.japp .activeHighLight\r\n{\r\n    color:var(--active-color);\r\n}\r\n\r\n.japp .active a, .japp .active span, .japp .active i\r\n{\r\n    color:var(--active-color);\r\n}\r\n\r\n.japp .no-padding\r\n{\r\n    padding:0px;\r\n}\r\n\r\n.japp .action\r\n{\r\n    color:var(--active-color);\r\n    cursor:pointer;\r\n}\r\n\r\n\r\n.japp i.action\r\n{\r\n    position:absolute;\r\n    right:0px;\r\n    margin-right:15px;\r\n    cursor:pointer;\r\n}\r\n*/\r\n\r\n.japp button {\r\n    border-radius: 4px;\r\n    width: 100%;\r\n    border: 1px solid var(--active-color);\r\n    color: var(--active-color);\r\n    background-color: transparent;\r\n    padding: 4px;\r\n    outline: none;\r\n}\r\n\r\n.japp button:active {\r\n    border: 1px solid #000;\r\n    color: #000;\r\n}\r\n\r\n.japp .red {\r\n    color: red;\r\n    border-color: 1px solid red;\r\n}\r\n\r\n.japp .link {\r\n    color: var(--active-color);\r\n    cursor: pointer;\r\n}\r\n\r\n.japp .uppercase {\r\n    text-transform: uppercase;\r\n}\r\n\r\n/** Navbar **/\r\n\r\n.japp .navbar {\r\n    position:fixed;\r\n    top: 0;\r\n    width: 100%;\r\n    z-index: 500;\r\n    margin: 0;\r\n    -webkit-backface-visibility: hidden;\r\n    -webkit-transform: translate3d(0,0,0);\r\n    -webkit-box-sizing: border-box;\r\n    backface-visibility: hidden;\r\n    box-sizing: border-box;\r\n    transform: translate3d(0,0,0);\r\n    height: 44px;\r\n    background-color: #fff;\r\n    box-shadow: 0 1px 2px rgba(0,0,0,.1);\r\n}\r\n\r\n.jwarning .navbar {\r\n    top:16px;\r\n}\r\n\r\n.japp .navbar-container {\r\n    padding: 0 8px;\r\n    position: absolute;\r\n    left: 0;\r\n    top: 0;\r\n    width: 100%;\r\n    height: 100%;\r\n    -webkit-box-sizing: border-box;\r\n    box-sizing: border-box;\r\n    vertical-align:middle;\r\n\r\n    display: flex;\r\n\r\n    -webkit-flex-flow: row wrap;\r\n    justify-content: space-between;\r\n    align-items:center;\r\n}\r\n\r\n.japp .navbar-container div {\r\n    text-align:center;\r\n}\r\n\r\n.japp .navbar-container div.title {\r\n    display: flex;\r\n}\r\n\r\n.japp .navbar-container div.title div\r\n{\r\n    line-height:24px;\r\n}\r\n\r\n.japp .navbar-container div.icon {\r\n    width:24px;\r\n}\r\n\r\n.japp .navbar-container div i {\r\n    display:block;\r\n}\r\n\r\n.japp .navbar-container div img {\r\n    height:28px;\r\n    display:block;\r\n}\r\n\r\n.japp .navbar-container div.title div img {\r\n    border-radius:12px;\r\n    width:24px;\r\n    height:24px;\r\n    margin-right:6px;\r\n}\r\n\r\n.japp .navbar .icon {\r\n    color:var(--active-color);\r\n}\r\n\r\n.japp .title {\r\n    font-size:1.2em;\r\n}\r\n\r\n.japp .block {\r\n    padding:15px;\r\n    color: #6d6d72;\r\n    font-size:0.9em;\r\n    box-sizing: border-box;\r\n}\r\n\r\n.japp .block-title {\r\n    text-transform: uppercase;\r\n    color: #6d6d72;\r\n    margin: 25px 15px 10px;\r\n    line-height: 17px;\r\n    position: relative;\r\n    overflow: hidden;\r\n    white-space: nowrap;\r\n    text-overflow: ellipsis;\r\n    line-height: 1;\r\n    font-size:0.9em;\r\n}\r\n\r\n.japp .block-strong {\r\n    background-color:#fff;\r\n    color: #000;\r\n}\r\n\r\n.japp .block-border {\r\n    border-top:1px solid #ddd;\r\n    border-bottom:1px solid #ddd; \r\n}\r\n\r\n.japp .block-footer {\r\n\ttext-transform: uppercase;\r\n    padding:15px;\r\n    color: #6d6d72;\r\n    font-size:0.7em;\r\n}\r\n\r\n.japp .block-collapse {\r\n    max-height:100px;\r\n    overflow-y:hidden;\r\n}\r\n\r\n.japp .block-instruction {\r\n    text-transform: uppercase;\r\n    padding:15px;\r\n    color: #6d6d72;\r\n    font-size:0.7em;\r\n    text-align:center;\r\n}\r\n\r\n.japp .pages {\r\n    display: flex;\r\n    flex-wrap: nowrap;\r\n    align-items: flex-start;\r\n    height: 100%;\r\n}\r\n\r\n.japp .page {\r\n    padding-top: 45px;\r\n    padding-bottom: 45px;\r\n    min-height: 100%;\r\n    min-width: 100%;\r\n    box-sizing: border-box;\r\n    overflow-y: auto;\r\n    /*will-change: scroll-position;\r\n    -webkit-overflow-scrolling: touch;\r\n    -webkit-transform: translateZ(0px);\r\n    -webkit-transform: translate3d(0,0,0);\r\n    -webkit-perspective: 1000;*/\r\n}\r\n\r\n.warning .page {\r\n    padding-top: 60px;\r\n}\r\n\r\n.japp .progressbar-container {\r\n    margin: 15px;\r\n    margin-left: 0px;\r\n    margin-right: 0px;\r\n    margin-top: 5px;\r\n}\r\n\r\n.japp .progressbar {\r\n    width:100%;\r\n    border:1px solid var(--active-color);\r\n    border-radius:1px;\r\n}\r\n\r\n.japp .progressbar-title {\r\n    margin-bottom:2px;\r\n    text-transform:uppercase;\r\n    display:flex;\r\n    justify-content:space-between;\r\n}\r\n\r\n.japp .progressbar div {\r\n    background-color:var(--active-color);\r\n    height:4px;\r\n    width:0%;\r\n}\r\n\r\n.japp .panel {\r\n    position:fixed;\r\n    top: 0;\r\n    left: 0;\r\n    margin: 0;\r\n    width:60vw;\r\n    max-width:220px;\r\n    height:100vh;\r\n    background: #fff;\r\n    z-index: 1003;\r\n    -webkit-box-sizing: border-box;\r\n    box-sizing: border-box;\r\n    box-shadow: 0 -1px 2px rgba(0,0,0,.1);\r\n    padding:4px;\r\n}\r\n\r\n.japp .panel-left {\r\n    -webkit-box-shadow: 0px 2px 15px -5px rgba(0, 0, 0, 0.7);\r\n    box-shadow: 0px 2px 15px -5px rgba(0, 0, 0, 0.7);\r\n}\r\n\r\n.japp .panel a {\r\n    color:var(--active-color);\r\n}\r\n\r\n/** Toolbar **/\r\n\r\n.japp .jtoolbar\r\n{\r\n    position:fixed;\r\n    bottom: 0;\r\n    margin: 0;\r\n    left: 0;\r\n    width: 100%;\r\n    background: #f7f7f8;\r\n    z-index: 500;\r\n    -webkit-box-sizing: border-box;\r\n    box-sizing: border-box;\r\n    box-shadow: 0 -1px 2px rgba(0,0,0,.1);\r\n    padding:4px;\r\n}\r\n\r\n.japp .jtoolbar > div\r\n{\r\n    -webkit-box-sizing: border-box;\r\n    box-sizing: border-box;\r\n    vertical-align:middle;\r\n\r\n    display: -webkit-box;\r\n    display: -moz-box;\r\n    display: -ms-flexbox;\r\n    display: -webkit-flex;\r\n    display: flex;\r\n\r\n    -webkit-flex-flow: row wrap;\r\n    justify-content: space-evenly;\r\n    align-items:center;\r\n    width: 100%;\r\n    cursor:pointer;\r\n}\r\n\r\n.japp .jtoolbar > div > div\r\n{\r\n    text-align:center;\r\n    flex:1;\r\n    margin:auto;\r\n}\r\n\r\n.japp .jtoolbar a\r\n{\r\n    text-decoration:none;\r\n    display:inline-block;\r\n}\r\n\r\n.japp .jtoolbar i\r\n{\r\n    color:#929292;\r\n}\r\n\r\n.japp .jtoolbar span\r\n{\r\n    font-size:0.7em;\r\n    display:block;\r\n    color:#929292;\r\n}\r\n\r\n.japp .jtoolbar .selected a,\r\n.japp .jtoolbar .selected i,\r\n.japp .jtoolbar .selected span\r\n{\r\n    color:var(--active-color);\r\n}\r\n\r\n\r\n/** Options **/\r\n\r\n.japp .options\r\n{\r\n    background-color:#fff;\r\n    border-top:1px solid #e6e6e8;\r\n    border-bottom:1px solid #e6e6e8;\r\n    padding-left:15px;\r\n    margin-top:10px;\r\n}\r\n\r\n.japp .options:empty\r\n{\r\n    display:none;\r\n}\r\n\r\n.japp .options .option\r\n{\r\n    padding-top:10px;\r\n    padding-bottom:10px;\r\n    text-transform:uppercase;\r\n    font-size:1em;\r\n    border-bottom:1px solid #e6e6e8;\r\n    overflow-x:hidden;\r\n\r\n    -webkit-box-sizing: border-box;\r\n    box-sizing: border-box;\r\n    vertical-align:middle;\r\n\r\n    display: -webkit-box;\r\n    display: -moz-box;\r\n    display: -ms-flexbox;\r\n    display: -webkit-flex;\r\n    display: flex;\r\n\r\n    -webkit-flex-flow: row nowrap;\r\n    justify-content: space-between;\r\n    align-items:center;\r\n    flex-wrap:nowrap;\r\n}\r\n\r\n.japp .options .option:last-child\r\n{\r\n    border-bottom:0px;\r\n}\r\n\r\n.japp .options label\r\n{\r\n    width:100%;\r\n}\r\n\r\n.japp .options input[type='text'], .japp .options textarea\r\n{\r\n    outline:none;\r\n    border:1px solid transparent;\r\n    font-size:1em;\r\n    margin:0px;\r\n    padding:0px;\r\n    width:100%;\r\n    box-sizing:border-box;\r\n}\r\n\r\n.japp .options textarea\r\n{\r\n    height:100px;\r\n}\r\n\r\n.japp .options input[type='checkbox'], .japp .options input[type='radio']\r\n{\r\n    visibility: hidden;\r\n}\r\n\r\n.japp .options input[type='checkbox'] ~ i, .japp .options input[type='radio'] ~ i\r\n{\r\n    background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTkgMTYuMTdMNC44MyAxMmwtMS40MiAxLjQxTDkgMTkgMjEgN2wtMS40MS0xLjQxeiIgZmlsbD0iIzAwN2FmZiIvPjwvc3ZnPg==');\r\n    background-repeat: no-repeat;\r\n    content:'';\r\n    visibility: hidden;\r\n    margin-right:10px;\r\n    line-height:24px;\r\n    width:24px;\r\n    height:24px;\r\n}\r\n\r\n.japp .options input[type='checkbox']:checked ~ i, .japp .options input[type='radio']:checked  ~ i\r\n{\r\n    visibility: visible;\r\n}\r\n\r\n.japp .options .jdropdown {\r\n    display: block;\r\n}\r\n\r\n.japp .options .jdropdown .jdropdown-header {\r\n    border: 0px;\r\n    border-bottom: 1px solid #e6e6e8;\r\n    padding: 15px;\r\n    text-transform: uppercase;\r\n}\r\n\r\n.japp .options .jdropdown:last-child .jdropdown-header {\r\n    border-bottom: 0px;\r\n}\r\n\r\n.japp .options .jdropdown-searchbar.jdropdown-focus .jdropdown-header {\r\n    border:0px;\r\n    padding: 5px;\r\n} \r\n\r\n.japp .options .icon {\r\n    float:left;\r\n    margin-right:10px;\r\n    max-width:40px;\r\n    max-height:40px;\r\n    border-radius:20px;\r\n    color:#929292;\r\n}\r\n\r\n.japp .options .option .option-actions {\r\n    display:flex;\r\n    transform: translateX(100%);\r\n    width:0px;\r\n}\r\n\r\n.japp .options .option .option-actions > div {\r\n    padding-right:5px;\r\n}\r\n\r\n.japp .options .option .option-actions > div > i {\r\n    width:40px;\r\n    height:40px;\r\n    color:#fff;\r\n    font-size:24px;\r\n    line-height:40px;\r\n    text-align:center;\r\n    border-radius:30px;\r\n    background-color:red;\r\n}\r\n\r\n.japp .options .option .option-actions.small > div > i\r\n{\r\n    color:#000;\r\n    width:24px;\r\n    height:24px;\r\n    font-size:24px;\r\n    line-height:24px;\r\n    border-radius:24px;\r\n    background-color:transparent;\r\n}\r\n\r\n.japp .options .option-title {\r\n    display:block;\r\n    padding-top:10px;\r\n    padding-bottom:10px;\r\n    text-transform:uppercase;\r\n    font-size:1em;\r\n    vertical-align:center;\r\n}\r\n\r\n.japp .options .option-title::after {\r\n    content:'\\e313';\r\n    font-family: 'material icons';\r\n    font-size:24px;\r\n    margin-right:10px;\r\n    float:right;\r\n    width:24px;\r\n    height:24px;\r\n    line-height:24px;\r\n}\r\n\r\n.japp .options .option-title.selected::after\r\n{\r\n    content:'\\e316';\r\n}\r\n\r\n.japp .options .option-title.selected ~ div {\r\n    display:block;\r\n}\r\n\r\n.japp .options .option-group {\r\n    border-top:1px solid #e6e6e8;\r\n    background-color:#fff;\r\n    display:none;\r\n}\r\n\r\n.japp .options .option-link:after {\r\n    content:'\\e315';\r\n    font-family: 'material icons';\r\n    color:var(--active-color);\r\n    font-size:24px;\r\n    margin-right:10px;\r\n    float:right;\r\n    width:24px;\r\n    height:24px;\r\n    line-height:24px;\r\n    display:block;\r\n}\r\n\r\n.japp .options .option-header {\r\n    padding-left:10px;\r\n    padding-right:10px;\r\n    flex-grow:10;\r\n    white-space: nowrap;\r\n    overflow: hidden;\r\n    text-overflow: ellipsis;\r\n}\r\n\r\n.japp .options .option .option-name {\r\n    font-size:1em;\r\n    text-overflow: ellipsis;\r\n    width: 100%;\r\n    overflow: hidden;\r\n}\r\n\r\n.japp .options .option .option-small {\r\n    font-size:0.7em;\r\n    color: #6d6d72;\r\n    text-overflow: ellipsis;\r\n    overflow: hidden;\r\n    white-space: nowrap;\r\n    display:block;\r\n}\r\n\r\n.japp .options .option .option-image img {\r\n    width:40px;\r\n    height:40px;\r\n    border-radius:20px;\r\n    display:block;\r\n}\r\n\r\n.japp .options .option .option-image-small img {\r\n    width:24px;\r\n    height:auto;\r\n    border-radius:50px;\r\n    display:block;\r\n}\r\n\r\n.japp .options .option .option-image .option-badge {\r\n    position: absolute;\r\n    background-color: red;\r\n    color: #fff;\r\n    width: 16px;\r\n    height: 16px;\r\n    line-height: 16px;\r\n    border-radius: 16px;\r\n    text-align: center;\r\n    font-size: 7px;\r\n    margin: 28px;\r\n}\r\n\r\n.japp .options .option .option-image-small .option-badge {\r\n    position: absolute;\r\n    background-color: red;\r\n    color: #fff;\r\n    width: 16px;\r\n    height: 16px;\r\n    line-height: 16px;\r\n    border-radius: 16px;\r\n    text-align: center;\r\n    font-size: 7px;\r\n    margin: 12px;\r\n}\r\n\r\n.japp .options .option .option-badge.solid {\r\n    background-color: red;\r\n    color: red;\r\n}\r\n\r\n.japp .options .option .option-badge.solid-green {\r\n    background-color: green;\r\n    color: green;\r\n}\r\n\r\n.japp .options .option .option-badge:empty {\r\n    display:none;\r\n}\r\n\r\n.japp .options .option .option-date {\r\n    float:right;\r\n    font-size:0.6em;\r\n    color:#888;\r\n    margin-right:15px;\r\n    white-space: nowrap;\r\n}\r\n\r\n.japp .options .option .option-right {\r\n    margin-right:15px;\r\n}\r\n\r\n.japp .options .option .option-badget {\r\n    background-color:red;\r\n    border-radius:12px;\r\n    width:24px;\r\n    height:24px;\r\n    line-height:24px;\r\n    font-size:0.7em;\r\n    color:#fff;\r\n    text-align:center;\r\n}\r\n\r\n.japp .options .option .option-badget:empty {\r\n    display:none;\r\n}\r\n\r\n/** Badge **/\r\n\r\n.japp .jbadge\r\n{\r\n    position:relative;\r\n    display:inline-block;\r\n    top:-12px;\r\n    left:-12px;\r\n}\r\n\r\n.japp .jbadge > div\r\n{\r\n    background-color:red;\r\n    color:#fff;\r\n    width:16px;\r\n    height:16px;\r\n    line-height:16px;\r\n    border-radius:16px;\r\n    text-align:center;\r\n    font-size:8px;\r\n    position:absolute;\r\n}\r\n\r\n.japp .jbadge > div:empty\r\n{\r\n    display:none;\r\n}\r\n\r\n/** Picker **/\r\n\r\n.japp .picker \r\n{\r\n    background-color:#fff;\r\n    border-top:1px solid #e6e6e8;\r\n    border-bottom:1px solid #e6e6e8;\r\n    padding-left:15px;\r\n    margin-top:10px;\r\n    position:absolute;\r\n    bottom:0px;\r\n    width:100%;\r\n    z-index:700;\r\n}\r\n\r\n.japp .picker .picker-options\r\n{\r\n    -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,1), rgba(0,0,0,.2)), linear-gradient(to top, rgba(0,0,0,1), rgba(0,0,0,.2));\r\n    -webkit-mask-size: 100% 50%;\r\n    -webkit-mask-repeat: no-repeat;\r\n    -webkit-mask-position: center bottom, top center;\r\n    overflow-y:scroll;\r\n    padding-top:100px;\r\n    padding-bottom:100px;\r\n}\r\n\r\n.japp .picker label\r\n{\r\n    display: block;\r\n    padding-top: 10px;\r\n    padding-bottom: 10px;\r\n}\r\n\r\n.japp .picker .picker-selected\r\n{\r\n    border-top:1px solid #e6e6e8;\r\n    border-bottom:1px solid #e6e6e8;\r\n}\r\n\r\n.japp .picker input\r\n{\r\n    display:none;\r\n}\r\n\r\n/** Topsearch **/\r\n\r\n.japp .top-search {\r\n    margin:0px;\r\n    padding:10px;\r\n    border-bottom:1px solid #ddd; \r\n}\r\n\r\n.japp .top-search input {\r\n    background-color: #e6e6e6;\r\n    border: 0px;\r\n    border-radius: 4px;\r\n    outline: none;\r\n    background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='gray'%3E%3Cpath d='M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E\");\r\n    background-repeat: no-repeat;\r\n    background-position: 5px center;\r\n    padding-left: 32px;\r\n    width: 100%;\r\n}\r\n\r\n.japp .searchbar-container i {\r\n    position:absolute;\r\n    margin-top:5px;\r\n    margin-left:8px;\r\n    color:#bbb;\r\n}\r\n\r\n/** Range **/\r\n\r\n.japp .range\r\n{\r\n  -webkit-appearance: none;\r\n  margin: 18px 0;\r\n  width: 100%;\r\n}\r\n\r\n.japp .range:focus\r\n{\r\n  outline: none;\r\n}\r\n\r\n.japp .range::-webkit-slider-runnable-track\r\n{\r\n    width: 100%;\r\n    height: 4px;\r\n    cursor: pointer;\r\n    animation: 0.2s;\r\n    background: #ccc;\r\n    border-radius: 1.3px;\r\n}\r\n\r\n.japp .range::-webkit-slider-thumb\r\n{\r\n    -webkit-appearance:none;\r\n    appearance:none;\r\n    width:24px;\r\n    height:24px;\r\n    background:#fff;\r\n    cursor:pointer;\r\n    border-radius:24px;\r\n    margin-top:-10px;\r\n    border:0px;\r\n    box-shadow:0px 1px 3px 1px #bbb;\r\n}\r\n\r\n.japp .range::-moz-range-track\r\n{\r\n    width:100%;\r\n    cursor:pointer;\r\n    animation:0.2s;\r\n    background:#ccc;\r\n    border-radius:1.3px;\r\n}\r\n\r\n.japp .range::-moz-range-thumb\r\n{\r\n    width:24px;\r\n    height:24px;\r\n    background:#fff;\r\n    cursor:pointer;\r\n    border-radius:24px;\r\n    border:0px;\r\n    box-shadow:0px 1px 3px 1px #bbb;\r\n}\r\n\r\n.japp .range::-ms-track\r\n{\r\n    width: 100%;\r\n    cursor: pointer;\r\n    animation: 0.2s;\r\n    background: transparent;\r\n    border-color: transparent;\r\n    border-width: 16px 0;\r\n    color: transparent;\r\n    border-radius:1.3px;\r\n}\r\n\r\n.japp .range::-ms-fill-lower\r\n{\r\n    background:#ccc;\r\n    height:5px;\r\n}\r\n\r\n.japp .range::-ms-fill-upper\r\n{\r\n    background:#ccc;\r\n    height:5px;\r\n}\r\n\r\n.japp .range::-ms-thumb {\r\n    width:24px;\r\n    height:24px;\r\n    background:#fff;\r\n    cursor:pointer;\r\n    border-radius:24px;\r\n    border:0px;\r\n    box-shadow:0px 1px 3px 1px #bbb;\r\n}\r\n\r\n.japp .range:focus::-ms-fill-lower\r\n{\r\n  background: #ccc;\r\n}\r\n\r\n.japp .range:focus::-ms-fill-upper\r\n{\r\n  background: #ccc;\r\n}\r\n\r\n\r\n/** Actionsheet **/\r\n\r\n.jactionsheet\r\n{\r\n    position:fixed;\r\n    left: 0;\r\n    bottom: 0;\r\n    width: 100%;\r\n    min-height:100%;\r\n    z-index:10000;\r\n\r\n    background-color:rgba(0,0,0,0.5);\r\n    -webkit-transition-duration: .2s;\r\n    transition-duration: .2s;\r\n    display: flex;\r\n    -ms-flex-align: baseline;\r\n    -webkit-align-items: baseline;\r\n    -webkit-box-align: baseline;\r\n\r\n    align-items: baseline;\r\n}\r\n\r\n.jactionsheet .jactionsheet-content\r\n{\r\n    width:100%;\r\n    align-self: flex-end;\r\n}\r\n\r\n.jactionsheet .jactionsheet-title\r\n{\r\n    font-size:1em;\r\n    font-weight:bold;\r\n    padding:8px;\r\n}\r\n\r\n.jactionsheet .jactionsheet-message\r\n{\r\n    padding:4px;\r\n}\r\n\r\n.jactionsheet .jactionsheet-group\r\n{\r\n    box-sizing: border-box;\r\n    margin:10px;\r\n    background-color:#fff;\r\n    border-radius:5px;\r\n}\r\n\r\n.jactionsheet .jactionsheet-group > div\r\n{\r\n    font-size:1.4em;\r\n    font-weight:bold;\r\n    padding:4px;\r\n    border-top:1px solid #ccc;\r\n    text-align:center;\r\n    width:100%;\r\n}\r\n.jactionsheet .jactionsheet-group > div:first-child\r\n{\r\n    border-top:0px;\r\n}\r\n\r\n.jactionsheet .jactionsheet-group input\r\n{\r\n    border:0px;\r\n    margin:0px;\r\n    background-color:transparent;\r\n    color:var(--active-color);\r\n    width:100%;\r\n    outline:none;\r\n    font-size:1em;\r\n}\r\n\r\n.jactionsheet .jactionsheet-group input.jactionsheet-cancel\r\n{\r\n    color:red;\r\n}\r\n\r\n/** Toggle **/\r\n\r\n.jtoggle {\r\n    display: inline-block;\r\n    cursor: pointer;\r\n    -webkit-tap-highlight-color: transparent;\r\n}\r\n\r\n.jtoggle i {\r\n    position: relative;\r\n    display: inline-block;\r\n    margin-right: .5rem;\r\n    border-radius: 23px;\r\n    vertical-align: text-bottom;\r\n    transition: all 0.3s linear;\r\n    width: 46px !important;\r\n    height: 27px !important;\r\n    background: #e6e6e6 !important;\r\n    visibility: visible !important;\r\n}\r\n\r\n.jtoggle i::before {\r\n  content: \"\";\r\n  position: absolute;\r\n  left: 0;\r\n  width: 42px;\r\n  height: 22px;\r\n  background-color: #fff;\r\n  border-radius: 11px;\r\n  transform: translate3d(2px, 2px, 0) scale3d(1, 1, 1);\r\n  transition: all 0.25s linear;\r\n}\r\n\r\n.jtoggle i::after {\r\n  content: \"\";\r\n  position: absolute;\r\n  left: 0;\r\n  width: 22px;\r\n  height: 22px;\r\n  background-color: #fff;\r\n  border-radius: 11px;\r\n  box-shadow: 0 2px 2px rgba(0, 0, 0, 0.24);\r\n  transform: translate3d(2px, 2px, 0);\r\n  transition: all 0.2s ease-in-out;\r\n}\r\n\r\n.jtoggle:active i::after {\r\n  width: 28px;\r\n  transform: translate3d(2px, 2px, 0);\r\n}\r\n\r\n.jtoggle:active input:checked + i::after {\r\n    transform: translate3d(16px, 2px, 0);\r\n}\r\n\r\n.jtoggle input {\r\n    display: none;\r\n}\r\n\r\n.jtoggle input:checked + i {\r\n    background-color: #4BD763 !important;\r\n}\r\n\r\n.jtoggle input:checked + i::before {\r\n    transform: translate3d(18px, 2px, 0) scale3d(0, 0, 0);\r\n}\r\n\r\n.jtoggle input:checked + i::after {\r\n    transform: translate3d(22px, 2px, 0);\r\n}\r\n\r\n/** Placeholders **/\r\n\r\n.japp .options input::-webkit-input-placeholder\r\n{\r\n    text-transform:uppercase;\r\n}\r\n\r\n.japp .options input::-moz-placeholder\r\n{\r\n    text-transform:uppercase;\r\n}\r\n\r\n.japp .options input:-ms-input-placeholder\r\n{\r\n    text-transform:uppercase;\r\n}\r\n\r\n.japp .options input:-moz-placeholder\r\n{\r\n    text-transform:uppercase;\r\n}\r\n\r\n::placeholder\r\n{\r\n    color: #cccccc;\r\n    opacity: 1;\r\n}\r\n\r\n:-ms-input-placeholder\r\n{\r\n    color: #cccccc;\r\n}\r\n\r\n::-ms-input-placeholder\r\n{\r\n    color: #cccccc;\r\n}\r\n\r\n::-webkit-input-placeholder\r\n{\r\n    color: #cccccc;\r\n}\r\n\r\n\r\n.jmodal {\r\n    position:fixed;\r\n    top:50%;\r\n    left:50%;\r\n    width:60%;\r\n    height:60%;\r\n    -webkit-box-shadow: 0 2px 10px rgba(0,0,0,.2);\r\n    -moz-box-shadow: 0 2px 10px rgba(0,0,0,.2);\r\n    border:1px solid #ccc;\r\n    background-color:#fff;\r\n    transform: translate(-50%, -50%);\r\n    box-sizing: border-box;\r\n    padding-top:50px;\r\n    z-index:9002;\r\n    border-radius:5px;\r\n}\r\n\r\n.jmodal:before {\r\n    position:absolute;\r\n    top:0;\r\n    left:0;\r\n    width:100%;\r\n    content:attr(title);\r\n    padding:15px;\r\n    box-sizing: border-box;\r\n    background: #e3e3e3;\r\n    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ededed', endColorstr='#e3e3e3');\r\n    background: -webkit-gradient(linear, left top, left bottom, from(#ededed), to(#e3e3e3));\r\n    background: -moz-linear-gradient(top,  #ededed,  #e3e3e3);\r\n    font-size:1.2em;\r\n}\r\n\r\n.jmodal_content {\r\n    padding:20px;\r\n    overflow-y:auto;\r\n    max-height:100%;\r\n}\r\n.jmodal.no-title {\r\n    padding-top:0px;\r\n}\r\n\r\n.jmodal.no-title:before {\r\n    display:none;\r\n}\r\n\r\n.jmodal:after {\r\n    content:'';\r\n    background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E\");\r\n    position:absolute;\r\n    top:0;\r\n    right:0;\r\n    margin:14px;\r\n    font-size:24px;\r\n    width:24px;\r\n    height:24px;\r\n    cursor:pointer;\r\n    text-shadow: 0px 0px 5px #fff;\r\n}\r\n\r\n.jmodal_fullscreen {\r\n    width: 100% !important;\r\n    height: 100% !important;\r\n    top: 0px;\r\n    left: 0px;\r\n    transform: none;\r\n    border-radius: 0px;\r\n}\r\n\r\n\r\n.jmodal_backdrop {\r\n    position: fixed;\r\n    top: 0px;\r\n    left: 0px;\r\n    min-width: 100%;\r\n    min-height: 100%;\r\n    background-color: rgba(0,0,0,0.5);\r\n    border: 0px;\r\n    padding: 0px;\r\n    z-index: 8000;\r\n    display: none;\r\n\r\n  -webkit-touch-callout: none; /* iOS Safari */\r\n    -webkit-user-select: none; /* Safari */\r\n     -khtml-user-select: none; /* Konqueror HTML */\r\n       -moz-user-select: none; /* Firefox */\r\n        -ms-user-select: none; /* Internet Explorer/Edge */\r\n            user-select: none; /* Non-prefixed version, currently\r\n                                  supported by Chrome and Opera */\r\n}\r\n\r\n\r\n.jnotification {\r\n    position: fixed;\r\n    z-index: 10000;\r\n    -webkit-box-sizing: border-box;\r\n    box-sizing: border-box;\r\n    padding: 10px;\r\n}\r\n\r\n.jnotification-container {\r\n    -webkit-box-shadow: 0px 2px 15px -5px rgba(0, 0, 0, 0.7);\r\n    box-shadow: 0px 2px 15px -5px rgba(0, 0, 0, 0.7);\r\n    padding: 12px;\r\n    border-radius: 8px;\r\n}\r\n\r\n.jnotification-close {\r\n    content: '';\r\n    background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='gray'%3E%3Cpath d='M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E\");\r\n    font-size: 20px;\r\n    width: 20px;\r\n    height: 20px;\r\n    cursor: pointer;\r\n}\r\n\r\n.jnotification-title {\r\n    font-weight: bold;\r\n}\r\n\r\n.jnotification-header {\r\n    display: flex;\r\n    padding-bottom: 5px;\r\n}\r\n\r\n.jnotification-header:empty {\r\n    display: none;\r\n}\r\n\r\n.jnotification-image {\r\n    margin-right: 5px;\r\n}\r\n\r\n.jnotification-image:empty {\r\n    display: none;\r\n}\r\n\r\n.jnotification-image img {\r\n    width: 24px;\r\n}\r\n\r\n.jnotification-name {\r\n    text-transform: uppercase;\r\n    font-size: 0.9em;\r\n    flex: 1;\r\n    letter-spacing: 0.1em;\r\n}\r\n\r\n@media (min-width: 320px) and (max-width: 800px) {\r\n    .jnotification {\r\n        top: 0px;\r\n        width: 100%;\r\n    }\r\n    .jnotification-container {\r\n        background: rgba(255,255,255,0.95);\r\n        border: 1px solid #eee;\r\n    }\r\n}\r\n\r\n@media (min-width: 801px) {\r\n    .jnotification {\r\n        bottom: 0px;\r\n    }\r\n    .jnotification-container {\r\n        background-color: #000;\r\n        background: rgba(92,92,92,1);\r\n        background: linear-gradient(0deg, rgba(92,92,92,1) 0%, rgba(77,77,77,1) 100%);\r\n        color: #fff;\r\n        width: 320px;\r\n        margin: 30px;\r\n        padding: 20px;\r\n    }\r\n}\r\n\r\n.jnotification-header {\r\n    display: -webkit-box;\r\n    display: -webkit-flex;\r\n    display: -ms-flexbox;\r\n    display: flex;\r\n    -webkit-box-pack: start;\r\n    -webkit-justify-content: flex-start;\r\n    -ms-flex-pack: start;\r\n    justify-content: flex-start;\r\n    -webkit-box-align: center;\r\n    -webkit-align-items: center;\r\n    -ms-flex-align: center;\r\n    align-items: center;\r\n}\r\n\r\n.jrating {\n    display:flex;\n}\n.jrating > div {\n    width:24px;\n    height:24px;\n    line-height:24px;\n    background-image: url(\"data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4l-3.76 2.27 1-4.28-3.32-2.88 4.38-.38L12 6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z' fill='gray'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E\");\n}\n\n.jrating .jrating-over {\n    background-image: url(\"data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='black'%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3Cpath d='M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E\");\n    opacity: 0.7;\n}\n\n.jrating .jrating-selected {\n    background-image: url(\"data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='red'%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3Cpath d='M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E\");\n}\n\r\n\r\n/**\r\n * (c) Image slider\r\n * https://github.com/paulhodel/jtools\r\n *\r\n * @author: Paul Hodel <paul.hodel@gmail.com>\r\n * @description: Image Slider\r\n */\r\n\r\n.jslider {\r\n    margin-top:10px;\r\n    margin-bottom:10px;\r\n}\r\n\r\n.jslider-container img {\r\n    width:60px;\r\n    margin-right:5px;\r\n    margin-bottom:5px;\r\n}\r\n\r\n.jslider-attach {\r\n    cursor:pointer;\r\n}\r\n\r\n.jslider-left::before {\r\n    position: fixed;\r\n    left: 10px;\r\n    content:'arrow_back_ios';\r\n    color: #fff;\r\n    width: 30px;\r\n    height: 30px;\r\n    font-family: 'Material Icons';\r\n    font-size: 30px;\r\n    text-shadow: 0px 0px 0px #000;\r\n    text-align: center;\r\n}\r\n\r\n.jslider-right::after {\r\n    position: fixed;\r\n    right: 5px;\r\n    content: 'arrow_forward_ios';\r\n    color: #fff;\r\n    width: 30px;\r\n    height: 30px;\r\n    font-family: 'Material Icons';\r\n    font-size: 30px;\r\n    text-shadow: 0px 0px 0px #000;\r\n    text-align: center;\r\n}\r\n\r\n.jslider-close {\r\n    width:24px;\r\n    height:24px;\r\n    background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='white'%3E%3Cpath d='M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E\");\r\n    position:fixed;\r\n    top:15px;\r\n    right:15px;\r\n    display:none;\r\n    cursor:pointer;\r\n    z-index:3000;\r\n}\r\n\r\n.jslider-preview {\r\n    position:fixed;\r\n    left:0;\r\n    top:0;\r\n    width: 100%;\r\n    min-height:100%;\r\n    z-index:2000;\r\n    margin:0px;\r\n    box-sizing:border-box;\r\n\r\n    background-color:rgba(0,0,0,0.9);\r\n    -webkit-transition-duration: .4s;\r\n    transition-duration: .4s;\r\n    display: flex;\r\n    -ms-flex-align: center;\r\n    -webkit-align-items: center;\r\n    -webkit-box-align: center;\r\n\r\n    align-items: center;\r\n}\r\n\r\n.jslider-preview img {\r\n    min-width:70%;\r\n    max-width:100%;\r\n    height:auto;\r\n    box-sizing: border-box;\r\n    margin:0 auto;\r\n    vertical-align:middle;\r\n    display:none;\r\n}\r\n\r\n\r\n.jtabs {\r\n    max-width: calc(100vw - 24px);\r\n    position: relative;\r\n}\r\n.jtabs > .jtabs-headers {\r\n    display: flex;\r\n    overflow-x: auto;\r\n}\r\n\r\n.jtabs > .jtabs-headers > div {\r\n    padding: 6px;\r\n    padding-left: 20px;\r\n    padding-right: 20px;\r\n    margin-left: 1px;\r\n    margin-right: 1px;\r\n    margin-bottom: 1px;\r\n    background-color: #f1f1f1;\r\n    cursor: pointer;\r\n}\r\n\r\n.jtabs > .jtabs-headers > div.jtabs-selected {\r\n    background-color: #e8e8e8;\r\n    color: #000;\r\n}\r\n\r\n.jtabs > .jtabs-content > div {\r\n    display: none;\r\n}\r\n\r\n.jtabs > .jtabs-content > div.jtabs-selected {\r\n    display: block;\r\n}\r\n\r\n.jtabs > .jtabs-border {\r\n    position: absolute;\r\n    height: 2px;\r\n    background-color: #888;\r\n    transform-origin: left;\r\n    transition: all .2s cubic-bezier(0.4,0,0.2,1);\r\n    transition-property: color,left,transform;\r\n}\r\n\r\n.jtabs .jtabs-add {\r\n    background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24' viewBox='0 0 24 24' width='24'%3E%3Cpath d='M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-2 10h-4v4h-2v-4H7v-2h4V7h2v4h4v2z' fill='%23bbbbbb'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E\");\r\n    cursor: pointer;\r\n    background-position: center;\r\n    background-repeat: no-repeat;\r\n    width: 24px;\r\n    height: 24px;\r\n    line-height: 24px;\r\n    margin: 3px;\r\n    margin-left: 10px;\r\n}\r\n\r\n.jtags {\r\n    display: flex;\r\n    flex-wrap: wrap;\r\n    -ms-flex-direction: row;\r\n    -webkit-flex-direction: row;\r\n    flex-direction: row;\r\n    -ms-flex-pack: flex-start;\r\n    -webkit-justify-content: space-between;\r\n    justify-content: flex-start;\r\n    padding: 2px;\r\n    border: 1px solid #ccc;\r\n} \r\n\r\n.jtags > div {\r\n    padding: 3px;\r\n    padding-left: 10px;\r\n    padding-right: 22px;\r\n    position: relative;\r\n    border-radius: 1px;\r\n    margin: 2px;\r\n    display: block;\r\n    outline: none;\r\n}\r\n\r\n.jtags > div::after {\r\n    content: 'x';\r\n    position: absolute;\r\n    top: 4px;\r\n    right: 4px;\r\n    width: 12px;\r\n    height: 12px;\r\n    cursor: pointer;\r\n    font-size: 11px;\r\n    display: none;\r\n}\r\n\r\n.jtags_label {\r\n    background-color: #eeeeee !important;\r\n}\r\n\r\n.jtags_label::after {\r\n    display: inline-block !important;\r\n}\r\n\r\n.jtags_error::after {\r\n    color: #fff  !important;\r\n}\r\n\r\n.jtags_error {\r\n    background-color: #d93025 !important;\r\n    color: #fff;\r\n}\r\n\r\n\r\n.jtags_search {\r\n    position: absolute;\r\n    display: none;\r\n    box-shadow: 0 1px 2px 0 rgba(60,64,67,0.302), 0 2px 6px 2px rgba(60,64,67,0.149);\r\n    border: none;\r\n    -webkit-border-radius: 4px;\r\n    border-radius: 4px;\r\n    width: 280px;\r\n    padding: 8px 0;\r\n\r\n    -webkit-box-shadow: 0 2px 4px rgba(0,0,0,0.2);\r\n    box-shadow: 0 2px 4px rgba(0,0,0,0.2);\r\n    -webkit-transition: opacity .218s;\r\n    transition: opacity .218s;\r\n    background: #fff;\r\n    border: 1px solid rgba(0,0,0,.2);\r\n    cursor: pointer;\r\n    margin: 0;\r\n    min-width: 300px;\r\n    outline: none;\r\n    width: auto;\r\n    user-select: none;\r\n}\r\n\r\n.jtags_search > div {\r\n    color: #333;\r\n    cursor: pointer;\r\n    display: -webkit-box;\r\n    display: -webkit-flex;\r\n    display: flex;\r\n    padding: 5px 10px;\r\n    user-select: none;\r\n    -webkit-align-items: center;\r\n    align-items: center;\r\n}\r\n\r\n.jtags_search > div:hover {\r\n    background-color: #e8eaed;\r\n}\r\n\r\n.jtags_search > div > img {\r\n    width: 32px;\r\n    height: 32px;\r\n    user-select: none;\r\n    border-radius: 16px;\r\n    margin-right: 2px;\r\n}\r\n\r\n.jtags_search > div > div {\r\n    overflow: hidden;\r\n    text-overflow: ellipsis;\r\n    margin-left: 2px;\r\n    max-width: 300px;\r\n    white-space: nowrap;\r\n    user-select: none;\r\n}\r\n\r\n.jtags_search .selected {\r\n    background-color: #e8eaed;\r\n}\r\n\r\n\r\n", ""]);
// Exports
module.exports = exports;


/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./runestone/activecode/css/activecode.css":
/*!***************************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./runestone/activecode/css/activecode.css ***!
  \***************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Imports
var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
exports = ___CSS_LOADER_API_IMPORT___(false);
// Module
exports.push([module.i, ".modal-profile {\n    display: none;\n    min-height: 300px;\n    overflow: hidden;\n    width: 700px;\n    padding: 25px;\n    border: 1px solid #fff;\n    box-shadow: 0px 2px 7px #292929;\n    -moz-box-shadow: 0px 2px 7px #292929;\n    -webkit-box-shadow: 0px 2px 7px #292929;\n    border-radius: 10px;\n    -moz-border-radius: 10px;\n    -webkit-border-radius: 10px;\n    background: #f2f2f2;\n    z-index: 50;\n}\n\n.modal-lightsout {\n    display: none;\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: 100%;\n    z-index: 25;\n    background: #000;\n}\n\n.modal-close-profile {\n    display: none;\n    position: absolute;\n    height: 43px;\n    width: 43px;\n    /*background-image: url('close.png'); */\n    top: 1px;\n    right: 0.5px;\n}\n\n.ac_actions {\n    text-align: center;\n}\n\n.ac_sep {\n    background: #000;\n    display: inline-block;\n    margin: -15px 10px;\n    width: 1px;\n    height: 35px;\n    padding: 0;\n    border: 0;\n}\n\n.ac_section {\n    position: relative;\n    margin-right: auto;\n    margin-left: auto;\n    max-width: 800px;\n    clear: both;\n}\n\n.ac_section > *:not(.ac_section) {\n    max-width: 500pt;\n    margin-left: auto;\n    margin-right: auto;\n    position: relative;\n}\n\n.ac_section .clearfix {\n    position: initial;\n}\n\n.ac_output {\n    display: none;\n    max-width: 450px;\n    background-color: inherit;\n}\n.ac_output pre {\n    background-color: lightgray;\n}\n\n.ac_caption {\n    text-align: center;\n    font-weight: bold;\n}\n\n.ac_caption_text {\n    font-weight: normal;\n}\n\n.ac_question {\n    background-color: rgb(224, 236, 217);\n    padding-left: 10px;\n    padding-top: 10px;\n    margin: 5px;\n}\n\n.active_out {\n    background-color: #dcdcdc;\n    border-radius: 6px;\n    min-width: 20em;\n    max-height: 300px;\n    overflow: auto;\n}\n\n.visible-ac-canvas {\n    border: 2px solid black;\n}\n\n.ac_section > .col-md-12 {\n    max-width: 100% !important;\n}\n\n.full_width ol {\n    max-width: 100% !important;\n}\n\n.ac-disabled {\n    pointer-events: none;\n}\n\n.ac-feedback {\n    border: 1px solid black;\n    padding: 3px;\n}\n\n.ac_sql_result {\n    background-color: lightgrey;\n    padding: 10px;\n    border-radius: 6px;\n    margin-bottom: 10px;\n}\n\n.ac_sql_result_success {\n    background-color: transparent;\n    color: green;\n    border: 0px;\n    padding: 0px;\n    margin-top: 10px;\n    margin-bottom: 10px;\n    min-height: 0px !important;\n}\n\n.ac_sql_result_failure {\n    background-color: transparent;\n    color: red;\n    border: 0px;\n    padding: 0px;\n    margin-top: 10px;\n    margin-bottom: 10px;\n    min-height: 0px !important;\n}\n", ""]);
// Exports
module.exports = exports;


/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./runestone/clickableArea/css/clickable.css":
/*!*****************************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./runestone/clickableArea/css/clickable.css ***!
  \*****************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Imports
var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
exports = ___CSS_LOADER_API_IMPORT___(false);
// Module
exports.push([module.i, ".clickable {\n    cursor: pointer;\n}\n\n.clickable-clicked {\n    box-shadow: 0px 0px 2px 2px rgba(0,100,0,0.75);\n}\n\nspan.clickable-clicked {\n    background-color: yellow;\n    box-shadow: initial;\n}\n\n.clickable-clicked td {\n    background-color: #dad6c1;\n    box-shadow: initial;\n}\n\ntable {\n    border-collapse: collapse;\n}\n\nth.clickable, td.clickable {\n    border: 1px solid black;\n}\n\ntd.clickable-clicked{\n    background-color: #dad6c1;\n    box-shadow: initial;\n}\n\ntd img {\n    margin: 5px;\n}\n\n.clickable-clicked th {\n    background-color: #dad6c1;\n    box-shadow: initial;\n}\n\nth.clickable-clicked{\n    background-color: #dad6c1;\n    box-shadow: initial;\n}\n\n.clickable-incorrect {\n    border: 1px solid red !important;\n    background-color: #f2dede !important;\n}\n\n.clickable-incorrect th {\n    border: 1px solid red !important;\n    background-color: #f2dede !important;\n}\n\n.clickable-incorrect td {\n    border: 1px solid red !important;\n    background-color: #f2dede !important;\n}\n\nimg.clickable-incorrect {\n    box-shadow: 0px 0px 2px 2px rgba(100,0,0,0.75);\n}\n", ""]);
// Exports
module.exports = exports;


/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./runestone/codelens/css/pytutor.css":
/*!**********************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./runestone/codelens/css/pytutor.css ***!
  \**********************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Imports
var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
exports = ___CSS_LOADER_API_IMPORT___(false);
// Module
exports.push([module.i, "/*\n\nOnline Python Tutor\nhttps://github.com/pgbovine/OnlinePythonTutor/\n\nCopyright (C) 2010-2012 Philip J. Guo (philip@pgbovine.net)\n\nPermission is hereby granted, free of charge, to any person obtaining a\ncopy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be included\nin all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\nOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n*/\n\n/* Most recent color scheme redesign on 2012-08-19 */\n\n/* To prevent CSS namespace clashes, prefix all rules with:\n     div.ExecutionVisualizer\n*/\n\n\n/* reset some styles to nullify effects of existing stylesheets\n   e.g., http://meyerweb.com/eric/tools/css/reset/\n*/\ndiv.ExecutionVisualizer {\n  /* none for now */\n}\n\ndiv.ExecutionVisualizer table.visualizer {\n  font-family: verdana, arial, helvetica, sans-serif;\n  font-size: 10pt;\n  margin-bottom: 10px;\n  width: 100%;\n}\n\ntable.visualizer tr {\n    text-align: center;\n}\n\ndiv.ExecutionVisualizer table.visualizer td.vizLayoutTd {\n  vertical-align: top;\n  display: inline-block;\n  margin: 0 20px;\n}\n\n#vizLayoutTdThird {\n    text-align: center;\n}\n\ndiv.ExecutionVisualizer td#stack_td,\ndiv.ExecutionVisualizer td#heap_td {\n  vertical-align:top;\n  font-size: 10pt; /* don't make fonts in the heap so big! */\n}\n\ndiv.ExecutionVisualizer #dataViz {\n  margin-left: 25px;\n}\n\ndiv.ExecutionVisualizer div#codeDisplayDiv {\n  /* set this as default unless user specifies a custom size */\n  width: 550px;\n}\n\ndiv.ExecutionVisualizer div#pyCodeOutputDiv {\n  /*max-width: 550px;*/\n  max-height: 450px;\n  /*max-height: 620px;*/\n  overflow: auto;\n  /*margin-bottom: 4px;*/\n\n  margin-left: auto;\n  margin-right: auto;\n}\n\ndiv.ExecutionVisualizer table#pyCodeOutput {\n  font-family: Andale mono, monospace;\n  font-size:12pt;\n  line-height:1.1em;\n\n  border-collapse: separate; /* some crazy CSS voodoo that needs to be\n                                there so that SVG arrows to the left\n                                of the code line up vertically in Chrome */\n  border-spacing: 0px;\n  border-top: 1px solid #bbb;\n  padding-top: 3px;\n  border-bottom: 1px solid #bbb;\n  /*margin-top: 6px;*/\n  margin: 6px auto; /* Center code in its pane */\n}\n\n/* don't wrap lines within code output ... FORCE scrollbars to appear */\ndiv.ExecutionVisualizer table#pyCodeOutput td {\n  white-space: nowrap;\n  vertical-align: middle; /* explicitly force, to override external CSS conflicts */\n}\n\ndiv.ExecutionVisualizer #leftCodeGutterSVG {\n  width: 18px;\n  min-width: 18px; /* make sure it doesn't squash too thin */\n  height: 0px; /* programmatically set this later ... IE needs this to\n                  be 0 or it defaults to something arbitrary and gross */\n}\n\ndiv.ExecutionVisualizer #prevLegendArrowSVG,\ndiv.ExecutionVisualizer #curLegendArrowSVG {\n  width: 18px;\n  height: 10px;\n}\n\ndiv.ExecutionVisualizer .arrow {\n  font-size: 16pt;\n}\n\ndiv.ExecutionVisualizer table#pyCodeOutput .lineNo {\n  color: #aaa;\n  padding: 0.2em;\n  padding-left: 0.3em;\n  padding-right: 0.5em;\n  text-align: right;\n}\n\ndiv.ExecutionVisualizer table#pyCodeOutput .cod {\n  /*font-weight: bold;*/\n  margin-left: 3px;\n  padding-left: 7px;\n  text-align: left; /* necessary or else doesn't work properly in IE */\n  color: black;\n}\n\ndiv.ExecutionVisualizer div#progOutputs {\n  margin-top: 8px;\n}\n\ndiv.ExecutionVisualizer div#legendDiv {\n  margin-top: 10px;\n  padding: 0px;\n  text-align: left;\n  color: #666;\n  font-size: 9pt;\n}\n\ndiv.ExecutionVisualizer div#editCodeLinkDiv {\n  text-align: center;\n  /*\n  margin-top: 12px;\n  margin-bottom: 4px;\n  */\n  margin: 8px auto;\n  font-size: 11pt;\n}\n\ndiv.ExecutionVisualizer div#annotateLinkDiv {\n  /*text-align: left;*/\n  margin-top: 0px;\n  margin-bottom: 12px;\n  /*\n  margin-left: auto;\n  margin-right: auto;\n  */\n}\n\ndiv.ExecutionVisualizer div#stepAnnotationDiv {\n  margin-bottom: 12px;\n}\n\ndiv.ExecutionVisualizer textarea#stepAnnotationEditor,\ndiv.ExecutionVisualizer textarea#vizTitleEditor,\ndiv.ExecutionVisualizer textarea#vizDescriptionEditor {\n  border: 1px solid #999999;\n  padding: 4px;\n\n  overflow: auto; /* to look pretty on IE */\n  /* make sure textarea doesn't grow and stretch */\n  resize: none;\n}\n\n\ndiv.ExecutionVisualizer #errorOutput {\n  color: #e93f34; /* should match brightRed JavaScript variable */\n  font-size: 12pt;\n  padding: 2px;\n  line-height: 1.5em;\n  margin-bottom: 4px;\n}\n\n/* VCR control buttons for stepping through execution */\n\ndiv.ExecutionVisualizer #vcrControls {\n  margin: 15px auto;\n  /*width: 100%;*/\n  text-align: center;\n}\n\ndiv.ExecutionVisualizer #vcrControls button {\n  margin-left: 2px;\n  margin-right: 2px;\n}\n\ndiv.ExecutionVisualizer #vcrControls #curInstr {\n  margin-left: 4px;\n  margin-right: 4px;\n}\n\ndiv.ExecutionVisualizer #pyStdout {\n  border: 1px solid #999999;\n  font-size: 12pt;\n  padding: 4px;\n  font-family: Andale mono, monospace;\n\n  overflow: auto; /* to look pretty on IE */\n  /* make sure textarea doesn't grow and stretch */\n  resize: none;\n  min-height: 100px!important;\n}\n\n\ndiv.ExecutionVisualizer .vizFrame {\n  margin-bottom: 20px;\n  padding-left: 8px;\n  border-left: 2px solid #cccccc;\n}\n\n\n/* Rendering of primitive types */\n\ndiv.ExecutionVisualizer .nullObj {\n  font-size: 8pt;\n}\n\ndiv.ExecutionVisualizer .stringObj,\ndiv.ExecutionVisualizer .customObj,\ndiv.ExecutionVisualizer .funcObj {\n  font-family: Andale mono, monospace;\n  white-space: nowrap;\n}\n\ndiv.ExecutionVisualizer .retval {\n  font-size: 9pt;\n}\n\ndiv.ExecutionVisualizer .stackFrame .retval {\n  color: #e93f34; /* highlight non-zombie stack frame return values -\n                     should match brightRed JavaScript variable */\n}\n\n/* Rendering of basic compound types */\n\ndiv.ExecutionVisualizer table.listTbl,\ndiv.ExecutionVisualizer table.tupleTbl,\ndiv.ExecutionVisualizer table.setTbl {\n  background-color: #ffffc6;\n}\n\n\ndiv.ExecutionVisualizer table.listTbl {\n  border: 0px solid black;\n  border-spacing: 0px;\n}\n\ndiv.ExecutionVisualizer table.listTbl td.listHeader,\ndiv.ExecutionVisualizer table.tupleTbl td.tupleHeader {\n  padding-left: 4px;\n  padding-top: 2px;\n  padding-bottom: 3px;\n  font-size: 8pt;\n  color: #777;\n  text-align: left;\n  border-left: 1px solid #555555;\n}\n\ndiv.ExecutionVisualizer table.tupleTbl {\n  border-spacing: 0px;\n  color: black;\n\n  border-bottom: 1px solid #555555; /* must match td.tupleHeader border */\n  border-top: 1px solid #555555; /* must match td.tupleHeader border */\n  border-right: 1px solid #555555; /* must match td.tupleHeader border */\n}\n\n\ndiv.ExecutionVisualizer table.listTbl td.listElt {\n  border-bottom: 1px solid #555555; /* must match td.listHeader border */\n  border-left: 1px solid #555555; /* must match td.listHeader border */\n}\n\ndiv.ExecutionVisualizer table.tupleTbl td.tupleElt {\n  border-left: 1px solid #555555; /* must match td.tupleHeader border */\n}\n\ndiv.ExecutionVisualizer table.customObjTbl {\n  background-color: white;\n  color: black;\n  border: 1px solid black;\n}\n\ndiv.ExecutionVisualizer table.customObjTbl td.customObjElt {\n  padding: 5px;\n}\n\ndiv.ExecutionVisualizer table.listTbl td.listElt,\ndiv.ExecutionVisualizer table.tupleTbl td.tupleElt {\n  padding-top: 0px;\n  padding-bottom: 8px;\n  padding-left: 10px;\n  padding-right: 10px;\n  vertical-align: bottom;\n}\n\ndiv.ExecutionVisualizer table.setTbl {\n  border: 1px solid #555555;\n  border-spacing: 0px;\n  text-align: center;\n}\n\ndiv.ExecutionVisualizer table.setTbl td.setElt {\n  padding: 8px;\n}\n\n\ndiv.ExecutionVisualizer table.dictTbl,\ndiv.ExecutionVisualizer table.instTbl,\ndiv.ExecutionVisualizer table.classTbl {\n  border-spacing: 1px;\n}\n\ndiv.ExecutionVisualizer table.dictTbl td.dictKey,\ndiv.ExecutionVisualizer table.instTbl td.instKey,\ndiv.ExecutionVisualizer table.classTbl td.classKey {\n  background-color: #faebbf;\n}\n\ndiv.ExecutionVisualizer table.dictTbl td.dictVal,\ndiv.ExecutionVisualizer table.instTbl td.instVal,\ndiv.ExecutionVisualizer table.classTbl td.classVal {\n  background-color: #ffffc6;\n}\n\n\ndiv.ExecutionVisualizer table.dictTbl td.dictKey,\ndiv.ExecutionVisualizer table.instTbl td.instKey,\ndiv.ExecutionVisualizer table.classTbl td.classKey {\n  padding-top: 12px /*15px*/;\n  padding-bottom: 5px;\n  padding-left: 10px;\n  padding-right: 4px;\n\n  text-align: right;\n}\n\ndiv.ExecutionVisualizer table.dictTbl td.dictVal,\ndiv.ExecutionVisualizer table.instTbl td.instVal,\ndiv.ExecutionVisualizer table.classTbl td.classVal {\n  padding-top: 12px /*15px*/;\n  padding-bottom: 5px;\n  padding-right: 10px;\n  padding-left: 4px;\n}\n\n\ndiv.ExecutionVisualizer table.classTbl td,\ndiv.ExecutionVisualizer table.instTbl td {\n  border-bottom: 1px #888 solid;\n}\n\ndiv.ExecutionVisualizer table.classTbl td.classVal,\ndiv.ExecutionVisualizer table.instTbl td.instVal {\n  border-left: 1px #888 solid;\n}\n\ndiv.ExecutionVisualizer table.classTbl {\n  border-collapse: collapse;\n  border: 1px #888 solid;\n}\n\n/* only add a border to dicts if they're embedded within another object */\ndiv.ExecutionVisualizer td.listElt table.dictTbl,\ndiv.ExecutionVisualizer td.tupleElt table.dictTbl,\ndiv.ExecutionVisualizer td.dictVal table.dictTbl,\ndiv.ExecutionVisualizer td.instVal table.dictTbl,\ndiv.ExecutionVisualizer td.classVal table.dictTbl {\n  border: 1px #888 solid;\n}\n\ndiv.ExecutionVisualizer .objectIdLabel {\n  font-size: 8pt;\n  color: #444;\n  margin-bottom: 2px;\n}\n\ndiv.ExecutionVisualizer .typeLabel {\n  font-size: 8pt;\n  color: #555;\n  margin-bottom: 2px;\n}\n\ndiv.ExecutionVisualizer div#stack,\ndiv.ExecutionVisualizer div#globals_area {\n  padding-left: 10px;\n  padding-right: 30px;\n\n  /* no longer necessary ... */\n  /*float: left;*/\n  /* border-right: 1px dashed #bbbbbb; */\n}\n\ndiv.ExecutionVisualizer div.stackFrame,\ndiv.ExecutionVisualizer div.zombieStackFrame {\n  background-color: #ffffff;\n  margin-bottom: 15px;\n  padding: 2px;\n  padding-left: 6px;\n  padding-right: 6px;\n  padding-bottom: 4px;\n  font-size: 10pt;\n}\n\ndiv.ExecutionVisualizer div.zombieStackFrame {\n  border-left: 1px dotted #aaa;\n  /*color: #c0c0c0;*/\n  color: #a0a0a0;\n}\n\ndiv.ExecutionVisualizer div.highlightedStackFrame {\n  background-color: #e2ebf6;\n  /*background-color: #d7e7fb;*/\n\n  /*background-color: #c0daf8;*/\n  /*background-color: #9eeaff #c5dfea;*/\n}\n\ndiv.ExecutionVisualizer div.stackFrame,\ndiv.ExecutionVisualizer div.highlightedStackFrame {\n  border-left: 1px solid #a6b3b6;\n}\n\n\ndiv.ExecutionVisualizer div.stackFrameHeader {\n  font-family: Andale mono, monospace;\n  font-size: 10pt;\n  margin-top: 4px;\n  margin-bottom: 3px;\n  white-space: nowrap;\n}\n\ndiv.ExecutionVisualizer td.stackFrameVar {\n  text-align: right;\n  padding-right: 8px;\n  padding-top: 3px;\n  padding-bottom: 3px;\n}\n\ndiv.ExecutionVisualizer td.stackFrameValue {\n  text-align: left;\n  border-bottom: 1px solid #aaaaaa;\n  border-left: 1px solid #aaaaaa;\n\n  vertical-align: middle;\n\n  padding-top: 3px;\n  padding-left: 3px;\n  padding-bottom: 3px;\n}\n\ndiv.ExecutionVisualizer .stackFrameVarTable tr {\n\n}\n\ndiv.ExecutionVisualizer .stackFrameVarTable {\n  text-align: right;\n  padding-top: 3px;\n\n  /* right-align the table */\n  margin-left: auto;\n  margin-right: 0px;\n\n  /* hack to counteract possible nasty CSS reset styles from parent divs */\n  border-collapse: separate;\n  border-spacing: 2px;\n}\n\ndiv.ExecutionVisualizer div#heap {\n  float: left;\n  padding-left: 30px;\n}\n\ndiv.ExecutionVisualizer td.toplevelHeapObject {\n  /* needed for d3 to do transitions */\n  padding-left: 8px;\n  padding-right: 8px;\n  padding-top: 4px;\n  padding-bottom: 4px;\n  /*\n  border: 2px dotted white;\n  border-color: white;\n  */\n}\n\ndiv.ExecutionVisualizer table.heapRow {\n  margin-bottom: 10px;\n}\n\ndiv.ExecutionVisualizer div.heapObject {\n  padding-left: 2px; /* leave a TINY amount of room for connector endpoints */\n}\n\ndiv.ExecutionVisualizer div.heapPrimitive {\n  padding-left: 4px; /* leave some more room for connector endpoints */\n}\n\ndiv.ExecutionVisualizer div#stackHeader {\n  margin-bottom: 15px;\n  text-align: right;\n}\n\ndiv.ExecutionVisualizer div#heapHeader {\n  /*margin-top: 2px;\n  margin-bottom: 13px;*/\n  margin-bottom: 15px;\n}\n\ndiv.ExecutionVisualizer div#stackHeader,\ndiv.ExecutionVisualizer div#heapHeader {\n  color: #333333;\n  font-size: 10pt;\n}\n\ndiv.ExecutionVisualizer #executionSlider {\n  /* if you set 'width', then it looks ugly when you dynamically resize */\n  margin-top: 15px;\n  margin-bottom: 5px;\n\n  margin-left: auto;\n  margin-right: auto;\n\n  width: 95%;\n}\n\ndiv.ExecutionVisualizer #executionSliderCaption {\n  font-size: 8pt;\n  color: #666666;\n  margin-top: 15px;\n}\n\ndiv.ExecutionVisualizer #executionSliderFooter {\n  margin-top: -7px; /* make it butt up against #executionSlider */\n}\n\n\n/* darken slider handle a bit */\ndiv.ExecutionVisualizer .ui-slider .ui-slider-handle {\n  border: 1px solid #999;\n}\n\n\n/* for annotation bubbles */\n\n/* For styling tricks, see: http://css-tricks.com/textarea-tricks/ */\ntextarea.bubbleInputText {\n  border: 1px solid #ccc;\n  outline: none;\n  overflow: auto; /* to look pretty on IE */\n\n  /* make sure textarea doesn't grow and stretch the enclosing bubble */\n  resize: none;\n  width: 225px;\n  max-width: 225px;\n  height: 35px;\n  max-height: 35px;\n}\n\n\n.ui-tooltip-pgbootstrap,\ntextarea.bubbleInputText {\n  font-family: verdana, arial, helvetica, sans-serif;\n\tfont-size: 9pt;\n\tline-height: 1.3em;\n}\n\n\n/* modified version of Twitter bootstrap style by Philip Guo */\n.ui-tooltip-pgbootstrap{\n\tcolor: #333;\n\tbackground-color: #ffffff;\n\n\tmax-width: 250px;\n\tmin-width: 10px;\n\n\tborder: 2px solid #4284D3;\n\n  cursor: pointer;\n\n\t*border-right-width: 2px;\n\t*border-bottom-width: 2px;\n\n\t-webkit-border-radius: 5px;\n\t-moz-border-radius: 5px;\n\tborder-radius: 5px;\n\n  /* way too poofy ...\n\t-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n\t-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n\tbox-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n  */\n\n\t-webkit-box-shadow: 2px 2px 3px 0px rgba(0, 0, 0, 0.2);\n\t-moz-box-shadow: 2px 2px 3px 0px rgba(0, 0, 0, 0.2);\n\tbox-shadow: 2px 2px 3px 0px rgba(0, 0, 0, 0.2);\n\n\t-webkit-background-clip: padding-box;\n\t-moz-background-clip: padding;\n\tbackground-clip: padding-box;\n}\n\n\t.ui-tooltip-pgbootstrap .ui-tooltip-titlebar{\n\t\tfont-size: 18px;\n\t\tline-height: 22px;\n\n\t\tborder-bottom: 1px solid #ccc;\n\t\tbackground-color: transparent;\n\t}\n\n\t.ui-tooltip-pgbootstrap .ui-tooltip-content{\n\t\tpadding: 5px /* 5px is minimum or else it might look ugly */ 8px;\n\t}\n\n\n\t\t.ui-tooltip-pgbootstrap .ui-tooltip-titlebar .ui-state-default{\n\t\t\tright: 9px; top: 49%;\n\t\t\tborder-style: none;\n\t\t}\n\n\t.ui-tooltip-pgbootstrap .ui-tooltip-icon{\n\t\tbackground: white;\n\t}\n\n\t\t.ui-tooltip-pgbootstrap .ui-tooltip-icon .ui-icon{\n\t\t\twidth: auto;\n\t\t\theight: auto;\n\t\t\tfloat: right;\n\t\t\tfont-size: 20px;\n\t\t\tfont-weight: bold;\n\t\t\tline-height: 18px;\n\t\t\tcolor: #000000;\n\t\t\ttext-shadow: 0 1px 0 #ffffff;\n\t\t\topacity: 0.2;\n\t\t\tfilter: alpha(opacity=20);\n\t\t}\n\n\t\t.ui-tooltip-pgbootstrap .ui-tooltip-icon .ui-icon:hover{\n\t\t\tcolor: #000000;\n\t\t\ttext-decoration: none;\n\t\t\tcursor: pointer;\n\t\t\topacity: 0.4;\n\t\t\tfilter: alpha(opacity=40);\n\t\t}\n\n\n/* Add rounded corners to your tooltips in: FF3+, Chrome 2+, Opera 10.6+, IE9+, Safari 2+ */\n.ui-tooltip-pgbootstrap{\n\t-moz-border-radius: 5px;\n\t-webkit-border-radius: 5px;\n\tborder-radius: 5px;\n}\n\n\n.ui-tooltip-pgbootstrap-stub{\n\tborder: 1px solid #999;\n\n  /*\n\t-webkit-box-shadow: none;\n\t-moz-box-shadow: none;\n\tbox-shadow: none;\n  */\n}\n\n\t.ui-tooltip-pgbootstrap-stub .ui-tooltip-content{\n\t\tpadding: 6px 9px;\n\t}\n\n\ndiv.ExecutionVisualizer .annotationText,\ndiv.ExecutionVisualizer .vizDescriptionText {\n  font-family: verdana, arial, helvetica, sans-serif;\n  font-size: 11pt;\n  line-height: 1.5em;\n}\n\ndiv.ExecutionVisualizer .vizTitleText {\n  font-family: verdana, arial, helvetica, sans-serif;\n  font-size: 16pt;\n  margin-bottom: 12pt;\n}\n\ndiv.ExecutionVisualizer div#vizHeader {\n  margin-bottom: 10px;\n  width: 700px;\n  max-width: 700px;\n}\n\n/* prev then curr, so curr gets precedence when both apply */\ndiv.ExecutionVisualizer .highlight-prev {\n  background-color: #F0F0EA;\n}\n\ndiv.ExecutionVisualizer .highlight-cur {\n  background-color: #FFFF66;\n}\n\ndiv.ExecutionVisualizer .highlight-legend {\n  padding: 2px;\n}\n\n/* resizing sliders from David Pritchard */\n.ui-resizable-e {\n  background-color: #dddddd;\n  width: 1px;\n  border: 3px solid white;\n}\n\n.ui-resizable-e:hover {\n  border-color: #dddddd;\n}\n\ndiv.ExecutionVisualizer div#rawUserInputDiv {\n  padding: 5px;\n  width: 95%;\n  margin: 5px auto;\n  text-align: center;\n  border: 1px #e93f34 solid;\n}\n\n/* for pyCrazyMode */\n\n/* prev then curr, so curr gets precedence when both apply */\ndiv.ExecutionVisualizer .pycrazy-highlight-prev {\n  background-color: #eeeeee; /*#F0F0EA;*/\n  /*\n  text-decoration: none;\n  border-bottom: 1px solid #dddddd;\n  */\n}\n\ndiv.ExecutionVisualizer .pycrazy-highlight-cur {\n  background-color: #FFFF66;\n  /* aligned slightly higher than border-bottom */\n  /*\n  text-decoration: none;\n  border-bottom: 1px solid #e93f34;\n  */\n}\n\ndiv.ExecutionVisualizer .pycrazy-highlight-prev-and-cur {\n  background-color: #FFFF66;\n\n  text-decoration: none;\n  border-bottom: 1px solid #999999;\n}\n\n\n#optTabularView thead.stepTableThead {\n  background-color: #bbb;\n}\n\n#optTabularView tbody.stepTableTbody {\n}\n\n#optTabularView td.stepTableTd {\n  padding: 3px 10px;\n}\n", ""]);
// Exports
module.exports = exports;


/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./runestone/common/css/user-highlights.css":
/*!****************************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./runestone/common/css/user-highlights.css ***!
  \****************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Imports
var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
var ___CSS_LOADER_GET_URL_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/getUrl.js */ "./node_modules/css-loader/dist/runtime/getUrl.js");
var ___CSS_LOADER_URL_IMPORT_0___ = __webpack_require__(/*! ./completed.png */ "./runestone/common/css/completed.png");
var ___CSS_LOADER_URL_IMPORT_1___ = __webpack_require__(/*! ./active.png */ "./runestone/common/css/active.png");
exports = ___CSS_LOADER_API_IMPORT___(false);
var ___CSS_LOADER_URL_REPLACEMENT_0___ = ___CSS_LOADER_GET_URL_IMPORT___(___CSS_LOADER_URL_IMPORT_0___);
var ___CSS_LOADER_URL_REPLACEMENT_1___ = ___CSS_LOADER_GET_URL_IMPORT___(___CSS_LOADER_URL_IMPORT_1___);
// Module
exports.push([module.i, "/* Variables holding theme colors */\n:root {\n\t--menu: #FFFFFF;\n}\n\n[data-theme=\"dark\"] {\n\t--menu: #000000;\n}\n\n.my-highlighted-text{\n\tbackground-color:#FFFF99;\n}\n.social-highlighted-text{\n\tbackground-color:#FFCCCC;\n}\n.highlight-option-box{\n\tposition:absolute;\n\ttop:0px;\n\tleft:0px;\n}\n.highlight-option-box li{ \n\theight:24px;\n}\n#continue-reading{\n\tpadding-top:20px;\n\tdisplay: none;\n}\n#continue-reading a{\n\tfloat:right;\n\tmargin-right:20px;\n}\nli.completed\n{\nlist-style-image: url(" + ___CSS_LOADER_URL_REPLACEMENT_0___ + ");\n}\nli.active\n{\nlist-style-image: url(" + ___CSS_LOADER_URL_REPLACEMENT_1___ + ");\n}\n.infoTextCompleted{\n\tmargin-left:10px;\n\tfont-size:0.9em;\n\tcolor:#3dc682;\n\tdisplay:none;\n\tfont-style:italic;\n\topacity: 0.7;\n}\n.infoTextActive{\n\tmargin-left:10px;\n\tfont-size:0.9em;\n\tcolor:#ff9f19;\n\tdisplay:none;\n\tfont-style:italic;\n\topacity: 0.7;\n}\n.dropdown-menu{\n\tbackground-clip: padding-box;\n    background-color: var(--menu);\n    border: 1px solid rgba(0, 0, 0, 0.2);\n    border-radius: 5px 5px 5px 5px;\n    box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n    display: none;\n    float: left;\n    left: 0;\n    list-style: none outside none;\n    margin: 1px 0 0;\n    min-width: 160px;\n    padding: 4px 0;\n    position: absolute;\n    top: 100%;\n    z-index: 1000;\n}\n.dropdown-menu ul {\n    list-style: disc outside none;\n    margin: 0 0 9px 25px;\n    padding: 0;\n}\n\n.sidebar-highlights{\n    background-color:#FFFF99;\n}\ndiv.documentwrapper {\n\tborder : 0 none;\n}\ndiv.bodywrapper {\n\tborder-right: 0 none;\n}\ndiv.sphinxsidebar {\n\tborder-left: 1px solid #CCCCCC;\n\tpadding-left: 10px;\n}\n.body>.section .section{\n\tpadding-top: 1px;\n}\n#jump-to-chapter{\n    border: 1px solid #CCCCCC;\n    letter-spacing: 0.015em;\n    line-height: 120%;\n    padding: 0.5em;\n\twidth: 80%;\n\tmargin-left: auto;\n\tmargin-right: auto;\n}\n#navigation-links{\n\tbackground-color: #F8F8F8;\n    border: 1px solid #CCCCCC;\n\tpadding:10px;\n\tmargin:10px;\n}\n", ""]);
// Exports
module.exports = exports;


/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./runestone/datafile/css/datafile.css":
/*!***********************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./runestone/datafile/css/datafile.css ***!
  \***********************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Imports
var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
exports = ___CSS_LOADER_API_IMPORT___(false);
// Module
exports.push([module.i, ".datafiletextfield {\n    margin-left: auto;\n    margin-right: auto;\n    display: block;\n}\n\n.datafile_caption {\n    margin-bottom: -1px; /* to collapse border w/ below w/o needing border-bottom: none */\n    margin-left: 1em;\n    display: inline-block;\n    padding: 0 0.2em;\n    border: 1px solid #ccc;\n    border-radius: 4px 4px 0 0;\n    font-style: italic;\n    background: white;\n}\n.datafile_caption code {\n    background: none;\n    border: none;\n}\n.datafile pre {\n    /* for ease of setting height based on #rows */\n    line-height: 1.5em;\n    max-width: 100%;\n    overflow: auto;\n    white-space: pre;\n    height: 30em;\n}\n.datafile_hidden {\n    display: none;\n}\n\n", ""]);
// Exports
module.exports = exports;


/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./runestone/dragndrop/css/dragndrop.css":
/*!*************************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./runestone/dragndrop/css/dragndrop.css ***!
  \*************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Imports
var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
exports = ___CSS_LOADER_API_IMPORT___(false);
// Module
exports.push([module.i, ".rsdraggable {\n    margin-left: 2%;\n    margin-top: 5px;\n    display:inline;\n    float:left;\n\ttext-align: left;\n\tvertical-align: top;\n    background-color: #efefff;\n    font-family: monospace;\n    font-size: 120%;\n    padding-bottom: 10px;\n    min-width: 48%;\n    max-width: 48%;\n    border: 1px solid #efefff;\n    box-sizing: border-box;\n}\n.drag-button {\n    display: inline-block;\n}\n.drag-reset {\n    background-color: #474949;\n    color: white;\n}\n.drag-reset:hover {\n    background-color: black;\n    color: white;\n}\n.drop-incorrect {\n    border: 1px solid red !important;\n    background-color: #f2dede !important;\n}\n.drag-reset:enabled {\n    background-color: #474949;\n    color: white;\n}\n.draggable-drag {\n    -moz-border-radius:10px;\n    -webkit-border-radius:10px;\n    border-radius: 10px;\n    display: block;\n    background-color:#EFEFEF;\n    border:1px solid lightgray;\n    padding:10px;\n    margin-top: 5px;\n    cursor: move;\n}\n\n.draggable-feedback {\n    text-align: left !important;\n    clear: both;\n}\n\n.draggable-drop {\n    -moz-border-radius:10px;\n    -webkit-border-radius:10px;\n    border-radius: 10px;\n    display: block;\n    background-color:#DDDDDD;\n    border:1px solid lightgray;\n    padding:10px;\n    margin-top: 5px;\n}\n.draggable-container {\n    text-align:center;\n}\n\n.possibleDrop {\n    border-color: #000000;\n    background-color: #CCCCCC;\n}\n", ""]);
// Exports
module.exports = exports;


/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./runestone/fitb/css/fitb.css":
/*!***************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./runestone/fitb/css/fitb.css ***!
  \***************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Imports
var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
exports = ___CSS_LOADER_API_IMPORT___(false);
// Module
exports.push([module.i, ".selectwidthauto {\n     width:auto !important;\n     display: inline-block !important;\n     margin: 5px;\n}\n\ninput.input-validation-error {\n    border: 1px solid #e80c4d;\n}\n\n", ""]);
// Exports
module.exports = exports;


/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./runestone/mchoice/css/mchoice.css":
/*!*********************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./runestone/mchoice/css/mchoice.css ***!
  \*********************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Imports
var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
exports = ___CSS_LOADER_API_IMPORT___(false);
// Module
exports.push([module.i, ".runestone label p {\n    display: inline;\n}\n", ""]);
// Exports
module.exports = exports;


/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./runestone/parsons/css/parsons.css":
/*!*********************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./runestone/parsons/css/parsons.css ***!
  \*********************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Imports
var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
var ___CSS_LOADER_GET_URL_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/getUrl.js */ "./node_modules/css-loader/dist/runtime/getUrl.js");
var ___CSS_LOADER_URL_IMPORT_0___ = __webpack_require__(/*! ./images/parsons-ll.svg */ "./runestone/parsons/css/images/parsons-ll.svg");
var ___CSS_LOADER_URL_IMPORT_1___ = __webpack_require__(/*! ./images/parsons-rl.svg */ "./runestone/parsons/css/images/parsons-rl.svg");
var ___CSS_LOADER_URL_IMPORT_2___ = __webpack_require__(/*! ./images/parsons-lr.svg */ "./runestone/parsons/css/images/parsons-lr.svg");
var ___CSS_LOADER_URL_IMPORT_3___ = __webpack_require__(/*! ./images/parsons-rr.svg */ "./runestone/parsons/css/images/parsons-rr.svg");
exports = ___CSS_LOADER_API_IMPORT___(false);
var ___CSS_LOADER_URL_REPLACEMENT_0___ = ___CSS_LOADER_GET_URL_IMPORT___(___CSS_LOADER_URL_IMPORT_0___);
var ___CSS_LOADER_URL_REPLACEMENT_1___ = ___CSS_LOADER_GET_URL_IMPORT___(___CSS_LOADER_URL_IMPORT_1___);
var ___CSS_LOADER_URL_REPLACEMENT_2___ = ___CSS_LOADER_GET_URL_IMPORT___(___CSS_LOADER_URL_IMPORT_2___);
var ___CSS_LOADER_URL_REPLACEMENT_3___ = ___CSS_LOADER_GET_URL_IMPORT___(___CSS_LOADER_URL_IMPORT_3___);
// Module
exports.push([module.i, ".parsons .sortable-code-container {\n    text-align: center;\n}\n.parsons .sortable-code-container:not(.block) {\n    /* Remove text highlighting in widget */\n    -webkit-user-select: none;\n    -moz-user-select: -moz-none;\n    -ms-user-select: none;\n    user-select: none;\n}\n.parsons .sortable-code {\n    position: static;\n    padding-left: 0px;\n    margin: 0px 15px;\n    display: inline-block;\n    text-align: left;\n    vertical-align: top;\n}\n.parsons .source,\n.parsons .answer,\n.parsons .answer1,\n.parsons .answer2,\n.parsons .answer3,\n.parsons .answer4 {\n    position: relative;\n    font-size: 100%;\n    list-style: none;\n    background-color: #efefff;\n    padding-bottom: 10px;\n    padding-left: 0;\n    margin-left: 0;\n    border: 1px solid #b8b8f2;\n}\n.parsons .source {\n    background-color: #efefff;\n}\n.parsons .answer {\n    background-color: #ffa;\n    border-color: #ee0;\n}\n.parsons .answer.incorrect {\n    background-color: #f2dede;\n    border-color: #f2b6b6;\n}\n.parsons .answer.correct {\n    background-color: #dff0d8;\n    border-color: #ade595;\n}\n.parsons .answer1 {\n    background: linear-gradient(#ee0, #ee0) no-repeat border-box;\n    background-size: 1px 100%;\n    background-position: 30px 0;\n    background-origin: padding-box;\n    background-color: #ffa;\n    border-color: #ee0;\n}\n.parsons .answer1.incorrect {\n    background: linear-gradient(#f2b6b6, #f2b6b6) no-repeat border-box;\n    background-size: 1px 100%;\n    background-position: 30px 0;\n    background-origin: padding-box;\n    background-color: #f2dede;\n    border-color: #f2b6b6;\n}\n.parsons .answer1.correct {\n    background: linear-gradient(#ade595, #ade595) no-repeat border-box;\n    background-size: 1px 100%;\n    background-position: 30px 0;\n    background-origin: padding-box;\n    background-color: #dff0d8;\n    border-color: #ade595;\n}\n.parsons .answer2 {\n    background: linear-gradient(#ee0, #ee0) no-repeat border-box,\n        linear-gradient(#ee0, #ee0) no-repeat border-box;\n    background-size: 1px 100%, 1px 100%;\n    background-position: 30px 0, 60px 0;\n    background-origin: padding-box, padding-box;\n    background-color: #ffa;\n    border-color: #ee0;\n}\n.parsons .answer2.incorrect {\n    background: linear-gradient(#f2b6b6, #f2b6b6) no-repeat border-box,\n        linear-gradient(#f2b6b6, #f2b6b6) no-repeat border-box;\n    background-size: 1px 100%, 1px 100%;\n    background-position: 30px 0, 60px 0;\n    background-origin: padding-box, padding-box;\n    background-color: #f2dede;\n    border-color: #f2b6b6;\n}\n.parsons .answer2.correct {\n    background: linear-gradient(#ade595, #ade595) no-repeat border-box,\n        linear-gradient(#ade595, #ade595) no-repeat border-box;\n    background-size: 1px 100%, 1px 100%;\n    background-position: 30px 0, 60px 0;\n    background-origin: padding-box, padding-box;\n    background-color: #dff0d8;\n    border-color: #ade595;\n}\n.parsons .answer3 {\n    background: linear-gradient(#ee0, #ee0) no-repeat border-box,\n        linear-gradient(#ee0, #ee0) no-repeat border-box,\n        linear-gradient(#ee0, #ee0) no-repeat border-box;\n    background-size: 1px 100%, 1px 100%, 1px 100%;\n    background-position: 30px 0, 60px 0, 90px 0;\n    background-origin: padding-box, padding-box, padding-box;\n    background-color: #ffa;\n    border-color: #ee0;\n}\n.parsons .answer3.incorrect {\n    background: linear-gradient(#f2b6b6, #f2b6b6) no-repeat border-box,\n        linear-gradient(#f2b6b6, #f2b6b6) no-repeat border-box,\n        linear-gradient(#f2b6b6, #f2b6b6) no-repeat border-box;\n    background-size: 1px 100%, 1px 100%, 1px 100%;\n    background-position: 30px 0, 60px 0, 90px 0;\n    background-origin: padding-box, padding-box, padding-box;\n    background-color: #f2dede;\n    border-color: #f2b6b6;\n}\n.parsons .answer3.correct {\n    background: linear-gradient(#ade595, #ade595) no-repeat border-box,\n        linear-gradient(#ade595, #ade595) no-repeat border-box,\n        linear-gradient(#ade595, #ade595) no-repeat border-box;\n    background-size: 1px 100%, 1px 100%, 1px 100%;\n    background-position: 30px 0, 60px 0, 90px 0;\n    background-origin: padding-box, padding-box, padding-box;\n    background-color: #dff0d8;\n    border-color: #ade595;\n}\n.parsons .answer4 {\n    background: linear-gradient(#ee0, #ee0) no-repeat border-box,\n        linear-gradient(#ee0, #ee0) no-repeat border-box,\n        linear-gradient(#ee0, #ee0) no-repeat border-box,\n        linear-gradient(#ee0, #ee0) no-repeat border-box;\n    background-size: 1px 100%, 1px 100%, 1px 100%, 1px 100%;\n    background-position: 30px 0, 60px 0, 90px 0, 120px 0;\n    background-origin: padding-box, padding-box, padding-box, padding-box;\n    background-color: #ffa;\n    border-color: #ee0;\n}\n.parsons .answer4.incorrect {\n    background: linear-gradient(#f2b6b6, #f2b6b6) no-repeat border-box,\n        linear-gradient(#f2b6b6, #f2b6b6) no-repeat border-box,\n        linear-gradient(#f2b6b6, #f2b6b6) no-repeat border-box,\n        linear-gradient(#f2b6b6, #f2b6b6) no-repeat border-box;\n    background-size: 1px 100%, 1px 100%, 1px 100%, 1px 100%;\n    background-position: 30px 0, 60px 0, 90px 0, 120px 0;\n    background-origin: padding-box, padding-box, padding-box, padding-box;\n    background-color: #f2dede;\n    border-color: #f2b6b6;\n}\n.parsons .answer4.correct {\n    background: linear-gradient(#ade595, #ade595) no-repeat border-box,\n        linear-gradient(#ade595, #ade595) no-repeat border-box,\n        linear-gradient(#ade595, #ade595) no-repeat border-box,\n        linear-gradient(#ade595, #ade595) no-repeat border-box;\n    background-size: 1px 100%, 1px 100%, 1px 100%, 1px 100%;\n    background-position: 30px 0, 60px 0, 90px 0, 120px 0;\n    background-origin: padding-box, padding-box, padding-box, padding-box;\n    background-color: #dff0d8;\n    border-color: #ade595;\n}\n.parsons .block {\n    position: absolute;\n    -moz-border-radius: 10px;\n    -webkit-border-radius: 10px;\n    border-radius: 10px;\n    background-color: #efefef;\n    border: 1px solid #d3d3d3;\n    padding: 5px 10px;\n    margin-top: 5px;\n    overflow: hidden;\n    cursor: move;\n}\n.parsons .block.disabled {\n    opacity: 0.3;\n}\n.parsons .block.down {\n    border: 2px solid black;\n    padding: 4px 9px;\n}\n.parsons .block.up {\n    border: 2px solid black;\n    padding: 4px 9px;\n    background-color: white;\n    box-shadow: 3px 3px 4px black;\n}\n.parsons {\n    display: block;\n}\n.parsons .block {\n    clear: both;\n    float: left;\n    /*background-color: transparent;*/\n}\n\n.parsons code {\n    display: inline-block;\n    clear: both;\n    float: left;\n    background-color: transparent;\n}\n\n.parsons-text code {\n    float: none;\n}\n\n.parsons .block p {\n    margin: 0;\n}\n.parsons .block,\n.parsons .block:before,\n.parsons .block:after {\n    box-sizing: content-box;\n}\n.parsons .indent1 {\n    margin-left: 30px;\n}\n.parsons .indent2 {\n    margin-left: 60px;\n}\n.parsons .indent3 {\n    margin-left: 90px;\n}\n.parsons .indent4 {\n    margin-left: 120px;\n}\n.parsons .correct {\n    border: 1px solid #d6e9c6;\n    background: none;\n    background-color: #dff0d8;\n}\n.parsons .indentLeft,\n.parsons .indentRight {\n    border: 2px solid #ffcc00;\n    padding: 4px 9px;\n    background-color: #fff0b3;\n}\n.parsons .indentLeft {\n    background-image: url(" + ___CSS_LOADER_URL_REPLACEMENT_0___ + "),\n        url(" + ___CSS_LOADER_URL_REPLACEMENT_1___ + ");\n    background-repeat: no-repeat, no-repeat;\n    background-position: left center, right center;\n}\n.parsons .indentRight {\n    background-image: url(" + ___CSS_LOADER_URL_REPLACEMENT_2___ + "),\n        url(" + ___CSS_LOADER_URL_REPLACEMENT_3___ + ");\n    background-repeat: no-repeat, no-repeat;\n    background-position: left center, right center;\n}\n.parsons .incorrectPosition {\n    background-color: #ffbaba;\n    border: 1px solid red;\n}\n.parsons .paired {\n    position: absolute;\n    -moz-border-radius: 10px;\n    -webkit-border-radius: 10px;\n    border-radius: 10px;\n    background-color: transparent;\n    border-left: 11px solid #b8b8f2;\n    border-right: 11px solid #b8b8f2;\n    border-top: 1px solid #b8b8f2;\n    border-bottom: 1px solid #b8b8f2;\n    margin-top: 5px;\n    overflow: hidden;\n}\n.parsons {\n    border-left: 0;\n    border-right: 0;\n    border-radius: 0;\n    margin: 10px 0;\n}\n.parsons .parsons-text {\n    max-width: 500pt;\n    margin-left: auto;\n    margin-right: auto;\n}\n\n.parsons .parsons-img {\n    display: inline-block;\n    text-align: center;\n    width: 100%;\n}\n\n.parsons .parsons-controls {\n    max-width: 500pt;\n    margin-left: auto;\n    margin-right: auto;\n    text-align: center;\n}\n.parsons [role=\"tooltip\"] {\n    max-width: 500pt;\n    margin-left: auto;\n    margin-right: auto;\n    margin-top: 5px;\n    margin-bottom: 5px;\n    padding: 0px 10px;\n    top: 0;\n    text-align: center;\n    font-style: italic;\n}\n\n.block-label {\n    font-family: \"Courier\";\n    font-size: 90%;\n    color: #7e7ee7;\n    vertical-align: top;\n    white-space: pre;\n}\n.right-label {\n    padding: 0.2em 0em 0.3em 0.6em;\n}\n.left-label {\n    padding: 0.2em 0.6em 0.3em 0em;\n}\n\n.labels {\n    padding-top: 2px;\n    display: inline-block;\n    vertical-align: top;\n}\n\n.runestone .lines {\n    display: inline-block;\n    align-self: left;\n}\n\n.border_left {\n    border-left: 1px solid #b0b0b0;\n}\n\n.block {\n    display: flex;\n    align-items: stretch;\n    align-content: stretch;\n}\n\n.parsons,\n.parsons .sortable-code-container,\n.cd_section {\n    max-width: none;\n}\n\n#timed_Test .pagination {\n    margin: 5px 0;\n}", ""]);
// Exports
module.exports = exports;


/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./runestone/parsons/css/prettify.css":
/*!**********************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./runestone/parsons/css/prettify.css ***!
  \**********************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Imports
var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
exports = ___CSS_LOADER_API_IMPORT___(false);
// Module
exports.push([module.i, ".str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun{color:#660}.pln{color:#000}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec{color:#606}pre.prettyprint{padding:2px;border:1px solid #888}@media print{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun{color:#440}.pln{color:#000}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}", ""]);
// Exports
module.exports = exports;


/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./runestone/poll/css/poll.css":
/*!***************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./runestone/poll/css/poll.css ***!
  \***************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Imports
var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
exports = ___CSS_LOADER_API_IMPORT___(false);
// Module
exports.push([module.i, "/* Styles for Runestone poll directive */\n.poll .poll-question {\n    padding-bottom: 10px;\n}\n\n.poll-options > label {\n    margin-right: 5px;\n}\n\n.poll .progress {\n    width: 350px;\n    height: 25px;\n    margin-top: -20px;\n}\n\n.poll .progress .progress-bar {\n    height: 30px;\n    color: #333;\n}\n\n.poll-text {\n    line-height: 22px;\n    display: inline-block;\n    vertical-align: text-bottom;\n}\n\n.results-container {\n    display: grid;\n    grid-template-columns: 2em 90%;\n}\n", ""]);
// Exports
module.exports = exports;


/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./runestone/reveal/css/reveal.css":
/*!*******************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./runestone/reveal/css/reveal.css ***!
  \*******************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Imports
var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
exports = ___CSS_LOADER_API_IMPORT___(false);
// Module
exports.push([module.i, ".iguide {\n    background-color: #ddffdd;\n    padding: 1em;\n}", ""]);
// Exports
module.exports = exports;


/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./runestone/shortanswer/css/shortanswer.css":
/*!*****************************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./runestone/shortanswer/css/shortanswer.css ***!
  \*****************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Imports
var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
exports = ___CSS_LOADER_API_IMPORT___(false);
// Module
exports.push([module.i, "div.journal div.latexoutput {\n    background-color: #eeeeee;\n    padding: 1em;\n    margin-bottom: 10px;\n    border-radius: 5px;\n}\n", ""]);
// Exports
module.exports = exports;


/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./runestone/showeval/css/showEval.css":
/*!***********************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./runestone/showeval/css/showEval.css ***!
  \***********************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Imports
var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
exports = ___CSS_LOADER_API_IMPORT___(false);
// Module
exports.push([module.i, "@charset \"UTF-8\";\n\n.evalCont {\n    font-family: monospace;\n    line-height: 1.5em;\n    padding: 9.5px;\n    background-color: #F5F5F5;\n    border: 1px solid #CCC;\n    border-radius: 4px;\n    display: block;\n    width: auto;\n    color: #333\n}\n.anno {\n    width: auto;\n    min-width: 150px;\n    max-width: 250px;\n    max-height: 100px;\n    background-color: #E2EBF6;\n    border: 1px solid #A6B3B6;\n    display: block;\n    position: absolute;\n    left: 80%;\n    top: 5px;\n    overflow: scroll;\n    font-family: sans-serif;\n    box-shadow: -2px 1px 5px #CCC;\n}\n.currentStepDiv {\n    position: relative;\n}\n", ""]);
// Exports
module.exports = exports;


/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./runestone/spreadsheet/css/spreadsheet.css":
/*!*****************************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./runestone/spreadsheet/css/spreadsheet.css ***!
  \*****************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Imports
var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
exports = ___CSS_LOADER_API_IMPORT___(false);
// Module
exports.push([module.i, "div.runestone .jexcel_container {\n    max-height: 550px;\n    overflow: scroll;\n    max-width: 800px;\n    overflow: scroll;\n}", ""]);
// Exports
module.exports = exports;


/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./runestone/tabbedStuff/css/tabbedstuff.css":
/*!*****************************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./runestone/tabbedStuff/css/tabbedstuff.css ***!
  \*****************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Imports
var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
exports = ___CSS_LOADER_API_IMPORT___(false);
// Module
exports.push([module.i, ".tab-pane {\n    padding: 20px 15px 10px 15px;\n}\n\n.question .nav.nav-tabs {\n    padding-top: 6px;\n    background-color: #CFCCB8;\n    border-color: #fbeed5;\n    border-radius: 4px;\n}\n\n.question .nav.nav-tabs li {\n    margin-right: 1px;\n    margin-left: 6px;\n}\n\n.question .nav.nav-tabs li > a {\n    background-color: #E6E2CC;\n    border-bottom-color: #fcf8e3;\n    color: #555;\n}\n\n.question .nav-tabs > li > a:hover {\n    border-color: #b4b19d;\n    border-bottom-width:0;\n}\n\n.question .nav.nav-tabs li.active > a {\n    background-color: #faf7df;\n}\n\n", ""]);
// Exports
module.exports = exports;


/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./runestone/timed/css/timed.css":
/*!*****************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./runestone/timed/css/timed.css ***!
  \*****************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Imports
var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
exports = ___CSS_LOADER_API_IMPORT___(false);
// Module
exports.push([module.i, "/* TIMED ASSESSMENT BUTTON STYLES */\nul#pageNums,\nul#pageNums li {\n    text-align: center;\n    padding: 10px 0;\n}\n\n.pagination > li > a:hover {\n    background-color: #eee !important;\n    color: #000 !important;\n}\n\n/* The following override styles in Bootstrap distribution file (bootstrap.min.css) */\n\n.pagination > .active > a,\n.pagination > .active > span {\n    background-color: #428bca !important;\n    color: #ffffff !important;\n}\n\n.col-md-6-override {\n    text-align: center !important;\n    max-width: 100% !important;\n}\n\n/* end */\n\n.pagination > .answered > a,\n.pagination > .answered > span {\n    background-color: #eee;\n    color: #000;\n}\n\n.pagination > .broken > a,\n.pagination > .broken > span {\n    background-color: #f99;\n    color: #000;\n}\n\n.pagination > .correctCount > a,\n.pagination > .correctCount > span {\n    background-color: #dff0d8;\n    color: #000000;\n    border-color: #000000;\n}\n\n.pagination > .skippedCount > a,\n.pagination > .skippedCount > span {\n    background-color: #fcf8e3;\n    color: #000000;\n    border-color: #000000;\n}\n\n.pagination > .incorrectCount > a,\n.pagination > .incorrectCount > span {\n    background-color: #f2dede;\n    color: #000000;\n    border-color: #000000;\n}\n\n.switchcontainer {\n    height: 600px;\n    overflow: auto;\n}\n", ""]);
// Exports
module.exports = exports;


/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./runestone/video/css/video.css":
/*!*****************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./runestone/video/css/video.css ***!
  \*****************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Imports
var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
exports = ___CSS_LOADER_API_IMPORT___(false);
// Module
exports.push([module.i, ".exercises {\n    background-color:#f0ffff;\n}\n\nfigcaption {\n\tmargin: .75em 0;\n\ttext-align: center;\n\tfont: italic 13px/18px Cambria, Georgia, \"Times New Roman\", Times, serif;\n}\n\nimg.bookfig {\n    margin-left: auto;\n    margin-right: auto;\n}\n\n.cl_caption {\n    text-align: center;\n    font-weight: bold;\n}\n\n", ""]);
// Exports
module.exports = exports;


/***/ }),

/***/ "./node_modules/css-loader/dist/runtime/api.js":
/*!*****************************************************!*\
  !*** ./node_modules/css-loader/dist/runtime/api.js ***!
  \*****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/*
  MIT License http://www.opensource.org/licenses/mit-license.php
  Author Tobias Koppers @sokra
*/
// css base code, injected by the css-loader
// eslint-disable-next-line func-names
module.exports = function (useSourceMap) {
  var list = []; // return the list of modules as css string

  list.toString = function toString() {
    return this.map(function (item) {
      var content = cssWithMappingToString(item, useSourceMap);

      if (item[2]) {
        return "@media ".concat(item[2], " {").concat(content, "}");
      }

      return content;
    }).join('');
  }; // import a list of modules into the list
  // eslint-disable-next-line func-names


  list.i = function (modules, mediaQuery, dedupe) {
    if (typeof modules === 'string') {
      // eslint-disable-next-line no-param-reassign
      modules = [[null, modules, '']];
    }

    var alreadyImportedModules = {};

    if (dedupe) {
      for (var i = 0; i < this.length; i++) {
        // eslint-disable-next-line prefer-destructuring
        var id = this[i][0];

        if (id != null) {
          alreadyImportedModules[id] = true;
        }
      }
    }

    for (var _i = 0; _i < modules.length; _i++) {
      var item = [].concat(modules[_i]);

      if (dedupe && alreadyImportedModules[item[0]]) {
        // eslint-disable-next-line no-continue
        continue;
      }

      if (mediaQuery) {
        if (!item[2]) {
          item[2] = mediaQuery;
        } else {
          item[2] = "".concat(mediaQuery, " and ").concat(item[2]);
        }
      }

      list.push(item);
    }
  };

  return list;
};

function cssWithMappingToString(item, useSourceMap) {
  var content = item[1] || ''; // eslint-disable-next-line prefer-destructuring

  var cssMapping = item[3];

  if (!cssMapping) {
    return content;
  }

  if (useSourceMap && typeof btoa === 'function') {
    var sourceMapping = toComment(cssMapping);
    var sourceURLs = cssMapping.sources.map(function (source) {
      return "/*# sourceURL=".concat(cssMapping.sourceRoot || '').concat(source, " */");
    });
    return [content].concat(sourceURLs).concat([sourceMapping]).join('\n');
  }

  return [content].join('\n');
} // Adapted from convert-source-map (MIT)


function toComment(sourceMap) {
  // eslint-disable-next-line no-undef
  var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))));
  var data = "sourceMappingURL=data:application/json;charset=utf-8;base64,".concat(base64);
  return "/*# ".concat(data, " */");
}

/***/ }),

/***/ "./node_modules/css-loader/dist/runtime/getUrl.js":
/*!********************************************************!*\
  !*** ./node_modules/css-loader/dist/runtime/getUrl.js ***!
  \********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


module.exports = function (url, options) {
  if (!options) {
    // eslint-disable-next-line no-param-reassign
    options = {};
  } // eslint-disable-next-line no-underscore-dangle, no-param-reassign


  url = url && url.__esModule ? url.default : url;

  if (typeof url !== 'string') {
    return url;
  } // If url is already wrapped in quotes, remove them


  if (/^['"].*['"]$/.test(url)) {
    // eslint-disable-next-line no-param-reassign
    url = url.slice(1, -1);
  }

  if (options.hash) {
    // eslint-disable-next-line no-param-reassign
    url += options.hash;
  } // Should url be wrapped?
  // See https://drafts.csswg.org/css-values-3/#urls


  if (/["'() \t\n]/.test(url) || options.needQuotes) {
    return "\"".concat(url.replace(/"/g, '\\"').replace(/\n/g, '\\n'), "\"");
  }

  return url;
};

/***/ }),

/***/ "./node_modules/jexcel/dist/jexcel.css":
/*!*********************************************!*\
  !*** ./node_modules/jexcel/dist/jexcel.css ***!
  \*********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var api = __webpack_require__(/*! ../../style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
            var content = __webpack_require__(/*! !../../css-loader/dist/cjs.js!./jexcel.css */ "./node_modules/css-loader/dist/cjs.js!./node_modules/jexcel/dist/jexcel.css");

            content = content.__esModule ? content.default : content;

            if (typeof content === 'string') {
              content = [[module.i, content, '']];
            }

var options = {};

options.insert = "head";
options.singleton = false;

var update = api(content, options);



module.exports = content.locals || {};

/***/ }),

/***/ "./node_modules/jexcel/dist/jexcel.js":
/*!********************************************!*\
  !*** ./node_modules/jexcel/dist/jexcel.js ***!
  \********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {


/**
 * jExcel v3.9.1
 *
 * Author: Paul Hodel <paul.hodel@gmail.com>
 * Website: https://bossanova.uk/jexcel/
 * Description: Create amazing web based spreadsheets.
 *
 * This software is distribute under MIT License
 */

 if (! jSuites && "function" === 'function') {
    var jSuites = __webpack_require__(/*! jsuites */ "./node_modules/jsuites/dist/jsuites.js");
    __webpack_require__(/*! jsuites/dist/jsuites.css */ "./node_modules/jsuites/dist/jsuites.css");
}

;(function (global, factory) {
     true ? module.exports = factory() :
    undefined;
}(this, (function () {

    'use strict';

// Jexcel core object

var jexcel = (function(el, options) {
    // Create jexcel object
    var obj = {};
    obj.options = {};

    if (! (el instanceof Element || el instanceof HTMLDocument)) {
        console.error('JEXCEL: el is not a valid DOM element');
        return false;
    }

    // Loading default configuration
    var defaults = {
        // External data
        url:null,
        // Data
        data:null,
        // Copy behavior
        copyCompatibility:false,
        // Rows and columns definitions
        rows:[],
        columns:[],
        // Deprected legacy options
        colHeaders:[],
        colWidths:[],
        colAlignments:[],
        nestedHeaders:null,
        // Column width that is used by default
        defaultColWidth:50,
        defaultColAlign:'center',
        // Spare rows and columns
        minSpareRows:0,
        minSpareCols:0,
        // Minimal table dimensions
        minDimensions:[0,0],
        // Allow Export
        allowExport:true,
        // @type {boolean} - Include the header titles on download
        includeHeadersOnDownload:false,
        // Allow column sorting
        columnSorting:true,
        // Allow column dragging
        columnDrag:false,
        // Allow column resizing
        columnResize:true,
        // Allow row resizing
        rowResize:false,
        // Allow row dragging
        rowDrag:true,
        // Allow table edition
        editable:true,
        // Allow new rows
        allowInsertRow:true,
        // Allow new rows
        allowManualInsertRow:true,
        // Allow new columns
        allowInsertColumn:true,
        // Allow new rows
        allowManualInsertColumn:true,
        // Allow row delete
        allowDeleteRow:true,
        // Allow deleting of all rows
        allowDeletingAllRows:false,
        // Allow column delete
        allowDeleteColumn:true,
        // Allow rename column
        allowRenameColumn:true,
        // Allow comments
        allowComments:false,
        // Global wrap
        wordWrap:false,
        // Image options
        imageOptions: null,
        // CSV source
        csv:null,
        // Filename
        csvFileName:'jexcel',
        // Consider first line as header
        csvHeaders:true,
        // Delimiters
        csvDelimiter:',',
        // Disable corner selection
        selectionCopy:true,
        // Merged cells
        mergeCells:{},
        // Create toolbar
        toolbar:null,
        // Allow search
        search:false,
        // Create pagination
        pagination:false,
        paginationOptions:null,
        // Full screen
        fullscreen:false,
        // Lazy loading
        lazyLoading:false,
        loadingSpin:false,
        // Table overflow
        tableOverflow:false,
        tableHeight:'300px',
        tableWidth:null,
        // Meta
        meta: null,
        // Style
        style:null,
        // Execute formulas
        parseFormulas:true,
        autoIncrement:true,
        autoCasting:true,
        // Event handles
        onundo:null,
        onredo:null,
        onload:null,
        onchange:null,
        onbeforechange:null,
        onafterchanges:null,
        onbeforeinsertrow: null,
        oninsertrow:null,
        onbeforeinsertcolumn: null,
        oninsertcolumn:null,
        onbeforedeleterow:null,
        ondeleterow:null,
        onbeforedeletecolumn:null,
        ondeletecolumn:null,
        onmoverow:null,
        onmovecolumn:null,
        onresizerow:null,
        onresizecolumn:null,
        onsort:null,
        onselection:null,
        onpaste:null,
        onbeforepaste:null,
        onmerge:null,
        onfocus:null,
        onblur:null,
        onchangeheader:null,
        oneditionstart:null,
        oneditionend:null,
        onchangestyle:null,
        onchangemeta:null,
        onchangepage:null,
        // Customize any cell behavior
        updateTable:null,
        // Detach the HTML table when calling updateTable
        detachForUpdates: false,
        // Texts
        text:{
            noRecordsFound: 'No records found',
            showingPage: 'Showing page {0} of {1} entries',
            show: 'Show ',
            search: 'Search',
            entries: ' entries',
            columnName: 'Column name',
            insertANewColumnBefore: 'Insert a new column before',
            insertANewColumnAfter: 'Insert a new column after',
            deleteSelectedColumns: 'Delete selected columns',
            renameThisColumn: 'Rename this column',
            orderAscending: 'Order ascending',
            orderDescending: 'Order descending',
            insertANewRowBefore: 'Insert a new row before',
            insertANewRowAfter: 'Insert a new row after',
            deleteSelectedRows: 'Delete selected rows',
            editComments: 'Edit comments',
            addComments: 'Add comments',
            comments: 'Comments',
            clearComments: 'Clear comments',
            copy: 'Copy...',
            paste: 'Paste...',
            saveAs: 'Save as...',
            about: 'About',
            areYouSureToDeleteTheSelectedRows: 'Are you sure to delete the selected rows?',
            areYouSureToDeleteTheSelectedColumns: 'Are you sure to delete the selected columns?',
            thisActionWillDestroyAnyExistingMergedCellsAreYouSure: 'This action will destroy any existing merged cells. Are you sure?',
            thisActionWillClearYourSearchResultsAreYouSure: 'This action will clear your search results. Are you sure?',
            thereIsAConflictWithAnotherMergedCell: 'There is a conflict with another merged cell',
            invalidMergeProperties: 'Invalid merged properties',
            cellAlreadyMerged: 'Cell already merged',
            noCellsSelected: 'No cells selected',
        },
        // About message
        about:"jExcel CE Spreadsheet\nVersion 3.9.1\nAuthor: Paul Hodel <paul.hodel@gmail.com>\nWebsite: https://bossanova.uk/jexcel/v3",
    };

    // Loading initial configuration from user
    for (var property in defaults) {
        if (options && options.hasOwnProperty(property)) {
            if (property === 'text') {
                obj.options[property] = defaults[property];
                for (var textKey in options[property]) {
                    if (options[property].hasOwnProperty(textKey)){
                        obj.options[property][textKey] = options[property][textKey];
                    }
                }
            } else {
                obj.options[property] = options[property];
            }
        } else {
            obj.options[property] = defaults[property];
        }
    }

    // Global elements
    obj.el = el;
    obj.corner = null;
    obj.contextMenu = null;
    obj.textarea = null;
    obj.ads = null;
    obj.content = null;
    obj.table = null;
    obj.thead = null;
    obj.tbody = null;
    obj.rows = [];
    obj.results = null;
    obj.searchInput = null;
    obj.toolbar = null;
    obj.pagination = null;
    obj.pageNumber = null;
    obj.headerContainer = null;
    obj.colgroupContainer = null;

    // Containers
    obj.headers = [];
    obj.records = [];
    obj.history = [];
    obj.formula = [];
    obj.colgroup = [];
    obj.selection = [];
    obj.highlighted  = [];
    obj.selectedCell = null;
    obj.selectedContainer = null;
    obj.style = [];
    obj.data = null;

    // Internal controllers
    obj.cursor = null;
    obj.historyIndex = -1;
    obj.ignoreEvents = false;
    obj.ignoreHistory = false;
    obj.edition = null;
    obj.hashString = null;
    obj.resizing = null;
    obj.dragging = null;

    // Lazy loading
    if (obj.options.lazyLoading == true && (obj.options.tableOverflow == false && obj.options.fullscreen == false)) {
        console.error('JEXCEL: The lazyloading only works when tableOverflow = yes or fullscreen = yes');
        obj.options.lazyLoading = false;
    }
    
    /**
     * Activate/Disable fullscreen 
     * use programmatically : table.fullscreen(); or table.fullscreen(true); or table.fullscreen(false);
     * @Param {boolean} activate
     */
    obj.fullscreen = function(activate) {
        // If activate not defined, get reverse options.fullscreen
        if (activate == null) {
            activate = ! obj.options.fullscreen;
        }

        // If change
        if (obj.options.fullscreen != activate) {
            obj.options.fullscreen = activate;

            // Test LazyLoading conflict
            if (activate == true) {
                el.classList.add('fullscreen');
            } else {
                el.classList.remove('fullscreen');
            }
        } 
    }

    /**
     * Prepare the jexcel table
     * 
     * @Param config
     */
    obj.prepareTable = function() {
        // Loading initial data from remote sources
        var results = [];

        // Number of columns
        var size = obj.options.columns.length;

        if (obj.options.data && typeof(obj.options.data[0]) !== 'undefined') {
            // Data keys
            var keys = Object.keys(obj.options.data[0]);

            if (keys.length > size) {
                size = keys.length;
            }
        }

        // Minimal dimensions
        if (obj.options.minDimensions[0] > size) {
            size = obj.options.minDimensions[0];
        }

        // Requests
        var multiple = [];

        // Preparations
        for (var i = 0; i < size; i++) {
            // Deprected options. You should use only columns
            if (! obj.options.colHeaders[i]) {
                obj.options.colHeaders[i] = '';
            }
            if (! obj.options.colWidths[i]) {
                obj.options.colWidths[i] = obj.options.defaultColWidth;
            }
            if (! obj.options.colAlignments[i]) {
                obj.options.colAlignments[i] = obj.options.defaultColAlign;
            }

            // Default column description
            if (! obj.options.columns[i]) {
                obj.options.columns[i] = { type:'text' };
            } else if (! obj.options.columns[i].type) {
                obj.options.columns[i].type = 'text';
            }
            if (! obj.options.columns[i].name) {
                obj.options.columns[i].name = keys && keys[i] ? keys[i] : i;
            }
            if (! obj.options.columns[i].source) {
                obj.options.columns[i].source = [];
            }
            if (! obj.options.columns[i].options) {
                obj.options.columns[i].options = [];
            }
            if (! obj.options.columns[i].editor) {
                obj.options.columns[i].editor = null;
            }
            if (! obj.options.columns[i].allowEmpty) {
                obj.options.columns[i].allowEmpty = false;
            }
            if (! obj.options.columns[i].title) {
                obj.options.columns[i].title = obj.options.colHeaders[i] ? obj.options.colHeaders[i] : '';
            }
            if (! obj.options.columns[i].width) {
                obj.options.columns[i].width = obj.options.colWidths[i] ? obj.options.colWidths[i] : obj.options.defaultColWidth;
            }
            if (! obj.options.columns[i].align) {
                obj.options.columns[i].align = obj.options.colAlignments[i] ? obj.options.colAlignments[i] : 'center';
            }

            // Pre-load initial source for json autocomplete
            if (obj.options.columns[i].type == 'autocomplete' || obj.options.columns[i].type == 'dropdown') {
                // if remote content
                if (obj.options.columns[i].url) {
                    multiple.push(jSuites.ajax({
                        url: obj.options.columns[i].url,
                        index: i,
                        method: 'GET',
                        dataType: 'json',
                        multiple: multiple,
                        success: function(data) {
                            var source = [];
                            for (var i = 0; i < data.length; i++) {
                                obj.options.columns[this.index].source.push(data[i]);
                            }
                        },
                        complete: function() {
                            obj.createTable();
                        }
                    }));
                }
            } else if (obj.options.columns[i].type == 'calendar') {
                // Default format for date columns
                if (! obj.options.columns[i].options.format) {
                    obj.options.columns[i].options.format = 'DD/MM/YYYY';
                }
            }
        }

        // On complete
        if (! multiple.length) {
            obj.createTable();
        }
    }

    obj.createTable = function() {
        // Elements
        obj.table = document.createElement('table');
        obj.thead = document.createElement('thead');
        obj.tbody = document.createElement('tbody');

        // Create headers controllers
        obj.headers = [];
        obj.colgroup = [];

        // Create table container
        obj.content = document.createElement('div');
        obj.content.classList.add('jexcel_content');

        // Create toolbar object
        obj.toolbar = document.createElement('div');
        obj.toolbar.classList.add('jexcel_toolbar');

        // Search
        var searchContainer = document.createElement('div');
        var searchText = document.createTextNode((obj.options.text.search) + ': ');
        obj.searchInput = document.createElement('input');
        obj.searchInput.classList.add('jexcel_search');
        searchContainer.appendChild(searchText);
        searchContainer.appendChild(obj.searchInput);
        obj.searchInput.onfocus = function() {
            obj.resetSelection();
        }

        // Pagination select option
        var paginationUpdateContainer = document.createElement('div');

        if (obj.options.pagination > 0 && obj.options.paginationOptions && obj.options.paginationOptions.length > 0) {
            obj.paginationDropdown = document.createElement('select');
            obj.paginationDropdown.classList.add('jexcel_pagination_dropdown');
            obj.paginationDropdown.onchange = function() {
                obj.options.pagination = parseInt(this.value);
                obj.page(0);
            }

            for (var i = 0; i < obj.options.paginationOptions.length; i++) {
                var temp = document.createElement('option');
                temp.value = obj.options.paginationOptions[i];
                temp.innerHTML = obj.options.paginationOptions[i];
                obj.paginationDropdown.appendChild(temp);
            }

            paginationUpdateContainer.appendChild(document.createTextNode(obj.options.text.show));
            paginationUpdateContainer.appendChild(obj.paginationDropdown);
            paginationUpdateContainer.appendChild(document.createTextNode(obj.options.text.entries));
        }

        // Filter and pagination container
        obj.filter = document.createElement('div');
        obj.filter.classList.add('jexcel_filter');
        obj.filter.appendChild(paginationUpdateContainer);
        obj.filter.appendChild(searchContainer);

        // Colsgroup
        obj.colgroupContainer = document.createElement('colgroup');
        var tempCol = document.createElement('col');
        tempCol.setAttribute('width', '50');
        obj.colgroupContainer.appendChild(tempCol);

        // Nested
        if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0) {
            // Flexible way to handle nestedheaders
            if (obj.options.nestedHeaders[0] && obj.options.nestedHeaders[0][0]) {
                for (var j = 0; j < obj.options.nestedHeaders.length; j++) {
                    obj.thead.appendChild(obj.createNestedHeader(obj.options.nestedHeaders[j]));
                }
            } else {
                obj.thead.appendChild(obj.createNestedHeader(obj.options.nestedHeaders));
            }
        }

        // Row
        obj.headerContainer = document.createElement('tr');
        var tempCol = document.createElement('td');
        tempCol.classList.add('jexcel_selectall');
        obj.headerContainer.appendChild(tempCol);

        for (var i = 0; i < obj.options.columns.length; i++) {
            // Create header
            obj.createCellHeader(i);
            // Append cell to the container
            obj.headerContainer.appendChild(obj.headers[i]);
            obj.colgroupContainer.appendChild(obj.colgroup[i]);
        }

        obj.thead.appendChild(obj.headerContainer);

        // Content table
        obj.table = document.createElement('table');
        obj.table.classList.add('jexcel');
        obj.table.setAttribute('cellpadding', '0');
        obj.table.setAttribute('cellspacing', '0');
        obj.table.setAttribute('unselectable', 'yes');
        obj.table.setAttribute('onselectstart', 'return false');
        obj.table.appendChild(obj.colgroupContainer);
        obj.table.appendChild(obj.thead);
        obj.table.appendChild(obj.tbody);

        // Spreadsheet corner
        obj.corner = document.createElement('div');
        obj.corner.className = 'jexcel_corner';
        obj.corner.setAttribute('unselectable', 'on');
        obj.corner.setAttribute('onselectstart', 'return false');

        if (obj.options.selectionCopy == false) {
            obj.corner.style.display = 'none';
        }

        // Textarea helper
        obj.textarea = document.createElement('textarea');
        obj.textarea.className = 'jexcel_textarea';
        obj.textarea.id = 'jexcel_textarea';
        obj.textarea.tabIndex = '-1';

        // Contextmenu container
        obj.contextMenu = document.createElement('div');
        obj.contextMenu.className = 'jexcel_contextmenu';

        // Create element
        jSuites.contextmenu(obj.contextMenu, {
            onclick:function() {
                obj.contextMenu.contextmenu.close(false);
            }
        });

        // Powered by jExcel
        var ads = document.createElement('a');
        ads.setAttribute('href', 'https://bossanova.uk/jexcel/');
        obj.ads = document.createElement('div');
        obj.ads.className = 'jexcel_about';
        if (typeof(sessionStorage) !== "undefined" && ! sessionStorage.getItem('jexcel')) {
            sessionStorage.setItem('jexcel', true);
            var img = document.createElement('img');
            img.src = '//bossanova.uk/jexcel/logo.png';
            ads.appendChild(img);
        }
        var span = document.createElement('span');
        span.innerHTML = 'Jexcel spreadsheet';
        ads.appendChild(span);
        obj.ads.appendChild(ads);

        // Create table container TODO: frozen columns
        var container = document.createElement('div');
        container.classList.add('jexcel_table');

        // Pagination
        obj.pagination = document.createElement('div');
        obj.pagination.classList.add('jexcel_pagination');
        var paginationInfo = document.createElement('div');
        var paginationPages = document.createElement('div');
        obj.pagination.appendChild(paginationInfo);
        obj.pagination.appendChild(paginationPages);

        // Hide pagination if not in use
        if (! obj.options.pagination) {
            obj.pagination.style.display = 'none';
        }

        // Append containers to the table
        if (obj.options.search == true) {
            el.appendChild(obj.filter);
        }

        // Elements
        obj.content.appendChild(obj.table);
        obj.content.appendChild(obj.corner);
        obj.content.appendChild(obj.textarea);

        el.appendChild(obj.toolbar);
        el.appendChild(obj.content);
        el.appendChild(obj.pagination);
        el.appendChild(obj.contextMenu);
        el.appendChild(obj.ads);
        el.classList.add('jexcel_container');

        // Create toolbar
        if (obj.options.toolbar && obj.options.toolbar.length) {
            obj.createToolbar();
        }

        // Fullscreen
        if (obj.options.fullscreen == true) {
            el.classList.add('fullscreen');
        } else {
            // Overflow
            if (obj.options.tableOverflow == true) {
                if (obj.options.tableHeight) {
                    obj.content.style['overflow-y'] = 'auto';
                    obj.content.style.maxHeight = obj.options.tableHeight;
                }
                if (obj.options.tableWidth) {
                    obj.content.style['overflow-x'] = 'auto';
                    obj.content.style.width = obj.options.tableWidth;
                }
            }
        }

        // With toolbars
        if (obj.options.tableOverflow != true && obj.options.toolbar) {
            el.classList.add('with-toolbar');
        }

        // Actions
        if (obj.options.columnDrag == true) {
            obj.thead.classList.add('draggable');
        }
        if (obj.options.columnResize == true) {
            obj.thead.classList.add('resizable');
        }
        if (obj.options.rowDrag == true) {
            obj.tbody.classList.add('draggable');
        }
        if (obj.options.rowResize == true) {
            obj.tbody.classList.add('resizable');
        }

        // Load data
        obj.setData();

        // Style
        if (obj.options.style) {
            obj.setStyle(obj.options.style, null, null, 1, 1);
        }
    }

    /**
     * Refresh the data
     * 
     * @return void
     */
    obj.refresh = function() {
        if (obj.options.url) {
            // Loading
            if (obj.options.loadingSpin == true) {
                jSuites.loading.show();
            }

            jSuites.ajax({
                url: obj.options.url,
                method: 'GET',
                dataType: 'json',
                success: function(result) {
                    // Data
                    obj.options.data = (result.data) ? result.data : result;
                    // Prepare table
                    obj.setData();
                    // Hide spin
                    if (obj.options.loadingSpin == true) {
                        jSuites.loading.hide();
                    }
                }
            });
        } else {
            obj.setData();
        }
    }

    /**
     * Set data
     * 
     * @param array data In case no data is sent, default is reloaded
     * @return void
     */
    obj.setData = function(data) {
        // Update data
        if (data) {
            if (typeof(data) == 'string') {
                data = JSON.parse(data);
            }

            obj.options.data = data;
        }

        // Data
        if (! obj.options.data) {
            obj.options.data = [];
        }

        // Prepare data
        if (obj.options.data) {
            var data = [];
            for (var j = 0; j < obj.options.data.length; j++) {
                var row = [];
                for (var i = 0; i < obj.options.columns.length; i++) {
                    row[i] = obj.options.data[j][obj.options.columns[i].name];
                }
                data.push(row);
            }

            obj.options.data = data;
        }

        // Adjust minimal dimensions
        var j = 0;
        var i = 0;
        var size_i = obj.options.columns.length;
        var size_j = obj.options.data.length;
        var min_i = obj.options.minDimensions[0];
        var min_j = obj.options.minDimensions[1];
        var max_i = min_i > size_i ? min_i : size_i;
        var max_j = min_j > size_j ? min_j : size_j;

        for (j = 0; j < max_j; j++) {
            for (i = 0; i < max_i; i++) {
                if (obj.options.data[j] == undefined) {
                    obj.options.data[j] = [];
                }

                if (obj.options.data[j][i] == undefined) {
                    obj.options.data[j][i] = '';
                }
            }
        }

        // Reset containers
        obj.rows = [];
        obj.results = null;
        obj.records = [];
        obj.history = [];

        // Reset internal controllers
        obj.historyIndex = -1;

        // Reset data
        obj.tbody.innerHTML = '';

        // Lazy loading
        if (obj.options.lazyLoading == true) {
            // Load only 100 records
            var startNumber = 0
            var finalNumber = obj.options.data.length < 100 ? obj.options.data.length : 100;

            if (obj.options.pagination) {
                obj.options.pagination = false;
                console.error('JEXCEL: Pagination will be disable due the lazyLoading');
            }
        } else if (obj.options.pagination) {
            // Pagination
            if (! obj.pageNumber) {
                obj.pageNumber = 0;
            }
            var quantityPerPage = obj.options.pagination;
            startNumber = (obj.options.pagination * obj.pageNumber);
            finalNumber = (obj.options.pagination * obj.pageNumber) + obj.options.pagination;

            if (obj.options.data.length < finalNumber) {
                finalNumber = obj.options.data.length;
            }
        } else {
            var startNumber = 0;
            var finalNumber = obj.options.data.length;
        }

        // Append nodes to the HTML
        for (j = 0; j < obj.options.data.length; j++) {
            // Create row
            var tr = obj.createRow(j, obj.options.data[j]);
            // Append line to the table
            if (j >= startNumber && j < finalNumber) {
                obj.tbody.appendChild(tr);
            }
        }

        if (obj.options.lazyLoading == true) {
            // Do not create pagination with lazyloading activated
        } else if (obj.options.pagination) {
            obj.updatePagination();
        }

        // Merge cells
        if (obj.options.mergeCells) {
            var keys = Object.keys(obj.options.mergeCells);
            for (var i = 0; i < keys.length; i++) {
                var num = obj.options.mergeCells[keys[i]];
                obj.setMerge(keys[i], num[0], num[1], 1);
            }
        }

        // Updata table with custom configurations if applicable
        obj.updateTable();

        // Onload
        if (! obj.ignoreEvents) {
            if (typeof(obj.options.onload) == 'function') {
                obj.options.onload(el, obj);
            }
        }
    }

    /**
     * Get the whole table data
     * 
     * @param bool get highlighted cells only
     * @return array data
     */
        obj.getData = function(highlighted, dataOnly) {
        // Control vars
        var dataset = [];
        var px = 0;
        var py = 0;

        // Data type
        var dataType = dataOnly == true || obj.options.copyCompatibility == false ? true : false;

        // Column and row length
        var x = obj.options.columns.length
        var y = obj.options.data.length

        // Go through the columns to get the data
        for (var j = 0; j < y; j++) {
            px = 0;
            for (var i = 0; i < x; i++) {
                // Cell selected or fullset
                if (! highlighted || obj.records[j][i].classList.contains('highlight')) {
                    // Get value
                    if (! dataset[py]) {
                        dataset[py] = [];
                    }
                    if (! dataType) {
                        dataset[py][px] = obj.records[j][i].innerHTML;
                    } else {
                        dataset[py][px] = obj.options.data[j][i];
                    }
                    px++;
                }
            }
            if (px > 0) {
                py++;
            }
       }

       return dataset;
    }

    /**
     * Get the whole table data
     * 
     * @param integer row number
     * @return string value
     */
    obj.getJson = function(highlighted) {
        // Control vars
        var data = [];

        // Column and row length
        var x = obj.options.columns.length
        var y = obj.options.data.length

        // Go through the columns to get the data
        for (var j = 0; j < y; j++) {
            var row = null;
            for (var i = 0; i < x; i++) {
                if (! highlighted || obj.records[j][i].classList.contains('highlight')) {
                    if (row == null) {
                        row = {};
                    }
                    row[obj.options.columns[i].name] = obj.options.data[j][i];
                }
            }

            if (row != null) {
                data.push(row);
            }
       }

       return data;
    }

    /**
     * Get a row data by rowNumber
     */
    obj.getRowData = function(rowNumber) {
        return obj.options.data[rowNumber];
    }

    /**
     * Set a row data by rowNumber
     */
    obj.setRowData = function(rowNumber, data) {
        for (var i = 0; i < obj.headers.length; i++) {
            // Update cell
            var columnName = jexcel.getColumnNameFromId([ i, rowNumber ]);
            // Set value
            if (data[i] != null) {
                obj.setValue(columnName, data[i]);
            }
        }
    }

    /**
     * Get a column data by columnNumber
     */
    obj.getColumnData = function(columnNumber) {
        var dataset = [];
        // Go through the rows to get the data
        for (var j = 0; j < obj.options.data.length; j++) {
            dataset.push(obj.options.data[j][columnNumber]);
        }
        return dataset;
    }

    /**
     * Set a column data by colNumber
     */
    obj.setColumnData = function(colNumber, data) {
        for (var j = 0; j < obj.rows.length; j++) {
            // Update cell
            var columnName = jexcel.getColumnNameFromId([ colNumber, j ]);
            // Set value
            if (data[j] != null) {
                obj.setValue(columnName, data[j]);
            }
        }
    }

    /**
     * Create row
     */
    obj.createRow = function(j, data) {
        // Create container
        if (! obj.records[j]) {
            obj.records[j] = [];
        }
        // Default data
        if (! data) {
            var data = obj.options.data[j];
        }
        // New line of data to be append in the table
        obj.rows[j] = document.createElement('tr');
        obj.rows[j].setAttribute('data-y', j);
        // Definitions
        if (obj.options.rows[j]) {
            if (obj.options.rows[j].height) {
                obj.rows[j].style.height = obj.options.rows[j].height;
            }
        }
        // Row number label
        var td = document.createElement('td');
        td.innerHTML = parseInt(j + 1);
        td.setAttribute('data-y', j);
        td.className = 'jexcel_row';
        obj.rows[j].appendChild(td);

        // Data columns
        for (i = 0; i < obj.options.columns.length; i++) {
            // New column of data to be append in the line
            obj.records[j][i] = obj.createCell(i, j, data[i]);
            // Add column to the row
            obj.rows[j].appendChild(obj.records[j][i]);
        }

        // Add row to the table body
        return obj.rows[j];
    }

    /**
     * Create cell
     */
    obj.createCell = function(i, j, value) {
        // Create cell and properties
        var td = document.createElement('td');
        td.setAttribute('data-x', i);
        td.setAttribute('data-y', j);
        // Hidden column
        if (obj.options.columns[i].type == 'hidden') {
            td.style.display = 'none';
            td.innerHTML = value;
        } else if (obj.options.columns[i].type == 'checkbox' || obj.options.columns[i].type == 'radio') {
            // Create input
            var element = document.createElement('input');
            element.type = obj.options.columns[i].type;
            element.name = 'c' + i;
            element.checked = (value == 1 || value == true || value == 'true') ? true : false;
            element.onclick = function() {
                obj.setValue(td, this.checked);
            }

            if (obj.options.columns[i].readOnly == true) {
                element.setAttribute('disabled', 'disabled');
            }

            // Append to the table
            td.appendChild(element);
            // Make sure the values are correct
            obj.options.data[j][i] = element.checked;
        } else if (obj.options.columns[i].type == 'calendar') {
            // Try formatted date
            var formatted = jSuites.calendar.extractDateFromString(value, obj.options.columns[i].options.format);
            // Create calendar cell
            td.innerHTML = jSuites.calendar.getDateString(formatted ? formatted : value, obj.options.columns[i].options.format);
        } else if (obj.options.columns[i].type == 'dropdown' || obj.options.columns[i].type == 'autocomplete') {
            // Create dropdown cell
            td.classList.add('jexcel_dropdown');
            td.innerHTML = obj.getDropDownValue(i, value);
        } else if (obj.options.columns[i].type == 'color') {
            if (obj.options.columns[i].render == 'square') {
                var color = document.createElement('div');
                color.className = 'color';
                color.style.backgroundColor = value;
                td.appendChild(color);
            } else {
                td.style.color = value;
                td.innerHTML = value;
            }
        } else if (obj.options.columns[i].type == 'image') {
            if (value && value.substr(0, 10) == 'data:image') {
                var img = document.createElement('img');
                img.src = value;
                td.appendChild(img);
            }
        } else {
            if ((''+value).substr(0,1) == '=' && obj.options.parseFormulas == true) {
                value = obj.executeFormula(value, i, j)
            }
            if (obj.options.columns[i].mask) {
                var decimal = obj.options.columns[i].decimal || '.';
                value = '' + jSuites.mask.run(value, obj.options.columns[i].mask, decimal);
            }

            td.innerHTML = value;
        }

        // Readonly
        if (obj.options.columns[i].readOnly == true) {
            td.className = 'readonly';
        }

        // Text align
        var colAlign = obj.options.columns[i].align ? obj.options.columns[i].align : 'center';
        td.style.textAlign = colAlign;

        // Wrap option
        if (obj.options.columns[i].wordWrap != false && (obj.options.wordWrap == true || obj.options.columns[i].wordWrap == true || td.innerHTML.length > 200)) {
            td.style.whiteSpace = 'pre-wrap';
        }

        // Overflow
        if (i > 0) {
            if (value || td.innerHTML) {
                obj.records[j][i-1].style.overflow = 'hidden';
            } else {
                if (i == obj.options.columns.length - 1) {
                    td.style.overflow = 'hidden';
                }
            }
        }

        return td;
    }

    obj.createCellHeader = function(colNumber) {
        // Create col global control
        var colWidth = obj.options.columns[colNumber].width ? obj.options.columns[colNumber].width : obj.options.defaultColWidth;
        var colAlign = obj.options.columns[colNumber].align ? obj.options.columns[colNumber].align : obj.options.defaultColAlign;

        // Create header cell
        obj.headers[colNumber] = document.createElement('td');
        obj.headers[colNumber].innerHTML = obj.options.columns[colNumber].title ? obj.options.columns[colNumber].title : jexcel.getColumnName(colNumber);
        obj.headers[colNumber].setAttribute('data-x', colNumber);
        obj.headers[colNumber].style.textAlign = colAlign;
        if (obj.options.columns[colNumber].title) {
            obj.headers[colNumber].setAttribute('title', obj.options.columns[colNumber].title);
        }

        // Width control
        obj.colgroup[colNumber] = document.createElement('col');
        obj.colgroup[colNumber].setAttribute('width', colWidth);

        // Hidden column
        if (obj.options.columns[colNumber].type == 'hidden') {
            obj.headers[colNumber].style.display = 'none';
            obj.colgroup[colNumber].style.display = 'none';
        }
    }

    obj.createNestedHeader = function(nestedInformation) {
        var tr = document.createElement('tr');
        tr.classList.add('jexcel_nested');
        var td = document.createElement('td');
        tr.appendChild(td);

        var headerIndex = 0;
        for (var i = 0; i < nestedInformation.length; i++) {
            // Default values
            if (! nestedInformation[i].colspan) {
                nestedInformation[i].colspan = 1;
            }
            if (! nestedInformation[i].align) {
                nestedInformation[i].align = 'center';
            }
            if (! nestedInformation[i].title) {
                nestedInformation[i].title = '';
            }

            // Number of columns
            var numberOfColumns = nestedInformation[i].colspan;

            // Classes container
            var column = [];
            // Header classes for this cell
            for (var x = 0; x < numberOfColumns; x++) {
                if (obj.options.columns[headerIndex] && obj.options.columns[headerIndex].type == 'hidden') {
                    numberOfColumns++;
                }
                column.push(headerIndex);
                headerIndex++;
            }

            // Created the nested cell
            var td = document.createElement('td');
            td.setAttribute('data-column', column.join(','));
            td.setAttribute('colspan', nestedInformation[i].colspan);
            td.setAttribute('align', nestedInformation[i].align);
            td.innerHTML = nestedInformation[i].title;
            tr.appendChild(td);
        }

        return tr;
    }

    /**
     * Create toolbar
     */
    obj.createToolbar = function(toolbar) {
        if (toolbar) {
            obj.options.toolbar = toolbar;
        } else {
            var toolbar = obj.options.toolbar;
        }

        for (var i = 0; i < toolbar.length; i++) {
            if (toolbar[i].type == 'i') {
                var toolbarItem = document.createElement('i');
                toolbarItem.classList.add('jexcel_toolbar_item');
                toolbarItem.classList.add('material-icons');
                toolbarItem.setAttribute('data-k', toolbar[i].k);
                toolbarItem.setAttribute('data-v', toolbar[i].v);
                // Tooltip
                if (toolbar[i].tooltip) {
                    toolbarItem.setAttribute('title', toolbar[i].tooltip);
                }
                // Handle click
                if (toolbar[i].onclick && typeof(toolbar[i].onclick)) {
                    toolbarItem.onclick = toolbar[i].onclick;
                } else {
                    toolbarItem.onclick = function() {
                        var k = this.getAttribute('data-k');
                        var v = this.getAttribute('data-v');
                        obj.setStyle(obj.highlighted, k, v);
                    }
                }
                // Append element
                toolbarItem.innerHTML = toolbar[i].content;
                obj.toolbar.appendChild(toolbarItem);
            } else if (toolbar[i].type == 'select') {
               var toolbarItem = document.createElement('select');
               toolbarItem.classList.add('jexcel_toolbar_item');
               toolbarItem.setAttribute('data-k', toolbar[i].k);
               // Tooltip
               if (toolbar[i].tooltip) {
                   toolbarItem.setAttribute('title', toolbar[i].tooltip);
               }
               // Handle onchange
               if (toolbar[i].onchange && typeof(toolbar[i].onchange)) {
                   toolbarItem.onchange = toolbar[i].onchange;
               } else {
                   toolbarItem.onchange = function() {
                       var k = this.getAttribute('data-k');
                       obj.setStyle(obj.highlighted, k, this.value);
                   }
               }
               // Add options to the dropdown
               for(var j = 0; j < toolbar[i].v.length; j++) {
                    var toolbarDropdownOption = document.createElement('option');
                    toolbarDropdownOption.value = toolbar[i].v[j];
                    toolbarDropdownOption.innerHTML = toolbar[i].v[j];
                    toolbarItem.appendChild(toolbarDropdownOption);
               }
               obj.toolbar.appendChild(toolbarItem);
            } else if (toolbar[i].type == 'color') {
                 var toolbarItem = document.createElement('i');
                 toolbarItem.classList.add('jexcel_toolbar_item');
                 toolbarItem.classList.add('material-icons');
                 toolbarItem.setAttribute('data-k', toolbar[i].k);
                 toolbarItem.setAttribute('data-v', '');
                 // Tooltip
                 if (toolbar[i].tooltip) {
                     toolbarItem.setAttribute('title', toolbar[i].tooltip);
                 }
                 obj.toolbar.appendChild(toolbarItem);
                 toolbarItem.onclick = function() {
                     this.color.open();
                 }
                 toolbarItem.innerHTML = toolbar[i].content;
                 jSuites.color(toolbarItem, {
                     onchange:function(o, v) {
                         var k = o.getAttribute('data-k');
                         obj.setStyle(obj.highlighted, k, v);
                     }
                 });
            }
        }
    }

    /**
     * Merge cells
     * @param cellName
     * @param colspan
     * @param rowspan
     * @param ignoreHistoryAndEvents
     */
    obj.setMerge = function(cellName, colspan, rowspan, ignoreHistoryAndEvents) {
        var test = false;

        if (! cellName) {
            if (! obj.highlighted.length) {
                alert(obj.options.text.noCellsSelected);
                return null;
            } else {
                var x1 = parseInt(obj.highlighted[0].getAttribute('data-x'));
                var y1 = parseInt(obj.highlighted[0].getAttribute('data-y'));
                var x2 = parseInt(obj.highlighted[obj.highlighted.length-1].getAttribute('data-x'));
                var y2 = parseInt(obj.highlighted[obj.highlighted.length-1].getAttribute('data-y'));
                var cellName = jexcel.getColumnNameFromId([ x1, y1 ]);
                var colspan = (x2 - x1) + 1;
                var rowspan = (y2 - y1) + 1;
            }
        }

        var cell = jexcel.getIdFromColumnName(cellName, true);

        if (obj.options.mergeCells[cellName]) {
            if (obj.records[cell[1]][cell[0]].getAttribute('data-merged')) {
                test = obj.options.text.cellAlreadyMerged;
            }
        } else if ((! colspan || colspan < 2) && (! rowspan || rowspan < 2)) {
            test = obj.options.text.invalidMergeProperties;
        } else {
            var cells = [];
            for (var j = cell[1]; j < cell[1] + rowspan; j++) {
                for (var i = cell[0]; i < cell[0] + colspan; i++) {
                    var columnName = jexcel.getColumnNameFromId([i, j]);
                    if (obj.records[j][i].getAttribute('data-merged')) {
                        test = obj.options.text.thereIsAConflictWithAnotherMergedCell;
                    }
                }
            }
        }

        if (test) {
            alert(test);
        } else {
            // Add property
            if (colspan > 1) {
                obj.records[cell[1]][cell[0]].setAttribute('colspan', colspan);
            } else {
                colspan = 1;
            }
            if (rowspan > 1) {
                obj.records[cell[1]][cell[0]].setAttribute('rowspan', rowspan);
            } else {
                rowspan = 1;
            }
            // Keep links to the existing nodes
            obj.options.mergeCells[cellName] = [ colspan, rowspan, [] ];
            // Mark cell as merged
            obj.records[cell[1]][cell[0]].setAttribute('data-merged', 'true');
            // Overflow
            obj.records[cell[1]][cell[0]].style.overflow = 'hidden';
            // History data
            var data = [];
            // Adjust the nodes
            for (var y = cell[1]; y < cell[1] + rowspan; y++) {
                for (var x = cell[0]; x < cell[0] + colspan; x++) {
                    if (! (cell[0] == x && cell[1] == y)) {
                        data.push(obj.options.data[y][x]);
                        obj.updateCell(x, y, '', true);
                        obj.options.mergeCells[cellName][2].push(obj.records[y][x]);
                        obj.records[y][x].style.display = 'none';
                        obj.records[y][x] = obj.records[cell[1]][cell[0]];
                    }
                }
            }
            // In the initialization is not necessary keep the history
            obj.updateSelection(obj.records[cell[1]][cell[0]]);

            if (! ignoreHistoryAndEvents) {
                obj.setHistory({
                    action:'setMerge',
                    column:cellName,
                    colspan:colspan,
                    rowspan:rowspan,
                    data:data,
                });

                if (typeof(obj.options.onmerge) == 'function') {
                    obj.options.onmerge(el, cellName, colspan, rowspan);
                }
            }
        }
    }

    /**
     * Merge cells
     * @param cellName
     * @param colspan
     * @param rowspan
     * @param ignoreHistoryAndEvents
     */
    obj.getMerge = function(cellName) {
        var data = {};
        if (cellName) {
            if (obj.options.mergeCells[cellName]) {
                data = [ obj.options.mergeCells[cellName][0], obj.options.mergeCells[cellName][1] ];
            } else {
                data = null;
            }
        } else {
            if (obj.options.mergeCells) {
                var mergedCells = obj.options.mergeCells;
                var keys = Object.keys(obj.options.mergeCells);
                for (var i = 0; i < keys.length; i++) {
                    data[keys[i]] = [ obj.options.mergeCells[keys[i]][0], obj.options.mergeCells[keys[i]][1] ];
                }
            }
        }

        return data;
    }

    /**
     * Remove merge by cellname
     * @param cellName
     */
    obj.removeMerge = function(cellName, data, keepOptions) {
        if (obj.options.mergeCells[cellName]) {
            var cell = jexcel.getIdFromColumnName(cellName, true);
            obj.records[cell[1]][cell[0]].removeAttribute('colspan');
            obj.records[cell[1]][cell[0]].removeAttribute('rowspan');
            obj.records[cell[1]][cell[0]].removeAttribute('data-merged');
            var info = obj.options.mergeCells[cellName];

            var index = 0;
            for (var j = 0; j < info[1]; j++) {
                for (var i = 0; i < info[0]; i++) {
                    if (j > 0 || i > 0) {
                        obj.records[cell[1]+j][cell[0]+i] = info[2][index];
                        obj.records[cell[1]+j][cell[0]+i].style.display = '';
                        // Recover data
                        if (data && data[index]) {
                            obj.updateCell(cell[0]+i, cell[1]+j, data[index]);
                        }
                        index++;
                    }
                }
            }

            // Update selection
            obj.updateSelection(obj.records[cell[1]][cell[0]], obj.records[cell[1]+j-1][cell[0]+i-1]);

            if (! keepOptions) {
                delete(obj.options.mergeCells[cellName]);
            }
        }
    }

    /**
     * Remove all merged cells
     */
    obj.destroyMerged = function(keepOptions) {
        // Remove any merged cells
        if (obj.options.mergeCells) {
            var mergedCells = obj.options.mergeCells;
            var keys = Object.keys(obj.options.mergeCells);
            for (var i = 0; i < keys.length; i++) {
                obj.removeMerge(keys[i], null, keepOptions);
            }
        }
    }

    /**
     * Is column merged
     */
    obj.isColMerged = function(x, insertBefore) {
        var cols = [];
        // Remove any merged cells
        if (obj.options.mergeCells) {
            var keys = Object.keys(obj.options.mergeCells);
            for (var i = 0; i < keys.length; i++) {
                var info = jexcel.getIdFromColumnName(keys[i], true);
                var colspan = obj.options.mergeCells[keys[i]][0];
                var x1 = info[0];
                var x2 = info[0] + (colspan > 1 ? colspan - 1 : 0);

                if (insertBefore == null) {
                    if ((x1 <= x && x2 >= x)) {
                        cols.push(keys[i]);
                    }
                } else {
                    if (insertBefore) {
                        if ((x1 < x && x2 >= x)) {
                            cols.push(keys[i]);
                        }
                    } else {
                        if ((x1 <= x && x2 > x)) {
                            cols.push(keys[i]);
                        }
                    }
                }
            }
        }

        return cols;
    }

    /**
     * Is rows merged
     */
    obj.isRowMerged = function(y, insertBefore) {
        var rows = [];
        // Remove any merged cells
        if (obj.options.mergeCells) {
            var keys = Object.keys(obj.options.mergeCells);
            for (var i = 0; i < keys.length; i++) {
                var info = jexcel.getIdFromColumnName(keys[i], true);
                var rowspan = obj.options.mergeCells[keys[i]][1];
                var y1 = info[1];
                var y2 = info[1] + (rowspan > 1 ? rowspan - 1 : 0);

                if (insertBefore == null) {
                    if ((y1 <= y && y2 >= y)) {
                        rows.push(keys[i]);
                    }
                } else {
                    if (insertBefore) {
                        if ((y1 < y && y2 >= y)) {
                            rows.push(keys[i]);
                        }
                    } else {
                        if ((y1 <= y && y2 > y)) {
                            rows.push(keys[i]);
                        }
                    }
                }
            }
        }

        return rows;
    }

    /**
     * Open the editor
     * 
     * @param object cell
     * @return void
     */
    obj.openEditor = function(cell, empty, e) {
        // Get cell position
        var y = cell.getAttribute('data-y');
        var x = cell.getAttribute('data-x');

        // On edition start
        if (! obj.ignoreEvents) {
            if (typeof(obj.options.oneditionstart) == 'function') {
                obj.options.oneditionstart(el, cell, x, y);
            }
        }

        // Overflow
        if (x > 0) {
            obj.records[y][x-1].style.overflow = 'hidden';
        }

        // Create editor
        var createEditor = function(type) {
            // Cell information
            var info = cell.getBoundingClientRect();

            // Create dropdown
            var editor = document.createElement(type);
            editor.style.width = (info.width) + 'px';
            editor.style.height = (info.height - 2) + 'px';
            editor.style.minHeight = (info.height - 2) + 'px';

            // Edit cell
            cell.classList.add('editor');
            cell.innerHTML = '';
            cell.appendChild(editor);

            return editor;
        }

        // Readonly
        if (cell.classList.contains('readonly') == true) {
            // Do nothing
        } else {
            // Holder
            obj.edition = [ obj.records[y][x], obj.records[y][x].innerHTML, x, y ];

            // If there is a custom editor for it
            if (obj.options.columns[x].editor) {
                // Custom editors
                obj.options.columns[x].editor.openEditor(cell, el, empty, e);
            } else {
                // Native functions
                if (obj.options.columns[x].type == 'hidden') {
                    // Do nothing
                } else if (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio') {
                    // Get value
                    var value = cell.children[0].checked ? false : true;
                    // Toogle value
                    obj.setValue(cell, value);
                    // Do not keep edition open
                    obj.edition = null;
                } else if (obj.options.columns[x].type == 'dropdown' || obj.options.columns[x].type == 'autocomplete') {
                    // Get current value
                    var value = obj.options.data[y][x];

                    // Create dropdown
                    if (typeof(obj.options.columns[x].filter) == 'function') {
                        var source = obj.options.columns[x].filter(el, cell, x, y, obj.options.columns[x].source);
                    } else {
                        var source = obj.options.columns[x].source;
                    }

                    // Create editor
                    var editor = createEditor('div');
                    var options = {
                        data: source,
                        multiple: obj.options.columns[x].multiple ? true : false,
                        autocomplete: obj.options.columns[x].autocomplete || obj.options.columns[x].type == 'autocomplete' ? true : false,
                        opened:true,
                        value: obj.options.columns[x].multiple ? value.split(';') : value,
                        width:'100%',
                        height:editor.style.minHeight,
                        position: (obj.options.tableOverflow == true || obj.options.fullscreen == true) ? true : false,
                        onclose:function() {
                            obj.closeEditor(cell, true);
                        }
                    };
                    if (obj.options.columns[x].options && obj.options.columns[x].options.type) {
                        options.type = obj.options.columns[x].options.type;
                    }
                    jSuites.dropdown(editor, options);
                } else if (obj.options.columns[x].type == 'calendar' || obj.options.columns[x].type == 'color') {
                    // Value
                    var value = obj.options.data[y][x];
                    // Create editor
                    var editor = createEditor('input');
                    editor.value = value;

                    if (obj.options.tableOverflow == true || obj.options.fullscreen == true) {
                        obj.options.columns[x].options.position = true;
                    }
                    obj.options.columns[x].options.value = obj.options.data[y][x];
                    obj.options.columns[x].options.opened = true;
                    obj.options.columns[x].options.onclose = function(el, value) {
                        obj.closeEditor(cell, true);
                    }
                    // Current value
                    if (obj.options.columns[x].type == 'color') {
                        jSuites.color(editor, obj.options.columns[x].options);
                    } else {
                        var calendar = jSuites.calendar(editor, obj.options.columns[x].options);
                        calendar.setValue(value);
                    }
                    // Focus on editor
                    editor.focus();
                } else if (obj.options.columns[x].type == 'image') {
                    // Value
                    var img = cell.children[0];
                    // Create editor
                    var editor = createEditor('div');
                    editor.style.position = 'relative';
                    var div = document.createElement('div');
                    div.classList.add('jclose');
                    if (img && img.src) {
                        div.appendChild(img);
                    }
                    editor.appendChild(div);
                    jSuites.image(div, obj.options.imageOptions);
                    const rect = cell.getBoundingClientRect();
                    const rectContent = div.getBoundingClientRect();
                    if (window.innerHeight < rect.bottom + rectContent.height) {
                        div.style.top = (rect.top - (rectContent.height + 2)) + 'px';
                    } else {
                        div.style.top = (rect.top) + 'px';
                    }
                } else {
                    // Value
                    var value = empty == true ? '' : obj.options.data[y][x];

                    // Basic editor
                    if (obj.options.columns[x].wordWrap != false && (obj.options.wordWrap == true || obj.options.columns[x].wordWrap == true)) {
                        var editor = createEditor('textarea');
                    } else {
                        var editor = createEditor('input');
                        // Mask
                        if (obj.options.columns[x].mask) {
                            editor.setAttribute('data-mask', obj.options.columns[x].mask);
                        }
                    }

                    editor.value = value;
                    editor.onblur = function() {
                        obj.closeEditor(cell, true);
                    };
                    editor.focus();
                }
            }
        }
    }

    /**
     * Close the editor and save the information
     * 
     * @param object cell
     * @param boolean save
     * @return void
     */
    obj.closeEditor = function(cell, save) {
        var x = parseInt(cell.getAttribute('data-x'));
        var y = parseInt(cell.getAttribute('data-y'));

        // Get cell properties
        if (save == true) {
            // If custom editor
            if (obj.options.columns[x].editor) {
                // Custom editor
                var value = obj.options.columns[x].editor.closeEditor(cell, save);
            } else {
                // Native functions
                if (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio' || obj.options.columns[x].type == 'hidden') {
                    // Do nothing
                } else if (obj.options.columns[x].type == 'dropdown' || obj.options.columns[x].type == 'autocomplete') {
                    var value = cell.children[0].dropdown.close(true);
                } else if (obj.options.columns[x].type == 'calendar') {
                    var value = cell.children[0].calendar.close(true);
                } else if (obj.options.columns[x].type == 'color') {
                    var value = cell.children[1].color.close(true);
                } else if (obj.options.columns[x].type == 'image') {
                    var img = cell.children[0].children[0].children[0];
                    var value = img && img.tagName == 'IMG' ? img.src : '';
                } else if (obj.options.columns[x].type == 'numeric') {
                    var value = cell.children[0].value;
                    if (value.substr(0,1) != '=') {
                        if (value == '') {
                            value = obj.options.columns[x].allowEmpty ? '' : 0;
                        }
                    }
                    cell.children[0].onblur = null;
                } else {
                    var value = cell.children[0].value;
                    cell.children[0].onblur = null;
                }
            }

            // Ignore changes if the value is the same
            if (obj.options.data[y][x] == value) {
                cell.innerHTML = obj.edition[1];
            } else {
                obj.setValue(cell, value);
            }
        } else {
            if (obj.options.columns[x].editor) {
                // Custom editor
                obj.options.columns[x].editor.closeEditor(cell, save);
            } else {
                if (obj.options.columns[x].type == 'dropdown' || obj.options.columns[x].type == 'autocomplete') {
                    cell.children[0].dropdown.close(true);
                } else if (obj.options.columns[x].type == 'calendar') {
                    cell.children[0].calendar.close(true);
                } else if (obj.options.columns[x].type == 'color') {
                    cell.children[1].color.close(true);
                } else {
                    cell.children[0].onblur = null;
                }
            }

            // Restore value
            cell.innerHTML = obj.edition && obj.edition[1] ? obj.edition[1] : '';
        }

        // On edition end
        if (! obj.ignoreEvents) {
            if (typeof(obj.options.oneditionend) == 'function') {
                obj.options.oneditionend(el, cell, x, y, value, save);
            }
        }

        // Remove editor class
        cell.classList.remove('editor');

        // Finish edition
        obj.edition = null;
    }

    /**
     * Get the cell object
     * 
     * @param object cell
     * @return string value
     */
    obj.getCell = function(cell) {
        // Convert in case name is excel liked ex. A10, BB92
        cell = jexcel.getIdFromColumnName(cell, true);
        var x = cell[0];
        var y = cell[1];

        return obj.records[y][x];
    }

    /**
     * Get the cell object from coords
     * 
     * @param object cell
     * @return string value
     */
    obj.getCellFromCoords = function(x, y) {
        return obj.records[y][x];
    }

    /**
     * Get label
     * 
     * @param object cell
     * @return string value
     */
    obj.getLabel = function(cell) {
        // Convert in case name is excel liked ex. A10, BB92
        cell = jexcel.getIdFromColumnName(cell, true);
        var x = cell[0];
        var y = cell[1];

        return obj.records[y][x].innerHTML;
    }

    /**
     * Get labelfrom coords
     * 
     * @param object cell
     * @return string value
     */
    obj.getLabelFromCoords = function(x, y) {
        return obj.records[y][x].innerHTML;
    }

    /**
     * Get the value from a cell
     * 
     * @param object cell
     * @return string value
     */
    obj.getValue = function(cell, processedValue) {
        if (typeof(cell) == 'object') {
            var x = cell.getAttribute('data-x');
            var y = cell.getAttribute('data-y');
        } else {
            cell = jexcel.getIdFromColumnName(cell, true);
            var x = cell[0];
            var y = cell[1];
        }

        var value = null;

        if (x != null && y != null) {
            if (obj.records[y] && obj.records[y][x] && (processedValue || obj.options.copyCompatibility == true)) {
                value = obj.records[y][x].innerHTML;
            } else {
                if (obj.options.data[y] && obj.options.data[y][x] != 'undefined') {
                    value = obj.options.data[y][x];
                }
            }
        }

        return value;
    }

    /**
     * Get the value from a coords
     * 
     * @param int x
     * @param int y
     * @return string value
     */
    obj.getValueFromCoords = function(x, y, processedValue) {
        var value = null;

        if (x != null && y != null) {
            if ((obj.records[y] && obj.records[y][x]) && processedValue || obj.options.copyCompatibility == true) {
                value = obj.records[y][x].innerHTML;
            } else {
                if (obj.options.data[y] && obj.options.data[y][x] != 'undefined') {
                    value = obj.options.data[y][x];
                }
            }
        }

        return value;
    }

    /**
     * Set a cell value
     * 
     * @param mixed cell destination cell
     * @param string value value
     * @return void
     */
    obj.setValue = function(cell, value, force) {
        var records = [];

        if (typeof(cell) == 'string') {
            var columnId = jexcel.getIdFromColumnName(cell, true);
            var x = columnId[0];
            var y = columnId[1];

            // Update cell
            records.push(obj.updateCell(x, y, value, force));

            // Update all formulas in the chain
            obj.updateFormulaChain(x, y, records);
        } else {
            var x = null;
            var y = null;
            if (cell && cell.getAttribute) {
                var x = cell.getAttribute('data-x');
                var y = cell.getAttribute('data-y');
            }

            // Update cell
            if (x != null && y != null) {
                records.push(obj.updateCell(x, y, value, force));

                // Update all formulas in the chain
                obj.updateFormulaChain(x, y, records);
            } else {
                var keys = Object.keys(cell);
                if (keys.length > 0) {
                    for (var i = 0; i < keys.length; i++) {
                        if (typeof(cell[i]) == 'string') {
                            var columnId = jexcel.getIdFromColumnName(cell[i], true);
                            var x = columnId[0];
                            var y = columnId[1];
                        } else {
                            var x = cell[i].getAttribute('data-x');
                            var y = cell[i].getAttribute('data-y');
                        }

                         // Update cell
                        if (x != null && y != null) {
                            records.push(obj.updateCell(x, y, value, force));

                            // Update all formulas in the chain
                            obj.updateFormulaChain(x, y, records);
                        }
                    }
                }
            }
        }

        // Update history
        obj.setHistory({
            action:'setValue',
            records:records,
            selection:obj.selectedCell,
        });

        // Update table with custom configurations if applicable
        obj.updateTable();

        // On after changes
        obj.onafterchanges(el, records);
    }

    /**
     * Set a cell value based on coordinates
     * 
     * @param int x destination cell
     * @param int y destination cell
     * @param string value
     * @return void
     */
    obj.setValueFromCoords = function(x, y, value, force) {
        var records = [];
        records.push(obj.updateCell(x, y, value, force));

        // Update all formulas in the chain
        obj.updateFormulaChain(x, y, records);

        // Update history
        obj.setHistory({
            action:'setValue',
            records:records,
            selection:obj.selectedCell,
        });

        // Update table with custom configurations if applicable
        obj.updateTable();

        // On after changes
        obj.onafterchanges(el, records);
    }

    /**
     * Toogle
     */
    obj.setCheckRadioValue = function() {
        var records = [];
        var keys = Object.keys(obj.highlighted);
        for (var i = 0; i < keys.length; i++) {
            var x = obj.highlighted[i].getAttribute('data-x');
            var y = obj.highlighted[i].getAttribute('data-y');

            if (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio') {
                // Update cell
                records.push(obj.updateCell(x, y, ! obj.options.data[y][x]));
            }
        }

        if (records.length) {
            // Update history
            obj.setHistory({
                action:'setValue',
                records:records,
                selection:obj.selectedCell,
            });

            // On after changes
            obj.onafterchanges(el, records);
        }
    }

    /**
     * Update cell content
     * 
     * @param object cell
     * @return void
     */
    obj.updateCell = function(x, y, value, force) {
        // Changing value depending on the column type
        if (obj.records[y][x].classList.contains('readonly') == true && ! force) {
            // Do nothing
            var record = {
                x: x,
                y: y,
                col: x,
                row: y
            }
        } else {
            // On change
            if (! obj.ignoreEvents) {
                if (typeof(obj.options.onbeforechange) == 'function') {
                    // Overwrite a value
                    var val = obj.options.onbeforechange(el, obj.records[y][x], x, y, value);

                    // If you return something this will overwrite the value
                    if (val != undefined) {
                        value = val;
                    }
                }
            }

            // History format
            var record = {
                x: x,
                y: y,
                col: x,
                row: y,
                newValue: value,
                oldValue: obj.options.data[y][x],
            }

            if (obj.options.columns[x].editor) {
                // Update data and cell
                obj.options.data[y][x] = value;
                obj.options.columns[x].editor.setValue(obj.records[y][x], value, force);
            } else {
                // Native functions
                if (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio') {
                    // Unchecked all options
                    if (obj.options.columns[x].type == 'radio') {
                        for (var j = 0; j < obj.options.data.length; j++) {
                            obj.options.data[j][x] = false;
                        }
                    }

                    // Update data and cell
                    obj.records[y][x].children[0].checked = (value == 1 || value == true || value == 'true' || value == 'TRUE') ? true : false;
                    obj.options.data[y][x] = obj.records[y][x].children[0].checked;
                } else if (obj.options.columns[x].type == 'dropdown' || obj.options.columns[x].type == 'autocomplete') {
                    // Update data and cell
                    obj.options.data[y][x] = value;
                    obj.records[y][x].innerHTML = obj.getDropDownValue(x, value);
                } else if (obj.options.columns[x].type == 'calendar') {
                    // Update calendar
                    var formatted = jSuites.calendar.extractDateFromString(value, obj.options.columns[x].options.format);
                    // Update data and cell
                    obj.options.data[y][x] = value;
                    obj.records[y][x].innerHTML = jSuites.calendar.getDateString(formatted ? formatted : value, obj.options.columns[x].options.format);
                } else if (obj.options.columns[x].type == 'color') {
                    // Update color
                    obj.options.data[y][x] = value;
                    // Render
                    if (obj.options.columns[x].render == 'square') {
                        var color = document.createElement('div');
                        color.className = 'color';
                        color.style.backgroundColor = value;
                        obj.records[y][x].innerHTML = '';
                        obj.records[y][x].appendChild(color);
                    } else {
                    obj.records[y][x].style.color = value;
                        obj.records[y][x].innerHTML = value;
                    }
                } else if (obj.options.columns[x].type == 'image') {
                    value = ''+value;
                    obj.options.data[y][x] = value;
                    obj.records[y][x].innerHTML = '';
                    if (value && value.substr(0, 10) == 'data:image') {
                        var img = document.createElement('img');
                        img.src = value;
                        obj.records[y][x].appendChild(img);
                    }
                } else {
                    // Update data and cell
                    obj.options.data[y][x] = value;
                    // Label
                    if (('' + value).substr(0,1) == '='  && obj.options.parseFormulas == true) {
                        value = obj.executeFormula(value, x, y);
                    }
                    if (obj.options.columns[x].mask) {
                        var decimal = obj.options.columns[x].decimal || '.';
                        value = '' + jSuites.mask.run(value, obj.options.columns[x].mask, decimal);
                    }
                    obj.records[y][x].innerHTML = value;

                    // Handle big text inside a cell
                    if (obj.options.columns[x].wordWrap != false && (obj.options.wordWrap == true || obj.options.columns[x].wordWrap == true || obj.records[y][x].innerHTML.length > 200)) {
                        obj.records[y][x].style.whiteSpace = 'pre-wrap';
                    } else {
                        obj.records[y][x].style.whiteSpace = '';
                    }
                }
            }

            // Overflow
            if (x > 0) {
                if (value) {
                    obj.records[y][x-1].style.overflow = 'hidden';
                } else {
                    obj.records[y][x-1].style.overflow = '';
                }
            }

            // On change
            if (! obj.ignoreEvents) {
                if (typeof(obj.options.onchange) == 'function') {
                    obj.options.onchange(el, (obj.records[y] && obj.records[y][x] ? obj.records[y][x] : null), x, y, value, record.oldValue);
                }
            }
        }

        return record;
    }

    /**
     * Helper function to copy data using the corner icon
     */
    obj.copyData = function(o, d) {
        // Get data from all selected cells
        var data = obj.getData(true, true);

        // Selected cells
        var h = obj.selectedContainer;

        // Cells
        var x1 = parseInt(o.getAttribute('data-x'));
        var y1 = parseInt(o.getAttribute('data-y'));
        var x2 = parseInt(d.getAttribute('data-x'));
        var y2 = parseInt(d.getAttribute('data-y'));

        // Records
        var records = [];
        var breakControl = false;

        if (h[0] == x1) {
            // Vertical copy
            if (y1 < h[1]) {
                var rowNumber = y1 - h[1];
            } else {
                var rowNumber = 1;
            }
            var colNumber = 0;
        } else {
            if (x1 < h[0]) {
                var colNumber = x1 - h[0];
            } else {
                var colNumber = 1;
            }
            var rowNumber = 0;
        }

        // Copy data procedure
        var posx = 0;
        var posy = 0;

        for (var j = y1; j <= y2; j++) {
            // Skip hidden rows
            if (obj.rows[j] && obj.rows[j].style.display == 'none') {
                continue;
            }

            // Controls
            if (data[posy] == undefined) {
                posy = 0;
            }
            posx = 0;

            // Data columns
            if (h[0] != x1) {
                if (x1 < h[0]) {
                    var colNumber = x1 - h[0];
                } else {
                    var colNumber = 1;
                }
            }
            // Data columns
            for (var i = x1; i <= x2; i++) {
                // Update non-readonly
                if (obj.records[j][i] && ! obj.records[j][i].classList.contains('readonly') && obj.records[j][i].style.display != 'none' && breakControl == false) {
                    // Stop if contains value
                    if (! obj.selection.length) {
                        if (obj.options.data[j][i] != '') {
                            breakControl = true;
                            continue;
                        }
                    }

                    // Column
                    if (data[posy] == undefined) {
                        posx = 0;
                    } else if (data[posy][posx] == undefined) {
                        posx = 0;
                    }

                    // Value
                    var value = data[posy][posx];

                        if (value && ! data[1] && obj.options.autoIncrement == true) {
                        if (obj.options.columns[i].type == 'text' || obj.options.columns[i].type == 'number') {
                            if ((''+value).substr(0,1) == '=') {
                                var tokens = value.match(/([A-Z]+[0-9]+)/g);

                                if (tokens) {
                                    var affectedTokens = [];
                                    for (var index = 0; index < tokens.length; index++) {
                                        var position = jexcel.getIdFromColumnName(tokens[index], 1);
                                        position[0] += colNumber;
                                        position[1] += rowNumber;
                                        if (position[1] < 0) {
                                            position[1] = 0;
                                        }
                                        var token = jexcel.getColumnNameFromId([position[0], position[1]]);

                                        if (token != tokens[index]) {
                                            affectedTokens[tokens[index]] = token;
                                        }
                                    }
                                    // Update formula
                                    if (affectedTokens) {
                                        value = obj.updateFormula(value, affectedTokens)
                                    }
                                }
                            } else {
                                if (value == Number(value)) {
                                    value = Number(value) + rowNumber;
                                }
                            }
                        } else if (obj.options.columns[i].type == 'calendar') {
                            var date = new Date(value);
                            date.setDate(date.getDate() + rowNumber);
                            value = date.getFullYear() + '-' + jexcel.doubleDigitFormat(parseInt(date.getMonth() + 1)) + '-' + jexcel.doubleDigitFormat(date.getDate()) + ' ' + '00:00:00';
                        }
                    }

                    records.push(obj.updateCell(i, j, value));

                    // Update all formulas in the chain
                    obj.updateFormulaChain(i, j, records);
                }
                posx++;
                if (h[0] != x1) {
                    colNumber++;
                }
            }
            posy++;
            rowNumber++;
        }

        // Update history
        obj.setHistory({
            action:'setValue',
            records:records,
            selection:obj.selectedCell,
        });

        // Update table with custom configuration if applicable
        obj.updateTable();

        // On after changes
        obj.onafterchanges(el, records);
    }

    /**
     * Refresh current selection
     */
    obj.refreshSelection = function() {
        if (obj.selectedCell) {
            obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
        }
    }

    /**
     * Move coords to A1 in case ovelaps with an excluded cell
     */
    obj.conditionalSelectionUpdate = function(type, o, d) {
        if (type == 1) {
            if (obj.selectedCell && ((o >= obj.selectedCell[1] && o <= obj.selectedCell[3]) || (d >= obj.selectedCell[1] && d <= obj.selectedCell[3]))) {
                obj.resetSelection();
                return;
            }
        } else {
            if (obj.selectedCell && ((o >= obj.selectedCell[0] && o <= obj.selectedCell[2]) || (d >= obj.selectedCell[0] && d <= obj.selectedCell[2]))) {
                obj.resetSelection();
                return;
            }
        }
    }

    /**
     * Clear table selection
     */
    obj.resetSelection = function(blur) {
        // Remove style
        if (! obj.highlighted.length) {
            var previousStatus = 0;
        } else {
            var previousStatus = 1;

            for (var i = 0; i < obj.highlighted.length; i++) {
                obj.highlighted[i].classList.remove('highlight');
                obj.highlighted[i].classList.remove('highlight-left');
                obj.highlighted[i].classList.remove('highlight-right');
                obj.highlighted[i].classList.remove('highlight-top');
                obj.highlighted[i].classList.remove('highlight-bottom');
                obj.highlighted[i].classList.remove('highlight-selected');

                var px = parseInt(obj.highlighted[i].getAttribute('data-x'));
                var py = parseInt(obj.highlighted[i].getAttribute('data-y'));

                // Check for merged cells
                if (obj.highlighted[i].getAttribute('data-merged')) {
                    var colspan = parseInt(obj.highlighted[i].getAttribute('colspan'));
                    var rowspan = parseInt(obj.highlighted[i].getAttribute('rowspan'));
                    var ux = colspan > 0 ? px + (colspan - 1) : px;
                    var uy = rowspan > 0 ? py + (rowspan - 1): py;
                } else {
                    var ux = px;
                    var uy = py;
                }

                // Remove selected from headers
                for (var j = px; j <= ux; j++) {
                    if (obj.headers[j]) {
                        obj.headers[j].classList.remove('selected');
                    }
                }

                // Remove selected from rows
                for (var j = py; j <= uy; j++) {
                    if (obj.rows[j]) {
                        obj.rows[j].classList.remove('selected');
                    }
                }
            }
        }

        // Reset highlighed cells
        obj.highlighted = [];

        // Reset
        obj.selectedCell = null;

        // Hide corner
        obj.corner.style.top = '-2000px';
        obj.corner.style.left = '-2000px';

        if (obj.ignoreEvents != true && blur == true) {
            if (obj.options.onblur) {
                if (typeof(obj.options.onblur) == 'function') {
                    if (previousStatus == 1) {
                        obj.options.onblur(el);
                    }
                }
            }
        }

        return previousStatus;
    }

    /**
     * Update selection based on two cells
     */
    obj.updateSelection = function(el1, el2, origin) {
        var x1 = el1.getAttribute('data-x');
        var y1 = el1.getAttribute('data-y');
        if (el2) {
            var x2 = el2.getAttribute('data-x');
            var y2 = el2.getAttribute('data-y');
        } else {
            var x2 = x1;
            var y2 = y1;
        }

        obj.updateSelectionFromCoords(x1, y1, x2, y2, origin);
    }

    /**
     * Update selection from coords
     */
    obj.updateSelectionFromCoords = function(x1, y1, x2, y2, origin) {
        // Reset Selection
        var updated = null;
        var previousState = obj.resetSelection();

        // Same element
        if (x2 == null) {
            x2 = x1;
        }
        if (y2 == null) {
            y2 = y1;
        }

        // Selection must be within the existing data
        if (x1 >= obj.headers.length) {
            x1 = obj.headers.length - 1;
        }
        if (y1 >= obj.rows.length) {
            y1 = obj.rows.length - 1;
        }
        if (x2 >= obj.headers.length) {
            x2 = obj.headers.length - 1;
        }
        if (y2 >= obj.rows.length) {
            y2 = obj.rows.length - 1;
        }

        // Keep selected cell
        obj.selectedCell = [x1, y1, x2, y2];

        // Select cells
        if (x1 != null) {
            // Add selected cell
            if (obj.records[y1][x1]) {
                obj.records[y1][x1].classList.add('highlight-selected');
            }

            // Origin & Destination
            if (parseInt(x1) < parseInt(x2)) {
                var px = parseInt(x1);
                var ux = parseInt(x2);
            } else {
                var px = parseInt(x2);
                var ux = parseInt(x1);
            }

            if (parseInt(y1) < parseInt(y2)) {
                var py = parseInt(y1);
                var uy = parseInt(y2);
            } else {
                var py = parseInt(y2);
                var uy = parseInt(y1);
            }

            // Verify merged columns
            for (var i = px; i <= ux; i++) {
                for (var j = py; j <= uy; j++) {
                    if (obj.records[j][i] && obj.records[j][i].getAttribute('data-merged')) {
                        var x = parseInt(obj.records[j][i].getAttribute('data-x'));
                        var y = parseInt(obj.records[j][i].getAttribute('data-y'));
                        var colspan = parseInt(obj.records[j][i].getAttribute('colspan'));
                        var rowspan = parseInt(obj.records[j][i].getAttribute('rowspan'));

                        if (colspan > 1) {
                            if (x < px) {
                                px = x;
                            }
                            if (x + colspan > ux) {
                                ux = x + colspan - 1;
                            }
                        }

                        if (rowspan) {
                            if (y < py) {
                                py = y;

                            }
                            if (y + rowspan > uy) {
                                uy = y + rowspan - 1;
                            }
                        }
                    }
                }
            }

            // Limits
            var borderLeft = null;
            var borderRight = null;
            var borderTop = null;
            var borderBottom = null;

            // Vertical limits
            for (var j = py; j <= uy; j++) {
                if (obj.rows[j].style.display != 'none') {
                    if (borderTop == null) {
                        borderTop = j;
                    }
                    borderBottom = j;
                }
            }

            // Redefining styles
            for (var i = px; i <= ux; i++) {
                for (var j = py; j <= uy; j++) {
                    if (obj.rows[j].style.display != 'none' && obj.records[j][i].style.display != 'none') {
                        obj.records[j][i].classList.add('highlight');
                        obj.highlighted.push(obj.records[j][i]);
                    }
                }

                // Horizontal limits
                if (obj.options.columns[i].type != 'hidden') {
                    if (borderLeft == null) {
                        borderLeft = i;
                    }
                    borderRight = i;
                }
            }

            // Create borders
            if (! borderLeft) {
                borderLeft = 0;
            }
            if (! borderRight) {
                borderRight = 0;
            }
            for (var i = borderLeft; i <= borderRight; i++) {
                if (obj.options.columns[i].type != 'hidden') {
                    // Top border
                    if (obj.records[borderTop][i]) {
                        obj.records[borderTop][i].classList.add('highlight-top');
                    }
                    // Bottom border
                    if (obj.records[borderBottom][i]) {
                        obj.records[borderBottom][i].classList.add('highlight-bottom');
                    }
                    // Add selected from headers
                    obj.headers[i].classList.add('selected');
                }
            }

            for (var j = borderTop; j <= borderBottom; j++) {
                if (obj.rows[j].style.display != 'none') {
                    // Left border
                    obj.records[j][borderLeft].classList.add('highlight-left');
                    // Right border
                    obj.records[j][borderRight].classList.add('highlight-right');
                    // Add selected from rows
                    obj.rows[j].classList.add('selected');
                }
            }

            obj.selectedContainer = [ borderLeft, borderTop, borderRight, borderBottom ];
        }

        // Handle events
        if (obj.ignoreEvents != true) {
            if (obj.options.onfocus) {
                if (typeof(obj.options.onfocus) == 'function') {
                    if (previousState == 0) {
                        obj.options.onfocus(el);
                    }
                }
            }

            if (typeof(obj.options.onselection) == 'function') {
                obj.options.onselection(el, borderLeft, borderTop, borderRight, borderBottom, origin);
            }
        }

        // Find corner cell
        obj.updateCornerPosition();
    }

    /**
     * Remove copy selection
     * 
     * @return void
     */
    obj.removeCopySelection = function() {
        // Remove current selection
        for (var i = 0; i < obj.selection.length; i++) {
            obj.selection[i].classList.remove('selection');
            obj.selection[i].classList.remove('selection-left');
            obj.selection[i].classList.remove('selection-right');
            obj.selection[i].classList.remove('selection-top');
            obj.selection[i].classList.remove('selection-bottom');
        }

        obj.selection = [];
    }

    /**
     * Update copy selection
     * 
     * @param int x, y
     * @return void
     */
    obj.updateCopySelection = function(x3, y3) {
        // Remove selection
        obj.removeCopySelection();

        // Get elements first and last
        var x1 = obj.selectedContainer[0];
        var y1 = obj.selectedContainer[1];
        var x2 = obj.selectedContainer[2];
        var y2 = obj.selectedContainer[3];

        if (x3 != null && y3 != null) {
            if (x3 - x2 > 0) {
                var px = parseInt(x2) + 1;
                var ux = parseInt(x3);
            } else {
                var px = parseInt(x3);
                var ux = parseInt(x1) - 1;
            }

            if (y3 - y2 > 0) {
                var py = parseInt(y2) + 1;
                var uy = parseInt(y3);
            } else {
                var py = parseInt(y3);
                var uy = parseInt(y1) - 1;
            }

            if (ux - px < uy - py) {
                var px = parseInt(x1);
                var ux = parseInt(x2);
            } else {
                var py = parseInt(y1);
                var uy = parseInt(y2);
            }

            for (var j = py; j <= uy; j++) {
                for (var i = px; i <= ux; i++) {
                    if (obj.records[j][i] && obj.rows[j].style.display != 'none' && obj.records[j][i].style.display != 'none') {
                        obj.records[j][i].classList.add('selection');
                        obj.records[py][i].classList.add('selection-top');
                        obj.records[uy][i].classList.add('selection-bottom');
                        obj.records[j][px].classList.add('selection-left');
                        obj.records[j][ux].classList.add('selection-right');

                        // Persist selected elements
                        obj.selection.push(obj.records[j][i]);
                    }
                }
            }
        }
    }

    /**
     * Update corner position
     * 
     * @return void
     */
    obj.updateCornerPosition = function() {
        // If any selected cells
        if (! obj.highlighted.length) {
            obj.corner.style.top = '-2000px';
            obj.corner.style.left = '-2000px';
        } else {
            // Get last cell
            var last = obj.highlighted[obj.highlighted.length-1];

            const contentRect = obj.content.getBoundingClientRect();
            var x1 = contentRect.left;
            var y1 = contentRect.top;

            const lastRect = last.getBoundingClientRect();
            var x2 = lastRect.left;
            var y2 = lastRect.top;
            var w2 = lastRect.width;
            var h2 = lastRect.height;

            var x = (x2 - x1) + obj.content.scrollLeft + w2 - 4;
            var y = (y2 - y1) + obj.content.scrollTop + h2 - 4;

            // Place the corner in the correct place
            obj.corner.style.top = y + 'px';
            obj.corner.style.left = x + 'px';
        }
    }

    /**
     * Update scroll position based on the selection
     */
    obj.updateScroll = function(direction) {
        // jExcel Container information
        const contentRect = obj.content.getBoundingClientRect();
        var x1 = contentRect.left;
        var y1 = contentRect.top;
        var w1 = contentRect.width;
        var h1 = contentRect.height;

        // Direction Left or Up
        var reference = obj.records[obj.selectedCell[3]][obj.selectedCell[2]];

            // Reference
        const referenceRect = reference.getBoundingClientRect();
        var x2 = referenceRect.left;
        var y2 = referenceRect.top;
        var w2 = referenceRect.width;
        var h2 = referenceRect.height;

        // Direction
        if (direction == 0 || direction == 1) {
            var x = (x2 - x1) + obj.content.scrollLeft;
            var y = (y2 - y1) + obj.content.scrollTop - 2;
        } else {
            var x = (x2 - x1) + obj.content.scrollLeft + w2;
            var y = (y2 - y1) + obj.content.scrollTop + h2;
        }

        // Top position check
        if (y > (obj.content.scrollTop + 30) && y < (obj.content.scrollTop + h1)) {
            // In the viewport
        } else {
            // Out of viewport
            if (y < obj.content.scrollTop + 30) {
                obj.content.scrollTop = y - h2;
            } else {
                obj.content.scrollTop = y - (h1 - 2);
            }
        }

        // Left position check - TODO: change that to the bottom border of the element
        if (x > (obj.content.scrollLeft) && x < (obj.content.scrollLeft + w1)) {
            // In the viewport
        } else {
            // Out of viewport
            if (x < obj.content.scrollLeft + 30) {
                obj.content.scrollLeft = x;
                if (obj.content.scrollLeft < 50) {
                    obj.content.scrollLeft = 0;
                }
            } else {
                obj.content.scrollLeft = x - (w1 - 20);
            }
        }
    }

    /**
     * Get the column width
     * 
     * @param int column column number (first column is: 0)
     * @return int current width
     */
    obj.getWidth = function(column) {
        if (! column) {
            // Get all headers
            var data = [];
            for (var i = 0; i < obj.headers.length; i++) {
                data.push(obj.options.columns[i].width);
            }
        } else {
            // In case the column is an object
            if (typeof(column) == 'object') {
                column = $(column).getAttribute('data-x');
            }

            data = obj.colgroup[column].getAttribute('width')
        }

        return data;
    }

    /**
     * Set the column width
     * 
     * @param int column number (first column is: 0)
     * @param int new column width
     * @param int old column width
     */
    obj.setWidth = function (column, width, oldWidth) {
        if (width > 0) {
            // In case the column is an object
            if (typeof(column) == 'object') {
                column = column.getAttribute('data-x');
            }

            // Oldwidth
            if (! oldWidth) {
                oldWidth = obj.colgroup[column].getAttribute('width');
            }

            // Set width
            obj.colgroup[column].setAttribute('width', width);
            obj.options.columns[column].width = width;

            // Keeping history of changes
            obj.setHistory({
                action:'setWidth',
                column:column,
                oldValue:oldWidth,
                newValue:width,
            });

            // On resize column
            if (obj.ignoreEvents != true) {
                if (typeof(obj.options.onresizecolumn) == 'function') {
                    obj.options.onresizecolumn(el, column, width, oldWidth);
                }
            }

            // Update corner position
            obj.updateCornerPosition();
        }
    }

    /**
     * Set the row height
     * 
     * @param row - row number (first row is: 0)
     * @param height - new row height
     * @param oldHeight - old row height
     */
    obj.setHeight = function (row, height, oldHeight) {
        if (height > 0) {
            // In case the column is an object
            if (typeof(row) == 'object') {
                row = row.getAttribute('data-y');
            }

            // Oldwidth
            if (! oldHeight) {
                oldHeight = obj.rows[row].getAttribute('height');

                if (! oldHeight) {
                    var rect = obj.rows[row].getBoundingClientRect();
                    oldHeight = rect.height;
                }
            }

            // Integer
            height = parseInt(height);

            // Set width
            obj.rows[row].style.height = height + 'px';

            // Keep options updated
            if (! obj.options.rows[row]) {
                obj.options.rows[row] = {};
            }
            obj.options.rows[row].height = height;

            // Keeping history of changes
            obj.setHistory({
                action:'setHeight',
                row:row,
                oldValue:oldHeight,
                newValue:height,
            });

            // On resize column
            if (obj.ignoreEvents != true) {
                if (typeof(obj.options.onresizerow) == 'function') {
                    obj.options.onresizerow(el, row, height, oldHeight);
                }
            }

            // Update corner position
            obj.updateCornerPosition();
        }
    }

    /**
     * Get the row height
     * 
     * @param row - row number (first row is: 0)
     * @return height - current row height
     */
    obj.getHeight = function(row) {
        if (! row) {
            // Get height of all rows
            var data = [];
            for (var j = 0; j < obj.rows.length; j++) {
                var h = obj.rows[j].style.height;
                if (h) {
                    data[j] = h;
                }
            }
        } else {
            // In case the row is an object
            if (typeof(row) == 'object') {
                row = $(row).getAttribute('data-y');
            }

            var data = obj.rows[row].style.height;
        }

        return data;
    }

    /**
     * Get the column title
     * 
     * @param column - column number (first column is: 0)
     * @param title - new column title
     */
    obj.getHeader = function(column) {
        return obj.headers[column].innerText;
    }

    /**
     * Set the column title
     * 
     * @param column - column number (first column is: 0)
     * @param title - new column title
     */
    obj.setHeader = function(column, newValue) {
        if (obj.headers[column]) {
            var oldValue = obj.headers[column].innerText;

            if (! newValue) {
                newValue = prompt(obj.options.text.columnName, oldValue)
            }

            if (newValue) {
                obj.headers[column].innerHTML = newValue;
                // Keep the title property
                obj.headers[column].setAttribute('title', newValue);
            }

            obj.setHistory({
                action: 'setHeader',
                column: column,
                oldValue: oldValue,
                newValue: newValue
            });

            // On change
            if (! obj.ignoreEvents) {
                if (typeof(obj.options.onchangeheader) == 'function') {
                    obj.options.onchangeheader(el, column, oldValue, newValue);
                }
            }
        }
    }

    /**
     * Get the headers
     * 
     * @param asArray
     * @return mixed
     */
    obj.getHeaders = function (asArray) {
        var title = [];

        for (var i = 0; i < obj.headers.length; i++) {
            title.push(obj.getHeader(i));
        }

        return asArray ? title : title.join(obj.options.csvDelimiter);
    }

    /**
     * Get meta information from cell(s)
     * 
     * @return integer
     */
    obj.getMeta = function(cell, key) {
        if (! cell) {
            return obj.options.meta;
        } else {
            if (key) {
                return obj.options.meta[cell] && obj.options.meta[cell][key] ? obj.options.meta[cell][key] : null;
            } else {
                return obj.options.meta[cell] ? obj.options.meta[cell] : null;
            }
        }
    }

    /**
     * Set meta information to cell(s)
     * 
     * @return integer
     */
    obj.setMeta = function(o, k, v) {
        if (! obj.options.meta) {
            obj.options.meta = {}
        }

        if (k && v) {
            // Set data value
            if (! obj.options.meta[o]) {
                obj.options.meta[o] = {};
            }
            obj.options.meta[o][k] = v;
        } else {
            // Apply that for all cells
            var keys = Object.keys(o);
            for (var i = 0; i < keys.length; i++) {
                if (! obj.options.meta[keys[i]]) {
                    obj.options.meta[keys[i]] = {};
                }

                var prop = Object.keys(o[keys[i]]);
                for (var j = 0; j < prop.length; j++) {
                    obj.options.meta[keys[i]][prop[j]] = o[keys[i]][prop[j]];
                }
            }
        }

        if (obj.ignoreEvents != true) {
            if (typeof(obj.options.onchangemeta) == 'function') {
                obj.options.onchangemeta(el, o, k, v);
            }
        }
    }

    /**
     * Update meta information
     * 
     * @return integer
     */
    obj.updateMeta = function(affectedCells) {
        if (obj.options.meta) {
            var newMeta = {};
            var keys = Object.keys(obj.options.meta);
            for (var i = 0; i < keys.length; i++) {
                if (affectedCells[keys[i]]) {
                    newMeta[affectedCells[keys[i]]] = obj.options.meta[keys[i]];
                } else {
                    newMeta[keys[i]] = obj.options.meta[keys[i]];
                }
            }
            // Update meta information
            obj.options.meta = newMeta;
        }
    }

    /**
     * Get style information from cell(s)
     * 
     * @return integer
     */
    obj.getStyle = function(cell, key) {
        // Cell
        if (! cell) {
            // Control vars
            var data = {};

            // Column and row length
            var x = obj.options.data[0].length;
            var y = obj.options.data.length;

            // Go through the columns to get the data
            for (var j = 0; j < y; j++) {
                for (var i = 0; i < x; i++) {
                    // Value
                    var v = key ? obj.records[j][i].style[key] : obj.records[j][i].getAttribute('style');

                    // Any meta data for this column?
                    if (v) {
                        // Column name
                        var k = jexcel.getColumnNameFromId([i, j]);
                        // Value
                        data[k] = v;
                    }
                }
            }

           return data;
        } else {
            cell = jexcel.getIdFromColumnName(cell, true);

            return key ? obj.records[cell[1]][cell[0]].style[key] : obj.records[cell[1]][cell[0]].getAttribute('style');
        }
    },

    obj.resetStyle = function(o, ignoreHistoryAndEvents) {
        var keys = Object.keys(o);
        for (var i = 0; i < keys.length; i++) {
            // Position
            var cell = jexcel.getIdFromColumnName(keys[i], true);
            if (obj.records[cell[1]] && obj.records[cell[1]][cell[0]]) {
                obj.records[cell[1]][cell[0]].setAttribute('style', '');
            }
        }
        obj.setStyle(o, null, null, null, ignoreHistoryAndEvents);
    }

    /**
     * Set meta information to cell(s)
     * 
     * @return integer
     */
    obj.setStyle = function(o, k, v, force, ignoreHistoryAndEvents) {
        var newValue = {};
        var oldValue = {};

        // Apply style
        var applyStyle = function(cellId, key, value) {
            // Position
            var cell = jexcel.getIdFromColumnName(cellId, true);

            if (obj.records[cell[1]] && obj.records[cell[1]][cell[0]]) {
                // Current value
                var currentValue = obj.records[cell[1]][cell[0]].style[key];

                // Change layout
                if (currentValue == value && ! force) {
                    value = '';
                    obj.records[cell[1]][cell[0]].style[key] = '';
                } else {
                    obj.records[cell[1]][cell[0]].style[key] = value;
                }

                // History
                if (! oldValue[cellId]) {
                    oldValue[cellId] = [];
                }
                if (! newValue[cellId]) {
                    newValue[cellId] = [];
                }

                oldValue[cellId].push([key + ':' + currentValue]);
                newValue[cellId].push([key + ':' + value]);
            }
        }

        if (k && v) {
            // Get object from string
            if (typeof(o) == 'string') {
                applyStyle(o, k, v);
            } else {
                // Avoid duplications
                var oneApplication = [];
                // Apply that for all cells
                for (var i = 0; i < o.length; i++) {
                    var x = o[i].getAttribute('data-x');
                    var y = o[i].getAttribute('data-y');
                    var cellName = jexcel.getColumnNameFromId([x, y]);
                    // This happens when is a merged cell
                    if (! oneApplication[cellName]) {
                        applyStyle(cellName, k, v);
                        oneApplication[cellName] = true;
                    }
                }
            }
        } else {
            var keys = Object.keys(o);
            for (var i = 0; i < keys.length; i++) {
                var style = o[keys[i]];
                if (typeof(style) == 'string') {
                    style = style.split(';');
                }
                for (var j = 0; j < style.length; j++) {
                    if (typeof(style[j]) == 'string') {
                        style[j] = style[j].split(':');
                    }
                    // Apply value
                    if (style[j][0].trim()) {
                        applyStyle(keys[i], style[j][0].trim(), style[j][1]);
                    }
                }
            }
        }

        var keys = Object.keys(oldValue);
        for (var i = 0; i < keys.length; i++) {
            oldValue[keys[i]] = oldValue[keys[i]].join(';');
        }
        var keys = Object.keys(newValue);
        for (var i = 0; i < keys.length; i++) {
            newValue[keys[i]] = newValue[keys[i]].join(';');
        }

        if (! ignoreHistoryAndEvents) {
            // Keeping history of changes
            obj.setHistory({
                action: 'setStyle',
                oldValue: oldValue,
                newValue: newValue,
            });
        }

        if (obj.ignoreEvents != true) {
            if (typeof(obj.options.onchangestyle) == 'function') {
                obj.options.onchangestyle(el, o, k, v);
            }
        }
    }

    /**
     * Get cell comments, null cell for all
     */
    obj.getComments = function(cell, withAuthor) {
        if (cell) {
            if (typeof(cell) == 'string') {
              var cell = jexcel.getIdFromColumnName(cell, true);
            }

            if (withAuthor) {
                return [obj.records[cell[1]][cell[0]].getAttribute('title'), obj.records[cell[1]][cell[0]].getAttribute('author')];
            } else {
                return obj.records[cell[1]][cell[0]].getAttribute('title') || '';
            }
        } else {
            var data = {};
            for (var j = 0; j < obj.options.data.length; j++) {
                for (var i = 0; i < obj.options.columns.length; i++) {
                    var comments = obj.records[j][i].getAttribute('title');
                    if (comments) {
                        var cell = jexcel.getColumnNameFromId([i, j]);
                        data[cell] = comments;
                    }
                }
            }
            return data;
        }
    }

    /**
     * Set cell comments
     */
    obj.setComments = function(cellId, comments, author) {
        if (typeof(cellId) == 'string') {
            var cell = jexcel.getIdFromColumnName(cellId, true);
        } else {
            var cell = cellId;
        }

        // Keep old value
        var title = obj.records[cell[1]][cell[0]].getAttribute('title');
        var author = obj.records[cell[1]][cell[0]].getAttribute('data-author');
        var oldValue = [ title, author ];

        // Set new values
        obj.records[cell[1]][cell[0]].setAttribute('title', comments ? comments : '');
        obj.records[cell[1]][cell[0]].setAttribute('data-author', author ? author : '');

        // Remove class if there is no comment
        if (comments) {
            obj.records[cell[1]][cell[0]].classList.add('jexcel_comments');
        } else {
            obj.records[cell[1]][cell[0]].classList.remove('jexcel_comments');
        }

        // Save history
        obj.setHistory({
            action:'setComments',
            column: cellId,
            newValue: [ comments, author ],
            oldValue: oldValue,
        });
    }

    /**
     * Get table config information
     */
    obj.getConfig = function() {
        var options = obj.options;
        options.style = obj.getStyle();
        options.mergeCells = obj.getMerge();
        options.comments = obj.getComments();

        return options;
    }

    /**
     * Sort data and reload table
     */
    obj.orderBy = function(column, order) {
        if (column >= 0) {
            // Merged cells
            if (Object.keys(obj.options.mergeCells).length > 0) {
                if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) {
                    return false;
                } else {
                    // Remove merged cells
                    obj.destroyMerged();
                }
            }

            // Direction
            if (order == null) {
                order = obj.headers[column].classList.contains('arrow-down') ? 1 : 0;
            } else {
                order = order ? 1 : 0;
            }

            // Filter
            Array.prototype.orderBy = function(p, o) {
                return this.slice(0).sort(function(a, b) {
                    var valueA = a[p] == '' ? '' : Number(a[p]) == a[p] ? Number(a[p]) : a[p].toLowerCase();
                    var valueB = b[p] == '' ? '' : Number(b[p]) == b[p] ? Number(b[p]) : b[p].toLowerCase();

                    if (! o) {
                        return (valueA == '' && valueB != '') ? 1 : (valueA != '' && valueB == '') ? -1 : (valueA > valueB) ? 1 : (valueA < valueB) ? -1 :  0;
                    } else {
                        return (valueA == '' && valueB != '') ? 1 : (valueA != '' && valueB == '') ? -1 : (valueA > valueB) ? -1 : (valueA < valueB) ? 1 :  0;
                    }
                });
            }

            // Test order
            var temp = [];
            if (obj.options.columns[column].type == 'calendar' ||
                obj.options.columns[column].type == 'checkbox' ||
                obj.options.columns[column].type == 'radio') {
                for (var j = 0; j < obj.options.data.length; j++) {
                    temp[j] = [ j, obj.options.data[j][column] ];
                }
            } else {
                for (var j = 0; j < obj.options.data.length; j++) {
                    temp[j] = [ j, obj.records[j][column].innerHTML ];
                }
            }
            temp = temp.orderBy(1, order);

            // Save history
            var newValue = [];
            for (var j = 0; j < temp.length; j++) {
                newValue[j] = temp[j][0];
            }

            // Save history
            obj.setHistory({
                action: 'orderBy',
                rows: newValue,
                column: column,
                order: order,
            });

            // Update order
            obj.updateOrderArrow(column, order);
            obj.updateOrder(newValue);

            // On sort event
            if (obj.ignoreEvents != true) {
                if (typeof(obj.options.onsort) == 'function') {
                    obj.options.onsort(el, column, order);
                }
            }

            return true;
        }
    }

    /**
     * Update order arrow
     */
    obj.updateOrderArrow = function(column, order) {
        // Remove order
        for (var i = 0; i < obj.headers.length; i++) {
            obj.headers[i].classList.remove('arrow-up');
            obj.headers[i].classList.remove('arrow-down');
        }

        // No order specified then toggle order
        if (order) {
            obj.headers[column].classList.add('arrow-up');
        } else {
            obj.headers[column].classList.add('arrow-down');
        }
    }

    /**
     * Update rows position
     */
    obj.updateOrder = function(rows) {
        // History
        var data = []
        for (var j = 0; j < rows.length; j++) {
            data[j] = obj.options.data[rows[j]];
        }
        obj.options.data = data;

        var data = []
        for (var j = 0; j < rows.length; j++) {
            data[j] = obj.records[rows[j]];
        }
        obj.records = data;

        var data = []
        for (var j = 0; j < rows.length; j++) {
            data[j] = obj.rows[rows[j]];
        }
        obj.rows = data;

        // Update references
        obj.updateTableReferences();

        // Redo search
        if (obj.searchInput.value) {
            obj.search(obj.searchInput.value);
        } else {
            // Create page
            obj.results = null;
            obj.pageNumber = 0;

            if (obj.options.pagination > 0) {
                obj.page(0);
            } else if (obj.options.lazyLoading == true) {
                obj.loadPage(0);
            } else {
                for (var j = 0; j < obj.rows.length; j++) {
                    obj.tbody.appendChild(obj.rows[j]);
                }
            }
        }
    }

    /**
     * Move row
     * 
     * @return void
     */
    obj.moveRow = function(o, d, ignoreDom) {
        if (Object.keys(obj.options.mergeCells).length > 0) {
           if (o > d) {
               var insertBefore = 1;
           } else {
               var insertBefore = 0;
           }

           if (obj.isRowMerged(o).length || obj.isRowMerged(d, insertBefore).length) {
                if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) {
                    return false;
                } else {
                    obj.destroyMerged();
                }
            }
        }

        if (obj.options.search == true) {
            if (obj.results && obj.results.length != obj.rows.length) {
                if (confirm(obj.options.text.thisActionWillClearYourSearchResultsAreYouSure)) {
                    obj.resetSearch();
                } else {
                    return false;
                }
            }

            obj.results = null;
        }

        if (! ignoreDom) {
            if (Array.prototype.indexOf.call(obj.tbody.children, obj.rows[d]) >= 0) {
                if (o > d) {
                    obj.tbody.insertBefore(obj.rows[o], obj.rows[d]);
                } else {
                    obj.tbody.insertBefore(obj.rows[o], obj.rows[d].nextSibling);
                }
            } else {
                obj.tbody.removeChild(obj.rows[o]);
            }
        }

        // Place references in the correct position
        obj.rows.splice(d, 0, obj.rows.splice(o, 1)[0]);
        obj.records.splice(d, 0, obj.records.splice(o, 1)[0]);
        obj.options.data.splice(d, 0, obj.options.data.splice(o, 1)[0]);

        // Respect pagination
        if (obj.options.pagination > 0 && obj.tbody.children.length != obj.options.pagination) {
            obj.page(obj.pageNumber);
        }

        // Keeping history of changes
        obj.setHistory({
            action:'moveRow',
            oldValue: o,
            newValue: d,
        });

        // Update table references
        obj.updateTableReferences();

        // Events
        if (obj.ignoreEvents != true) {
            if (typeof(obj.options.onmoverow) == 'function') {
                obj.options.onmoverow(el, o, d);
            }
        }
    }

    /**
     * Insert a new row
     * 
     * @param mixed - number of blank lines to be insert or a single array with the data of the new row
     * @param rowNumber
     * @param insertBefore
     * @return void
     */
    obj.insertRow = function(mixed, rowNumber, insertBefore) {
        // Configuration
        if (obj.options.allowInsertRow == true) {
            // Records
            var records = [];

            // Data to be insert
            var data = [];

            // The insert could be lead by number of rows or the array of data
            if (mixed > 0) {
                var numOfRows = mixed;
            } else {
                var numOfRows = 1;

                if (mixed) {
                    data = mixed;
                }
            }

            // Direction
            var insertBefore = insertBefore ? true : false;

            // Current column number
            var lastRow = obj.options.data.length - 1;

            if (rowNumber == undefined || rowNumber >= parseInt(lastRow) || rowNumber < 0) {
                rowNumber = lastRow;
            }

            // Onbeforeinsertrow
            if (typeof(obj.options.onbeforeinsertrow) == 'function') {
                if (! obj.options.onbeforeinsertrow(el, rowNumber, numOfRows, insertBefore)) {
                    console.log('onbeforeinsertrow returned false');

                    return false;
                }
            }

            // Merged cells
            if (Object.keys(obj.options.mergeCells).length > 0) {
                if (obj.isRowMerged(rowNumber, insertBefore).length) {
                    if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) {
                        return false;
                    } else {
                        obj.destroyMerged();
                    }
                }
            }

            // Clear any search
            if (obj.options.search == true) {
                if (obj.results && obj.results.length != obj.rows.length) {
                    if (confirm(obj.options.text.thisActionWillClearYourSearchResultsAreYouSure)) {
                        obj.resetSearch();
                    } else {
                        return false;
                    }
                }

                obj.results = null;
            }

            // Insertbefore
            var rowIndex = (! insertBefore) ? rowNumber + 1 : rowNumber;

            // Keep the current data
            var currentRecords = obj.records.splice(rowIndex);
            var currentData = obj.options.data.splice(rowIndex);
            var currentRows = obj.rows.splice(rowIndex);

            // Adding lines
            var rowRecords = [];
            var rowData = [];
            var rowNode = [];

            for (var row = rowIndex; row < (numOfRows + rowIndex); row++) {
                // Push data to the data container
                obj.options.data[row] = [];
                for (var col = 0; col < obj.options.columns.length; col++) {
                    obj.options.data[row][col]  = data[col] ? data[col] : '';
                }
                // Create row
                var tr = obj.createRow(row, obj.options.data[row]);
                // Append node
                if (currentRows[0]) {
                    if (Array.prototype.indexOf.call(obj.tbody.children, currentRows[0]) >= 0) {
                        obj.tbody.insertBefore(tr, currentRows[0]);
                    }
                } else {
                    if (Array.prototype.indexOf.call(obj.tbody.children, obj.rows[rowNumber]) >= 0) {
                        obj.tbody.appendChild(tr);
                    }
                }
                // Record History
                rowRecords.push(obj.records[row]);
                rowData.push(obj.options.data[row]);
                rowNode.push(tr);
            }

            // Copy the data back to the main data
            Array.prototype.push.apply(obj.records, currentRecords);
            Array.prototype.push.apply(obj.options.data, currentData);
            Array.prototype.push.apply(obj.rows, currentRows);

            // Respect pagination
            if (obj.options.pagination > 0) {
                obj.page(obj.pageNumber);
            }

            // Keep history
            obj.setHistory({
                action: 'insertRow',
                rowNumber: rowNumber,
                numOfRows: numOfRows,
                insertBefore: insertBefore,
                rowRecords: rowRecords,
                rowData: rowData,
                rowNode: rowNode,
            });

            // Remove table references
            obj.updateTableReferences();

            // Events
            if (obj.ignoreEvents != true) {
                if (typeof(obj.options.oninsertrow) == 'function') {
                    obj.options.oninsertrow(el, rowNumber, numOfRows, rowRecords, insertBefore);
                }
            }
        }
    }

    /**
     * Delete a row by number
     * 
     * @param integer rowNumber - row number to be excluded
     * @param integer numOfRows - number of lines
     * @return void
     */
    obj.deleteRow = function(rowNumber, numOfRows) {
        // Global Configuration
        if (obj.options.allowDeleteRow == true) {
            if (obj.options.allowDeletingAllRows == true || obj.options.data.length > 1) {
                // Delete row definitions
                if (rowNumber == undefined) {
                    var number = obj.getSelectedRows();

                    if (! number[0]) {
                        rowNumber = obj.options.data.length - 1;
                        numOfRows = 1;
                    } else {
                        rowNumber = parseInt(number[0].getAttribute('data-y'));
                        numOfRows = number.length;
                    }
                }

                // Last column
                var lastRow = obj.options.data.length - 1;

                if (rowNumber == undefined || rowNumber > lastRow || rowNumber < 0) {
                    rowNumber = lastRow;
                }

                if (! numOfRows) {
                    numOfRows = 1;
                }

                // Do not delete more than the number of recoreds
                if (rowNumber + numOfRows >= obj.options.data.length) {
                    numOfRows = obj.options.data.length - rowNumber;
                }

                // Onbeforedeleterow
                if (typeof(obj.options.onbeforedeleterow) == 'function') {
                    if (! obj.options.onbeforedeleterow(el, rowNumber, numOfRows)) {
                        console.log('onbeforedeleterow returned false');
                        return false;
                    }
                }

                if (parseInt(rowNumber) > -1) {
                    // Merged cells
                    var mergeExists = false;
                    if (Object.keys(obj.options.mergeCells).length > 0) {
                        for (var row = rowNumber; row < rowNumber + numOfRows; row++) {
                            if (obj.isRowMerged(row, false).length) {
                                mergeExists = true;
                            }
                        }
                    }
                    if (mergeExists) {
                        if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) {
                            return false;
                        } else {
                            obj.destroyMerged();
                        }
                    }

                    // Clear any search
                    if (obj.options.search == true) {
                        if (obj.results && obj.results.length != obj.rows.length) {
                            if (confirm(obj.options.text.thisActionWillClearYourSearchResultsAreYouSure)) {
                                obj.resetSearch();
                            } else {
                                return false;
                            }
                        }

                        obj.results = null;
                    }

                    // Remove node
                    for (var row = rowNumber; row < rowNumber + numOfRows; row++) {
                        if (Array.prototype.indexOf.call(obj.tbody.children, obj.rows[row]) >= 0) {
                            obj.rows[row].className = '';
                            obj.rows[row].parentNode.removeChild(obj.rows[row]);
                        }
                    }

                    // Remove data
                    var rowRecords = obj.records.splice(rowNumber, numOfRows);
                    var rowData = obj.options.data.splice(rowNumber, numOfRows);
                    var rowNode = obj.rows.splice(rowNumber, numOfRows);

                    // Respect pagination
                    if (obj.options.pagination > 0 && obj.tbody.children.length != obj.options.pagination) {
                        obj.page(obj.pageNumber);
                    }

                    // Remove selection
                    obj.conditionalSelectionUpdate(1, rowNumber, (rowNumber + numOfRows) - 1);

                    // Keep history
                    obj.setHistory({
                        action: 'deleteRow',
                        rowNumber: rowNumber,
                        numOfRows: numOfRows,
                        insertBefore: 1,
                        rowRecords: rowRecords,
                        rowData: rowData,
                        rowNode: rowNode
                    });

                    // Remove table references
                    obj.updateTableReferences();

                    // Events
                    if (obj.ignoreEvents != true) {
                        if (typeof(obj.options.ondeleterow) == 'function') {
                            obj.options.ondeleterow(el, rowNumber, numOfRows, rowRecords);
                        }
                    }
                }
            } else {
                console.error('JEXCEL. It is not possible to delete the last row');
            }
        }
    }


    /**
     * Move column
     * 
     * @return void
     */
    obj.moveColumn = function(o, d) {
        if (Object.keys(obj.options.mergeCells).length > 0) {
            if (o > d) {
                var insertBefore = 1;
            } else {
                var insertBefore = 0;
            }

            if (obj.isColMerged(o).length || obj.isColMerged(d, insertBefore).length) {
                if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) {
                    return false;
                } else {
                    obj.destroyMerged();
                }
            }
        }

        var o = parseInt(o);
        var d = parseInt(d);

        if (o > d) {
            obj.headerContainer.insertBefore(obj.headers[o], obj.headers[d]);
            obj.colgroupContainer.insertBefore(obj.colgroup[o], obj.colgroup[d]);

            for (var j = 0; j < obj.rows.length; j++) {
                obj.rows[j].insertBefore(obj.records[j][o], obj.records[j][d]);
            }
        } else {
            obj.headerContainer.insertBefore(obj.headers[o], obj.headers[d].nextSibling);
            obj.colgroupContainer.insertBefore(obj.colgroup[o], obj.colgroup[d].nextSibling);

            for (var j = 0; j < obj.rows.length; j++) {
                obj.rows[j].insertBefore(obj.records[j][o], obj.records[j][d].nextSibling);
            }
        }

        obj.options.columns.splice(d, 0, obj.options.columns.splice(o, 1)[0]);
        obj.headers.splice(d, 0, obj.headers.splice(o, 1)[0]);
        obj.colgroup.splice(d, 0, obj.colgroup.splice(o, 1)[0]);

        for (var j = 0; j < obj.rows.length; j++) {
            obj.options.data[j].splice(d, 0, obj.options.data[j].splice(o, 1)[0]);
            obj.records[j].splice(d, 0, obj.records[j].splice(o, 1)[0]);
        }

        // Keeping history of changes
        obj.setHistory({
            action:'moveColumn',
            oldValue: o,
            newValue: d,
        });

        // Update table references
        obj.updateTableReferences();

        // Events
        if (obj.ignoreEvents != true) {
            if (typeof(obj.options.onmovecolumn) == 'function') {
                obj.options.onmovecolumn(el, o, d);
            }
        }
    }


    /**
     * Insert a new column
     * 
     * @param mixed - num of columns to be added or data to be added in one single column
     * @param int columnNumber - number of columns to be created
     * @param bool insertBefore
     * @param object properties - column properties
     * @return void
     */
    obj.insertColumn = function(mixed, columnNumber, insertBefore, properties) {
        // Configuration
        if (obj.options.allowInsertColumn == true) {
            // Records
            var records = [];

            // Data to be insert
            var data = [];

            // The insert could be lead by number of rows or the array of data
            if (mixed > 0) {
                var numOfColumns = mixed;
            } else {
                var numOfColumns = 1;

                if (mixed) {
                    data = mixed;
                }
            }

            // Direction
            var insertBefore = insertBefore ? true : false;

            // Current column number
            var lastColumn = obj.options.columns.length - 1;

            // Confirm position
            if (columnNumber == undefined || columnNumber >= parseInt(lastColumn) || columnNumber < 0) {
                columnNumber = lastColumn;
            }

            // Onbeforeinsertcolumn
            if (typeof(obj.options.onbeforeinsertcolumn) == 'function') {
                if (! obj.options.onbeforeinsertcolumn(el, columnNumber, numOfColumns, insertBefore)) {
                    console.log('onbeforeinsertcolumn returned false');

                    return false;
                }
            }

            // Merged cells
            if (Object.keys(obj.options.mergeCells).length > 0) {
                if (obj.isColMerged(columnNumber, insertBefore).length) {
                    if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) {
                        return false;
                    } else {
                        obj.destroyMerged();
                    }
                }
            }

            // Create default properties
            if (! properties) {
                properties = [];
            }

            for (var i = 0; i < numOfColumns; i++) {
                if (! properties[i]) {
                    properties[i] = { type:'text', source:[], options:[], width:obj.options.defaultColWidth, align:obj.options.defaultColAlign };
                }
            }

            // Insert before
            var columnIndex = (! insertBefore) ? columnNumber + 1 : columnNumber;
            obj.options.columns = jexcel.injectArray(obj.options.columns, columnIndex, properties);

            // Open space in the containers
            var currentHeaders = obj.headers.splice(columnIndex);
            var currentColgroup = obj.colgroup.splice(columnIndex);

            // History
            var historyHeaders = [];
            var historyColgroup = [];
            var historyRecords = [];
            var historyData = [];

            // Add new headers
            for (var col = columnIndex; col < (numOfColumns + columnIndex); col++) {
                obj.createCellHeader(col);
                obj.headerContainer.insertBefore(obj.headers[col], obj.headerContainer.children[col+1]);
                obj.colgroupContainer.insertBefore(obj.colgroup[col], obj.colgroupContainer.children[col+1]);

                historyHeaders.push(obj.headers[col]);
                historyColgroup.push(obj.colgroup[col]);
            }

            // Adding visual columns
            for (var row = 0; row < obj.options.data.length; row++) {
                // Keep the current data
                var currentData = obj.options.data[row].splice(columnIndex);
                var currentRecord = obj.records[row].splice(columnIndex);

                // History
                historyData[row] = [];
                historyRecords[row] = [];

                for (var col = columnIndex; col < (numOfColumns + columnIndex); col++) {
                    // New value
                    var value = data[row] ? data[row] : '';
                    obj.options.data[row][col] = value;
                    // New cell
                    var td = obj.createCell(col, row, obj.options.data[row][col]);
                    obj.records[row][col] = td;
                    // Add cell to the row
                    if (obj.rows[row]) {
                        obj.rows[row].insertBefore(td, obj.rows[row].children[col+1]);
                    }

                    // Record History
                    historyData[row].push(value);
                    historyRecords[row].push(td);
                }

                // Copy the data back to the main data
                Array.prototype.push.apply(obj.options.data[row], currentData);
                Array.prototype.push.apply(obj.records[row], currentRecord);
            }

            Array.prototype.push.apply(obj.headers, currentHeaders);
            Array.prototype.push.apply(obj.colgroup, currentColgroup);

            // Adjust nested headers
            if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0) {
                // Flexible way to handle nestedheaders
                if (obj.options.nestedHeaders[0] && obj.options.nestedHeaders[0][0]) {
                    for (var j = 0; j < obj.options.nestedHeaders.length; j++) {
                        var colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan) + numOfColumns;
                        obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan = colspan;
                        obj.thead.children[j].children[obj.thead.children[j].children.length-1].setAttribute('colspan', colspan);
                    }
                } else {
                    var colspan = parseInt(obj.options.nestedHeaders[0].colspan) + numOfColumns;
                    obj.options.nestedHeaders[0].colspan = colspan;
                    obj.thead.children[0].children[obj.thead.children[0].children.length-1].setAttribute('colspan', colspan);
                }
            }

            // Keep history
            obj.setHistory({
                action: 'insertColumn',
                columnNumber:columnNumber,
                numOfColumns:numOfColumns,
                insertBefore:insertBefore,
                columns:properties,
                headers:historyHeaders,
                colgroup:historyColgroup,
                records:historyRecords,
                data:historyData,
            });

            // Remove table references
            obj.updateTableReferences();

            // Events
            if (obj.ignoreEvents != true) {
                if (typeof(obj.options.oninsertcolumn) == 'function') {
                    obj.options.oninsertcolumn(el, columnNumber, numOfColumns, historyRecords, insertBefore);
                }
            }
        }
    }

    /**
     * Delete a column by number
     * 
     * @param integer columnNumber - reference column to be excluded
     * @param integer numOfColumns - number of columns to be excluded from the reference column
     * @return void
     */
    obj.deleteColumn = function(columnNumber, numOfColumns) {
        // Global Configuration
        if (obj.options.allowDeleteColumn == true) {
            if (obj.headers.length > 1) {
                // Delete column definitions
                if (columnNumber == undefined) {
                    var number = obj.getSelectedColumns(true);

                    if (! number.length) {
                        // Remove last column
                        columnNumber = obj.headers.length - 1;
                        numOfColumns = 1;
                    } else {
                        // Remove selected
                        columnNumber = parseInt(number[0]);
                        numOfColumns = parseInt(number.length);
                    }
                }

                // Lasat column
                var lastColumn = obj.options.data[0].length - 1;

                if (columnNumber == undefined || columnNumber > lastColumn || columnNumber < 0) {
                    columnNumber = lastColumn;
                }

                // Minimum of columns to be delete is 1
                if (! numOfColumns) {
                    numOfColumns = 1;
                }



                // Can't delete more than the limit of the table
                if (numOfColumns > obj.options.data[0].length - columnNumber) {
                    numOfColumns = obj.options.data[0].length - columnNumber;
                }

                // onbeforedeletecolumn
                if (typeof(obj.options.onbeforedeletecolumn) == 'function') {
                   if (! obj.options.onbeforedeletecolumn(el, columnNumber, numOfColumns)) {
                      console.log('onbeforedeletecolumn returned false');
                      return false;
                   }
                }

                // Can't remove the last column
                if (parseInt(columnNumber) > -1) {
                    // Merged cells
                    var mergeExists = false;
                    if (Object.keys(obj.options.mergeCells).length > 0) {
                        for (var col = columnNumber; col < columnNumber + numOfColumns; col++) {
                            if (obj.isColMerged(col, false).length) {
                                mergeExists = true;
                            }
                        }
                    }
                    if (mergeExists) {
                        if (! confirm(obj.options.text.thisActionWillDestroyAnyExistingMergedCellsAreYouSure)) {
                            return false;
                        } else {
                            obj.destroyMerged();
                        }
                    }

                    // Delete the column properties
                    var columns = obj.options.columns.splice(columnNumber, numOfColumns);

                    for (var col = columnNumber; col < columnNumber + numOfColumns; col++) {
                        obj.colgroup[col].className = '';
                        obj.headers[col].className = '';
                        obj.colgroup[col].parentNode.removeChild(obj.colgroup[col]);
                        obj.headers[col].parentNode.removeChild(obj.headers[col]);
                    }

                    var historyHeaders = obj.headers.splice(columnNumber, numOfColumns);
                    var historyColgroup = obj.colgroup.splice(columnNumber, numOfColumns);
                    var historyRecords = [];
                    var historyData = [];

                    for (var row = 0; row < obj.options.data.length; row++) {
                        for (var col = columnNumber; col < columnNumber + numOfColumns; col++) {
                            obj.records[row][col].className = '';
                            obj.records[row][col].parentNode.removeChild(obj.records[row][col]);
                        }
                    }

                    // Delete headers
                    for (var row = 0; row < obj.options.data.length; row++) {
                        // History
                        historyData[row] = obj.options.data[row].splice(columnNumber, numOfColumns);
                        historyRecords[row] = obj.records[row].splice(columnNumber, numOfColumns);
                    }

                    // Remove selection
                    obj.conditionalSelectionUpdate(0, columnNumber, (columnNumber + numOfColumns) - 1);

                    // Adjust nested headers
                    if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0) {
                        // Flexible way to handle nestedheaders
                        if (obj.options.nestedHeaders[0] && obj.options.nestedHeaders[0][0]) {
                            for (var j = 0; j < obj.options.nestedHeaders.length; j++) {
                                var colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan) - numOfColumns;
                                obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan = colspan;
                                obj.thead.children[j].children[obj.thead.children[j].children.length-1].setAttribute('colspan', colspan);
                            }
                        } else {
                            var colspan = parseInt(obj.options.nestedHeaders[0].colspan) - numOfColumns;
                            obj.options.nestedHeaders[0].colspan = colspan;
                            obj.thead.children[0].children[obj.thead.children[0].children.length-1].setAttribute('colspan', colspan);
                        }
                    }

                    // Keeping history of changes
                    obj.setHistory({
                        action:'deleteColumn',
                        columnNumber:columnNumber,
                        numOfColumns:numOfColumns,
                        insertBefore: 1,
                        columns:columns,
                        headers:historyHeaders,
                        colgroup:historyColgroup,
                        records:historyRecords,
                        data:historyData,
                    });

                    // Update table references
                    obj.updateTableReferences();

                    // Delete
                    if (obj.ignoreEvents != true) {
                        if (typeof(obj.options.ondeletecolumn) == 'function') {
                            obj.options.ondeletecolumn(el, columnNumber, numOfColumns, historyRecords);
                        }
                    }
                }
            } else {
                console.error('JEXCEL. It is not possible to delete the last column');
            }
        }
    }

    /**
     * Get seleted rows numbers
     * 
     * @return array
     */
    obj.getSelectedRows = function(asIds) {
        var rows = [];
        // Get all selected rows
        for (var j = 0; j < obj.rows.length; j++) {
            if (obj.rows[j].classList.contains('selected')) {
                if (asIds) {
                    rows.push(j);
                } else {
                    rows.push(obj.rows[j]);
                }
            }
        }

        return rows;
    },

    /**
     * Get seleted column numbers
     * 
     * @return array
     */
    obj.getSelectedColumns = function() {
        var cols = [];
        // Get all selected cols
        for (var i = 0; i < obj.headers.length; i++) {
            if (obj.headers[i].classList.contains('selected')) {
                cols.push(i);
            }
        }

        return cols;
    }

    /**
     * Get highlighted
     * 
     * @return array
     */
    obj.getHighlighted = function() {
        return obj.highlighted;
    }

    /**
     * Update cell references
     * 
     * @return void
     */
    obj.updateTableReferences = function() {
        // Update headers
        for (var i = 0; i < obj.headers.length; i++) {
            var x = obj.headers[i].getAttribute('data-x');

            if (x != i) {
                // Update coords
                obj.headers[i].setAttribute('data-x', i);
                // Title
                if (! obj.headers[i].getAttribute('title')) {
                    obj.headers[i].innerHTML = jexcel.getColumnName(i);
                }
            }
        }

        // Update all rows
        for (var j = 0; j < obj.rows.length; j++) {
            if (obj.rows[j]) {
                var y = obj.rows[j].getAttribute('data-y');

                if (y != j) {
                    // Update coords
                    obj.rows[j].setAttribute('data-y', j);
                    obj.rows[j].children[0].setAttribute('data-y', j);
                    // Row number
                    obj.rows[j].children[0].innerHTML = j + 1;
                }
            }
        }

        // Regular cells affected by this change
        var affectedTokens = [];
        var mergeCellUpdates = [];

        // Update cell
        var updatePosition = function(x,y,i,j) {
            if (x != i) {
                obj.records[j][i].setAttribute('data-x', i);
            }
            if (y != j) {
                obj.records[j][i].setAttribute('data-y', j);
            }

            // Other updates
            if (x != i || y != j) {
                var columnIdFrom = jexcel.getColumnNameFromId([x, y]);
                var columnIdTo = jexcel.getColumnNameFromId([i, j]);
                affectedTokens[columnIdFrom] = columnIdTo;
            }
        }

        for (var j = 0; j < obj.records.length; j++) {
            for (var i = 0; i < obj.records[0].length; i++) {
                if (obj.records[j][i]) {
                    // Current values
                    var x = obj.records[j][i].getAttribute('data-x');
                    var y = obj.records[j][i].getAttribute('data-y');

                    // Update column
                    if (obj.records[j][i].getAttribute('data-merged')) {
                        var columnIdFrom = jexcel.getColumnNameFromId([x, y]);
                        var columnIdTo = jexcel.getColumnNameFromId([i, j]);
                        if (mergeCellUpdates[columnIdFrom] == null) {
                            if (columnIdFrom == columnIdTo) {
                                mergeCellUpdates[columnIdFrom] = false;
                            } else {
                                var totalX = parseInt(i - x);
                                var totalY = parseInt(j - y);
                                mergeCellUpdates[columnIdFrom] = [ columnIdTo, totalX, totalY ];
                            }
                        }
                    } else {
                        updatePosition(x,y,i,j);
                    }
                }
            }
        }

        // Update merged if applicable
        var keys = Object.keys(mergeCellUpdates);
        if (keys.length) {
            for (var i = 0; i < keys.length; i++) {
                if (mergeCellUpdates[keys[i]]) {
                    var info = jexcel.getIdFromColumnName(keys[i], true)
                    var x = info[0];
                    var y = info[1];
                    updatePosition(x,y,x + mergeCellUpdates[keys[i]][1],y + mergeCellUpdates[keys[i]][2]);

                    var columnIdFrom = keys[i];
                    var columnIdTo = mergeCellUpdates[keys[i]][0];
                    for (var j = 0; j < obj.options.mergeCells[columnIdFrom][2].length; j++) {
                        var x = parseInt(obj.options.mergeCells[columnIdFrom][2][j].getAttribute('data-x'));
                        var y = parseInt(obj.options.mergeCells[columnIdFrom][2][j].getAttribute('data-y'));
                        obj.options.mergeCells[columnIdFrom][2][j].setAttribute('data-x', x + mergeCellUpdates[keys[i]][1]);
                        obj.options.mergeCells[columnIdFrom][2][j].setAttribute('data-y', y + mergeCellUpdates[keys[i]][2]);
                    }

                    obj.options.mergeCells[columnIdTo] = obj.options.mergeCells[columnIdFrom];
                    delete(obj.options.mergeCells[columnIdFrom]);
                }
            }
        }

        // Update formulas
        obj.updateFormulas(affectedTokens);

        // Update meta data
        obj.updateMeta(affectedTokens);

        // Refresh selection
        obj.refreshSelection();

        // Update table with custom configuration if applicable
        obj.updateTable();
    }

    /**
     * Custom settings for the cells
     */
    obj.updateTable = function() {
        // Check for spare
        if (obj.options.minSpareRows > 0) {
            var numBlankRows = 0;
            for (var j = obj.rows.length - 1; j >= 0; j--) {
                var test = false;
                for (var i = 0; i < obj.headers.length; i++) {
                    if (obj.options.data[j][i]) {
                        test = true;
                    }
                }
                if (test) {
                    break;
                } else {
                    numBlankRows++;
                }
            }

            if (obj.options.minSpareRows - numBlankRows > 0) {
                obj.insertRow(obj.options.minSpareRows - numBlankRows)
            }
        }

        if (obj.options.minSpareCols > 0) {
            var numBlankCols = 0;
            for (var i = obj.headers.length - 1; i >= 0 ; i--) {
                var test = false;
                for (var j = 0; j < obj.rows.length; j++) {
                    if (obj.options.data[j][i]) {
                        test = true;
                    }
                }
                if (test) {
                    break;
                } else {
                    numBlankCols++;
                }
            }

            if (obj.options.minSpareCols - numBlankCols > 0) {
                obj.insertColumn(obj.options.minSpareCols - numBlankCols)
            }
        }

        // Customizations by the developer
        if (typeof(obj.options.updateTable) == 'function') {
            if (obj.options.detachForUpdates) {
                el.removeChild(obj.content);
            }

            for (var j = 0; j < obj.rows.length; j++) {
                for (var i = 0; i < obj.headers.length; i++) {
                    obj.options.updateTable(el, obj.records[j][i], i, j, obj.options.data[j][i], obj.records[j][i].innerText, jexcel.getColumnNameFromId([i, j]));
                }
            }

            if (obj.options.detachForUpdates) {
                el.insertBefore(obj.content, obj.pagination);
            }
        }

        // Update corner position
        setTimeout(function() {
            obj.updateCornerPosition();
        },0);
    }

    /**
     * Show column
     */
    obj.showColumn = function(colNumber) {
        obj.headers[colNumber].style.display = '';
        obj.colgroup[colNumber].style.display = '';
        for (var j = 0; j < obj.options.data.length; j++) {
            obj.records[j][colNumber].style.display = '';
        }
    }

    /**
     * Hide column
     */
    obj.hideColumn = function(colNumber) {
        obj.headers[colNumber].style.display = 'none';
        obj.colgroup[colNumber].style.display = 'none';
        for (var j = 0; j < obj.options.data.length; j++) {
            obj.records[j][colNumber].style.display = 'none';
        }
    }

    /**
     * Show index column
     */
    obj.showIndex = function() {
        obj.table.classList.remove('jexcel_hidden_index');
    }

    /**
     * Hide index column
     */
    obj.hideIndex = function() {
        obj.table.classList.add('jexcel_hidden_index');
    }

    /**
     * Update all related cells in the chain
     */
    var chainLoopProtection = [];

    obj.updateFormulaChain = function(x, y, records) {
        var cellId = jexcel.getColumnNameFromId([x, y]);
        if (obj.formula[cellId] && obj.formula[cellId].length > 0) {
            if (chainLoopProtection[cellId]) {
                obj.records[y][x].innerHTML = '#ERROR';
                obj.formula[cellId] = '';
            } else {
                // Protection
                chainLoopProtection[cellId] = true;

                for (var i = 0; i < obj.formula[cellId].length; i++) {
                    var cell = jexcel.getIdFromColumnName(obj.formula[cellId][i], true);
                    // Update cell
                    var value = ''+obj.options.data[cell[1]][cell[0]];
                    if (value.substr(0,1) == '=') {
                        records.push(obj.updateCell(cell[0], cell[1], value, true));
                    } else {
                        // No longer a formula, remove from the chain
                        Object.keys(obj.formula)[i] = null;
                    }
                    obj.updateFormulaChain(cell[0], cell[1], records);
                }
            }
        }

        chainLoopProtection = [];
    }

    /**
     * Update formulas
     */
    obj.updateFormulas = function(referencesToUpdate) {
        // Update formulas
        for (var j = 0; j < obj.options.data.length; j++) {
            for (var i = 0; i < obj.options.data[0].length; i++) {
                var value = '' + obj.options.data[j][i];
                // Is formula
                if (value.substr(0,1) == '=') {
                    // Replace tokens
                    var newFormula = obj.updateFormula(value, referencesToUpdate);
                    if (newFormula != value) {
                        obj.options.data[j][i] = newFormula;
                    }
                }
            }
        }

        // Update formula chain
        var formula = [];
        var keys = Object.keys(obj.formula);
        for (var j = 0; j < keys.length; j++) {
            // Current key and values
            var key = keys[j];
            var value = obj.formula[key];
            // Update key
            if (referencesToUpdate[key]) {
                key = referencesToUpdate[key];
            }
            // Update values
            formula[key] = [];
            for (var i = 0; i < value.length; i++) {
                var letter = value[i];
                if (referencesToUpdate[letter]) {
                    letter = referencesToUpdate[letter];
                }
                formula[key].push(letter);
            }
        }
        obj.formula = formula;
    }

    /**
     * Update formula
     */
    obj.updateFormula = function(formula, referencesToUpdate) {
        var testLetter = /[A-Z]/;
        var testNumber = /[0-9]/;

        var newFormula = '';
        var letter = null;
        var number = null;
        var token = '';

        for (var index = 0; index < formula.length; index++) {
            if (testLetter.exec(formula[index])) {
                letter = 1;
                number = 0;
                token += formula[index];
            } else if (testNumber.exec(formula[index])) {
                number = letter ? 1 : 0;
                token += formula[index];
            } else {
                if (letter && number) {
                    token = referencesToUpdate[token] ? referencesToUpdate[token] : token;
                }
                newFormula += token;
                newFormula += formula[index];
                letter = 0;
                number = 0;
                token = '';
            }
        }

        if (token) {
            if (letter && number) {
                token = referencesToUpdate[token] ? referencesToUpdate[token] : token;
            }
            newFormula += token;
        }

        return newFormula;
    }

    /**
     * Parse formulas
     */
    obj.executeFormula = function(expression, x, y) {

        var formulaResults = [];
        var formulaLoopProtection = [];

        // Execute formula with loop protection
        var execute = function(expression, x, y) {
         // Parent column identification
            var parentId = jexcel.getColumnNameFromId([x, y]);

            // Code protection
            if (formulaLoopProtection[parentId]) {
                console.error('Reference loop detected');
                return '#ERROR';
            }

            formulaLoopProtection[parentId] = true;

            // Convert range tokens
            var tokensUpdate = function(tokens) {
                for (var index = 0; index < tokens.length; index++) {
                    var f = [];
                    var token = tokens[index].split(':');
                    var e1 = jexcel.getIdFromColumnName(token[0], true);
                    var e2 = jexcel.getIdFromColumnName(token[1], true);

                    if (e1[0] <= e2[0]) {
                        var x1 = e1[0];
                        var x2 = e2[0];
                    } else {
                        var x1 = e2[0];
                        var x2 = e1[0];
                    }

                    if (e1[1] <= e2[1]) {
                        var y1 = e1[1];
                        var y2 = e2[1];
                    } else {
                        var y1 = e2[1];
                        var y2 = e1[1];
                    }

                    for (var j = y1; j <= y2; j++) {
                        for (var i = x1; i <= x2; i++) {
                            f.push(jexcel.getColumnNameFromId([i, j]));
                        }
                    }

                    expression = expression.replace(tokens[index], f.join(','));
                }
            }

            var tokens = expression.match(/([A-Z]+[0-9]+)\:([A-Z]+[0-9]+)/g);
            if (tokens && tokens.length) {
                tokensUpdate(tokens);
            }

            // String
            var evalstring = '';

            // Get tokens
            var tokens = expression.match(/([A-Z]+[0-9]+)/g);

            // Direct self-reference protection
            if (tokens && tokens.indexOf(parentId) > -1) {
                console.error('Self Reference detected');
                return '#ERROR';
            } else {
                if (tokens) {
                    for (var i = 0; i < tokens.length; i++) {
                        // Keep chain
                        if (! obj.formula[tokens[i]]) {
                            obj.formula[tokens[i]] = [];
                        }
                        // Is already in the register
                        if (obj.formula[tokens[i]].indexOf(parentId) < 0) {
                            obj.formula[tokens[i]].push(parentId);
                        }

                        // Do not calculate again
                        if (eval('typeof(' + tokens[i] + ') == "undefined"')) {
                            // Coords
                            var position = jexcel.getIdFromColumnName(tokens[i], 1);
                            // Get value
                            if (typeof(obj.options.data[position[1]]) != 'undefined' && typeof(obj.options.data[position[1]][position[0]]) != 'undefined') {
                                var value = obj.options.data[position[1]][position[0]];
                            } else {
                                var value = '';
                            }
                            // Get column data
                            if ((''+value).substr(0,1) == '=') {
                                if (formulaResults[tokens[i]]) {
                                    value = formulaResults[tokens[i]];
                                } else {
                                    value = execute(value, position[0], position[1]);
                                    formulaResults[tokens[i]] = value;
                                }
                            }
                            // Type!
                            if ((''+value).trim() == '') {
                                // Null
                                evalstring += "var " + tokens[i] + " = null;";
                            } else {
                                if (value == Number(value) && obj.options.autoCasting == true) {
                                    // Number
                                    evalstring += "var " + tokens[i] + " = " + Number(value) + ";";
                                } else {
                                    // Trying any formatted number
                                    var number = obj.parseNumber(value, position[0])
                                    if (obj.options.autoCasting == true && number) {
                                        // Render as number
                                        evalstring += "var " + tokens[i] + " = " + number + ";";
                                    } else {
                                        // Render as string
                                        evalstring += "var " + tokens[i] + " = '" + value + "';";
                                    }
                                }
                            }
                        }
                    }
                }

                // Convert formula to javascript
                try {
                    evalstring += "function COLUMN() { return parseInt(x) + 1; }; function ROW() { return parseInt(y) + 1; }; function CELL() { return parentId; };";

                    var res = eval(evalstring + expression.substr(1));
                } catch (e) {
                    var res = '#ERROR';
                }

                return res;
            }
        }

        return execute(expression, x, y);
    }

    /**
     * Trying to extract a number from a string
     */
    obj.parseNumber = function(value, columnNumber) {
        // Decimal point
        var decimal = columnNumber && obj.options.columns[columnNumber].decimal ? obj.options.columns[columnNumber].decimal : '.';

        // Parse both parts of the number
        var number = ('' + value);
        number = number.split(decimal);
        number[0] = number[0].match(/[+-]?[0-9]/g);
        if (number[0]) {
            number[0] = number[0].join('');
        }
        if (number[1]) {
            number[1] = number[1].match(/[0-9]*/g).join('');
        }

        // Is a valid number
        if (number[0] && Number(number[0]) >= 0) {
            if (! number[1]) {
                var value = Number(number[0] + '.00');
            } else {
                var value = Number(number[0] + '.' + number[1]);
            }
        } else {
            var value = null;
        }

        return value;
    }

    /**
     * Get row number
     */
    obj.row = function(cell) {
    }

    /**
     * Get col number
     */
    obj.col = function(cell) {
    }

    obj.up = function(shiftKey, ctrlKey) {
        if (shiftKey) {
            if (obj.selectedCell[3] > 0) {
                obj.up.visible(1, ctrlKey ? 0 : 1)
            }
        } else {
            if (obj.selectedCell[1] > 0) {
                obj.up.visible(0, ctrlKey ? 0 : 1)
            }
            obj.selectedCell[2] = obj.selectedCell[0];
            obj.selectedCell[3] = obj.selectedCell[1];
        }

        // Update selection
        obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);

        // Change page
        if (obj.options.lazyLoading == true) {
            if (obj.selectedCell[1] == 0 || obj.selectedCell[3] == 0) {
                obj.loadPage(0);
                obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
            } else {
                if (obj.loadValidation()) {
                    obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
                } else {
                    var item = parseInt(obj.tbody.firstChild.getAttribute('data-y'));
                    if (obj.selectedCell[1] - item < 30) {
                        obj.loadUp();
                        obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
                    }
                }
            }
        } else if (obj.options.pagination > 0) {
            var pageNumber = obj.whichPage(obj.selectedCell[3]);
            if (pageNumber != obj.pageNumber) {
                obj.page(pageNumber);
            }
        }

        obj.updateScroll(1);
    }

    obj.up.visible = function(group, direction) {
        if (group == 0) {
            var x = parseInt(obj.selectedCell[0]);
            var y = parseInt(obj.selectedCell[1]);
        } else {
            var x = parseInt(obj.selectedCell[2]);
            var y = parseInt(obj.selectedCell[3]);
        }

        if (direction == 0) {
            for (var j = 0; j < y; j++) {
                if (obj.records[j][x].style.display != 'none' && obj.rows[j].style.display != 'none') {
                    y = j;
                    break;
                }
            }
        } else {
            y = obj.up.get(x, y);
        }

        if (group == 0) {
            obj.selectedCell[0] = x;
            obj.selectedCell[1] = y;
        } else {
            obj.selectedCell[2] = x;
            obj.selectedCell[3] = y;
        }
    }

    obj.up.get = function(x, y) {
        var x = parseInt(x);
        var y = parseInt(y);
        for (var j = (y - 1); j >= 0; j--) {
            if (obj.records[j][x].style.display != 'none' && obj.rows[j].style.display != 'none') {
                if (obj.records[j][x].getAttribute('data-merged')) {
                    if (obj.records[j][x] == obj.records[y][x]) {
                        continue;
                    }
                }
                y = j;
                break;
            }
        }

        return y;
    }

    obj.down = function(shiftKey, ctrlKey) {
        if (shiftKey) {
            if (obj.selectedCell[3] < obj.records.length - 1) {
                obj.down.visible(1, ctrlKey ? 0 : 1)
            }
        } else {
            if (obj.selectedCell[1] < obj.records.length - 1) {
                obj.down.visible(0, ctrlKey ? 0 : 1)
            }
            obj.selectedCell[2] = obj.selectedCell[0];
            obj.selectedCell[3] = obj.selectedCell[1];
        }

        obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);

        // Change page
        if (obj.options.lazyLoading == true) {
            if ((obj.selectedCell[1] == obj.records.length - 1 || obj.selectedCell[3] == obj.records.length - 1)) {
                obj.loadPage(-1);
                obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
            } else {
                if (obj.loadValidation()) {
                    obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
                } else {
                    var item = parseInt(obj.tbody.lastChild.getAttribute('data-y'));
                    if (item - obj.selectedCell[3] < 30) {
                        obj.loadDown();
                        obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
                    }
                }
            }
        } else if (obj.options.pagination > 0) {
            var pageNumber = obj.whichPage(obj.selectedCell[3]);
            if (pageNumber != obj.pageNumber) {
                obj.page(pageNumber);
            }
        }

        obj.updateScroll(3);
    }

    obj.down.visible = function(group, direction) {
        if (group == 0) {
            var x = parseInt(obj.selectedCell[0]);
            var y = parseInt(obj.selectedCell[1]);
        } else {
            var x = parseInt(obj.selectedCell[2]);
            var y = parseInt(obj.selectedCell[3]);
        }

        if (direction == 0) {
            for (var j = obj.rows.length - 1; j > y; j--) {
                if (obj.records[j][x].style.display != 'none' && obj.rows[j].style.display != 'none') {
                    y = j;
                    break;
                }
            }
        } else {
            y = obj.down.get(x, y);
        }

        if (group == 0) {
            obj.selectedCell[0] = x;
            obj.selectedCell[1] = y;
        } else {
            obj.selectedCell[2] = x;
            obj.selectedCell[3] = y;
        }
    }

    obj.down.get = function(x, y) {
        var x = parseInt(x);
        var y = parseInt(y);
        for (var j = (y + 1); j < obj.rows.length; j++) {
            if (obj.records[j][x].style.display != 'none' && obj.rows[j].style.display != 'none') {
                if (obj.records[j][x].getAttribute('data-merged')) {
                    if (obj.records[j][x] == obj.records[y][x]) {
                        continue;
                    }
                }
                y = j;
                break;
            }
        }

        return y;
    }

    obj.right = function(shiftKey, ctrlKey) {
        if (shiftKey) {
            if (obj.selectedCell[2] < obj.headers.length - 1) {
                obj.right.visible(1, ctrlKey ? 0 : 1)
            }
        } else {
            if (obj.selectedCell[0] < obj.headers.length - 1) {
                obj.right.visible(0, ctrlKey ? 0 : 1)
            }
            obj.selectedCell[2] = obj.selectedCell[0];
            obj.selectedCell[3] = obj.selectedCell[1];
        }

        obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
        obj.updateScroll(2);
    }

    obj.right.visible = function(group, direction) {
        if (group == 0) {
            var x = parseInt(obj.selectedCell[0]);
            var y = parseInt(obj.selectedCell[1]);
        } else {
            var x = parseInt(obj.selectedCell[2]);
            var y = parseInt(obj.selectedCell[3]);
        }

        if (direction == 0) {
            for (var i = obj.headers.length - 1; i > x; i--) {
                if (obj.records[y][i].style.display != 'none') {
                    x = i;
                    break;
                }
            }
        } else {
            x = obj.right.get(x, y);
        }

        if (group == 0) {
            obj.selectedCell[0] = x;
            obj.selectedCell[1] = y;
        } else {
            obj.selectedCell[2] = x;
            obj.selectedCell[3] = y;
        }
    }

    obj.right.get = function(x, y) {
        var x = parseInt(x);
        var y = parseInt(y);

        for (var i = (x + 1); i < obj.headers.length; i++) {
            if (obj.records[y][i].style.display != 'none') {
                if (obj.records[y][i].getAttribute('data-merged')) {
                    if (obj.records[y][i] == obj.records[y][x]) {
                        continue;
                    }
                }
                x = i;
                break;
            }
        }

        return x;
    }

    obj.left = function(shiftKey, ctrlKey) {
        if (shiftKey) {
            if (obj.selectedCell[2] > 0) {
                obj.left.visible(1, ctrlKey ? 0 : 1)
            }
        } else {
            if (obj.selectedCell[0] > 0) {
                obj.left.visible(0, ctrlKey ? 0 : 1)
            }
            obj.selectedCell[2] = obj.selectedCell[0];
            obj.selectedCell[3] = obj.selectedCell[1];
        }

        obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
        obj.updateScroll(0);
    }

    obj.left.visible = function(group, direction) {
        if (group == 0) {
            var x = parseInt(obj.selectedCell[0]);
            var y = parseInt(obj.selectedCell[1]);
        } else {
            var x = parseInt(obj.selectedCell[2]);
            var y = parseInt(obj.selectedCell[3]);
        }

        if (direction == 0) {
            for (var i = 0; i < x; i++) {
                if (obj.records[y][i].style.display != 'none') {
                    x = i;
                    break;
                }
            }
        } else {
            x = obj.left.get(x, y);
        }

        if (group == 0) {
            obj.selectedCell[0] = x;
            obj.selectedCell[1] = y;
        } else {
            obj.selectedCell[2] = x;
            obj.selectedCell[3] = y;
        }
    }

    obj.left.get = function(x, y) {
        var x = parseInt(x);
        var y = parseInt(y);
        for (var i = (x - 1); i >= 0; i--) {
            if (obj.records[y][i].style.display != 'none') {
                if (obj.records[y][i].getAttribute('data-merged')) {
                    if (obj.records[y][i] == obj.records[y][x]) {
                        continue;
                    }
                }
                x = i;
                break;
            }
        }

        return x;
    }

    obj.first = function(shiftKey, ctrlKey) {
        if (shiftKey) {
            if (ctrlKey) {
                obj.selectedCell[3] = 0;
            } else {
                obj.left.visible(1, 0);
            }
        } else {
            if (ctrlKey) {
                obj.selectedCell[1] = 0;
            } else {
                obj.left.visible(0, 0);
            }
            obj.selectedCell[2] = obj.selectedCell[0];
            obj.selectedCell[3] = obj.selectedCell[1];
        }

        // Change page
        if (obj.options.lazyLoading == true && (obj.selectedCell[1] == 0 || obj.selectedCell[3] == 0)) {
            obj.loadPage(0);
        } else if (obj.options.pagination > 0) {
            var pageNumber = obj.whichPage(obj.selectedCell[3]);
            if (pageNumber != obj.pageNumber) {
                obj.page(pageNumber);
            }
        }

        obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
        obj.updateScroll(1);
    }

    obj.last = function(shiftKey, ctrlKey) {
        if (shiftKey) {
            if (ctrlKey) {
                obj.selectedCell[3] = obj.records.length - 1;
            } else {
                obj.right.visible(1, 0);
            }
        } else {
            if (ctrlKey) {
                obj.selectedCell[1] = obj.records.length - 1;
            } else {
                obj.right.visible(0, 0);
            }
            obj.selectedCell[2] = obj.selectedCell[0];
            obj.selectedCell[3] = obj.selectedCell[1];
        }

        // Change page
        if (obj.options.lazyLoading == true && (obj.selectedCell[1] == obj.records.length - 1 || obj.selectedCell[3] == obj.records.length - 1)) {
            obj.loadPage(-1);
        } else if (obj.options.pagination > 0) {
            var pageNumber = obj.whichPage(obj.selectedCell[3]);
            if (pageNumber != obj.pageNumber) {
                obj.page(pageNumber);
            }
        }

        obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
        obj.updateScroll(3);
    }

    obj.selectAll = function() {
        if (! obj.selectedCell) {
            obj.selectedCell = [];
        }

        obj.selectedCell[0] = 0;
        obj.selectedCell[1] = 0;
        obj.selectedCell[2] = obj.headers.length - 1;
        obj.selectedCell[3] = obj.records.length - 1;

        obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
    }

    /**
     * Go to a page in a lazyLoading
     */
    obj.loadPage = function(pageNumber) {
        // Search
        if (obj.options.search == true && obj.results) {
            var results = obj.results;
        } else {
            var results = obj.rows;
        }

        // Per page
        var quantityPerPage = 100;

        // pageNumber
        if (pageNumber == null || pageNumber == -1) {
            // Last page
            pageNumber = Math.ceil(results.length / quantityPerPage); 
        }

        var startRow = (pageNumber * quantityPerPage);
        var finalRow = (pageNumber * quantityPerPage) + quantityPerPage;
        if (finalRow > results.length) {
            finalRow = results.length;
        }
        startRow = finalRow - 100;
        if (startRow < 0) {
            startRow = 0;
        }

        // Appeding items
        for (var j = startRow; j < finalRow; j++) {
            if (obj.options.search == true && obj.results) {
                obj.tbody.appendChild(obj.rows[results[j]]);
            } else {
                obj.tbody.appendChild(obj.rows[j]);
            }

            if (obj.tbody.children.length > quantityPerPage) {
                obj.tbody.removeChild(obj.tbody.firstChild);
            }
        }
    }

    obj.loadUp = function() {
        // Search
        if (obj.options.search == true && obj.results) {
            var results = obj.results;
        } else {
            var results = obj.rows;
        }
        var test = 0;
        if (results.length > 100) {
            // Get the first element in the page
            var item = parseInt(obj.tbody.firstChild.getAttribute('data-y'));
            if (obj.options.search == true && obj.results) {
                item = results.indexOf(item);
            }
            if (item > 0) {
                for (var j = 0; j < 30; j++) {
                    item = item - 1;
                    if (item > -1) {
                        if (obj.options.search == true && obj.results) {
                            obj.tbody.insertBefore(obj.rows[results[item]], obj.tbody.firstChild);
                        } else {
                            obj.tbody.insertBefore(obj.rows[item], obj.tbody.firstChild);
                        }
                        if (obj.tbody.children.length > 100) {
                            obj.tbody.removeChild(obj.tbody.lastChild);
                            test = 1;
                        }
                    }
                }
            }
        }
        return test;
    }

    obj.loadDown = function() {
        // Search
        if (obj.options.search == true && obj.results) {
            var results = obj.results;
        } else {
            var results = obj.rows;
        }
        var test = 0;
        if (results.length > 100) {
            // Get the last element in the page
            var item = parseInt(obj.tbody.lastChild.getAttribute('data-y'));
            if (obj.options.search == true && obj.results) {
                item = results.indexOf(item);
            }
            if (item < obj.rows.length - 1) {
                for (var j = 0; j <= 30; j++) {
                    if (item < results.length) {
                        if (obj.options.search == true && obj.results) {
                            obj.tbody.appendChild(obj.rows[results[item]]);
                        } else {
                            obj.tbody.appendChild(obj.rows[item]);
                        }
                        if (obj.tbody.children.length > 100) {
                            obj.tbody.removeChild(obj.tbody.firstChild);
                            test = 1;
                        }
                    }
                    item = item + 1;
                }
            }
        }

        return test;
    }

    obj.loadValidation = function() {
        if (obj.selectedCell) {
            var currentPage = parseInt(obj.tbody.firstChild.getAttribute('data-y')) / 100;
            var selectedPage = parseInt(obj.selectedCell[3] / 100);
            var totalPages = parseInt(obj.rows.length / 100);

            if (currentPage != selectedPage && selectedPage <= totalPages) {
                if (! Array.prototype.indexOf.call(obj.tbody.children, obj.rows[obj.selectedCell[3]])) {
                    obj.loadPage(selectedPage);
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Reset search
     */
    obj.resetSearch = function() {
        obj.searchInput.value = '';
        obj.search('');
        obj.results = null;
    }

    /**
     * Search
     */
    obj.search = function(query) {
        // Query
        if (query) {
            var query = query.toLowerCase();
        }

        // Reset selection
        obj.resetSelection();

        // Total of results
        obj.pageNumber = 0;
        obj.results = [];

        if (query) {
            // Search filter
            var search = function(item, query, index) {
                for (var i = 0; i < item.length; i++) {
                    if ((''+item[i]).toLowerCase().search(query) >= 0 ||
                        (''+obj.records[index][i].innerHTML).toLowerCase().search(query) >= 0) {
                        return true;
                    }
                }
                return false;
            }

            // Result
            var addToResult = function(k) {
                if (obj.results.indexOf(k) == -1) {
                    obj.results.push(k);
                }
            }

            // Filter
            var data = obj.options.data.filter(function(v, k) {
                if (search(v, query, k)) {
                    // Merged rows found
                    var rows = obj.isRowMerged(k);
                    if (rows.length) {
                        for (var i = 0; i < rows.length; i++) {
                            var row = jexcel.getIdFromColumnName(rows[i], true);
                            for (var j = 0; j < obj.options.mergeCells[rows[i]][1]; j++) {
                                addToResult(row[1]+j);
                            }
                        }
                    } else {
                        // Normal row found
                        addToResult(k);
                    }
                    return true;
                } else {
                    return false;
                }
            });
        } else {
            obj.results = null;
        }

        var total = 0;
        var index = 0;

        // Page 1
        if (obj.options.lazyLoading == true) {
            total = 100;
        } else if (obj.options.pagination > 0) {
            total = obj.options.pagination;
        } else {
            if (obj.results) {
                total = obj.results.length;
            } else {
                total = obj.rows.length;
            }
        }

        // Reset current nodes
        while (obj.tbody.firstChild) {
            obj.tbody.removeChild(obj.tbody.firstChild);
        }

        // Hide all records from the table
        for (var j = 0; j < obj.rows.length; j++) {
            if (! obj.results || obj.results.indexOf(j) > -1) {
                if (index < total) {
                    obj.tbody.appendChild(obj.rows[j]);
                    index++;
                }
                obj.rows[j].style.display = '';
            } else {
                obj.rows[j].style.display = 'none';
            }
        }

        // Update pagination
        if (obj.options.pagination > 0) {
            obj.updatePagination();
        }

        return total;
    }

    /**
     * Which page the cell is
     */
    obj.whichPage = function(cell) {
        // Search
        if (obj.options.search == true && obj.results) {
            cell = obj.results.indexOf(cell);
        }

        return (Math.ceil((parseInt(cell) + 1) / parseInt(obj.options.pagination))) - 1;
    }

    /**
     * Go to page
     */
    obj.page = function(pageNumber) {
        var oldPage = obj.pageNumber;

        // Search
        if (obj.options.search == true && obj.results) {
            var results = obj.results;
        } else {
            var results = obj.rows;
        }

        // Per page
        var quantityPerPage = parseInt(obj.options.pagination);

        // pageNumber
        if (pageNumber == null || pageNumber == -1) {
            // Last page
            pageNumber = Math.ceil(results.length / quantityPerPage); 
        }

        // Page number
        obj.pageNumber = pageNumber;

        var startRow = (pageNumber * quantityPerPage);
        var finalRow = (pageNumber * quantityPerPage) + quantityPerPage;
        if (finalRow > results.length) {
            finalRow = results.length;
        }
        if (startRow < 0) {
            startRow = 0;
        }

        // Reset container
        while (obj.tbody.firstChild) {
            obj.tbody.removeChild(obj.tbody.firstChild);
        }

        // Appeding items
        for (var j = startRow; j < finalRow; j++) {
            if (obj.options.search == true && obj.results) {
                obj.tbody.appendChild(obj.rows[results[j]]);
            } else {
                obj.tbody.appendChild(obj.rows[j]);
            }
        }

        if (obj.options.pagination > 0) {
            obj.updatePagination();
        }

        // Update corner position
        obj.updateCornerPosition();

        if (typeof(obj.options.onchangepage) == 'function') {
            obj.options.onchangepage(el, pageNumber, oldPage);
        }
    }

    /**
     * Update the pagination
     */
    obj.updatePagination = function() {
        // Reset container
        obj.pagination.children[0].innerHTML = '';
        obj.pagination.children[1].innerHTML = '';

        // Start pagination
        if (obj.options.pagination) {
            // Searchable
            if (obj.options.search == true && obj.results) {
                var results = obj.results.length;
            } else {
                var results = obj.rows.length;
            }

            if (! results) {
                // No records found
                obj.pagination.children[0].innerHTML = obj.options.text.noRecordsFound;
            } else {
                // Pagination container
                var quantyOfPages = Math.ceil(results / obj.options.pagination);

                if (obj.pageNumber < 6) {
                    var startNumber = 1;
                    var finalNumber = quantyOfPages < 10 ? quantyOfPages : 10;
                } else if (quantyOfPages - obj.pageNumber < 5) {
                    var startNumber = quantyOfPages - 9;
                    var finalNumber = quantyOfPages;
                    if (startNumber < 1) {
                        startNumber = 1;
                    }
                } else {
                    var startNumber = obj.pageNumber - 4;
                    var finalNumber = obj.pageNumber + 5;
                }

                // First
                if (startNumber > 1) {
                    var paginationItem = document.createElement('div');
                    paginationItem.className = 'jexcel_page';
                    paginationItem.innerHTML = '<';
                    paginationItem.title = 1;
                    obj.pagination.children[1].appendChild(paginationItem);
                }

                // Get page links
                for (var i = startNumber; i <= finalNumber; i++) {
                    var paginationItem = document.createElement('div');
                    paginationItem.className = 'jexcel_page';
                    paginationItem.innerHTML = i;
                    obj.pagination.children[1].appendChild(paginationItem);

                    if (obj.pageNumber == (i-1)) {
                        paginationItem.classList.add('jexcel_page_selected');
                    }
                }

                // Last
                if (finalNumber < quantyOfPages) {
                    var paginationItem = document.createElement('div');
                    paginationItem.className = 'jexcel_page';
                    paginationItem.innerHTML = '>';
                    paginationItem.title = quantyOfPages;
                    obj.pagination.children[1].appendChild(paginationItem);
                }

                // Text
                var format = function(format) {
                    var args = Array.prototype.slice.call(arguments, 1);
                    return format.replace(/{(\d+)}/g, function(match, number) {
                      return typeof args[number] != 'undefined'
                        ? args[number]
                        : match
                      ;
                    });
                };

                obj.pagination.children[0].innerHTML = format(obj.options.text.showingPage, obj.pageNumber + 1, quantyOfPages)
            }
        }
    }

    /**
     * Download CSV table
     * 
     * @return null
     */
    obj.download = function(includeHeaders) {
        if (obj.options.allowExport == false) {
            console.error('Export not allowed');
        } else {
            // Data
            var data = '';
            if (includeHeaders == true || obj.options.includeHeadersOnDownload == true) {
                data += obj.getHeaders();
                data += "\r\n";
            }

            // Get data
            data += obj.copy(false, obj.options.csvDelimiter, true);

            // Download element
            var blob = new Blob(["\uFEFF"+data], {type: 'text/csv;charset=utf-8;'});

            // IE Compatibility
            if (window.navigator && window.navigator.msSaveOrOpenBlob) {
                window.navigator.msSaveOrOpenBlob(blob, options.csvFileName + '.csv');
            } else {
                // Download element
                var pom = document.createElement('a');
                var url = URL.createObjectURL(blob);
                pom.href = url;
                pom.setAttribute('download', obj.options.csvFileName + '.csv');
                document.body.appendChild(pom);
                pom.click();
                pom.parentNode.removeChild(pom);
            }
        }
    }

    /**
     * Initializes a new history record for undo/redo
     * 
     * @return null
     */
    obj.setHistory = function(changes) {
        if (obj.ignoreHistory != true) {
            // Increment and get the current history index
            var index = ++obj.historyIndex;

            // Slice the array to discard undone changes
            obj.history = (obj.history = obj.history.slice(0, index + 1));

            // Keep history
            obj.history[index] = changes;
        }
    }

    /**
     * Copy method
     * 
     * @param bool highlighted - Get only highlighted cells
     * @param delimiter - \t default to keep compatibility with excel
     * @return string value
     */
    obj.copy = function(highlighted, delimiter, returnData) {
        if (! delimiter) {
            delimiter = "\t";
        }

        // Controls
        var col = [];
        var colLabel = [];
        var row = [];
        var rowLabel = [];
        var x = obj.options.data[0].length
        var y = obj.options.data.length
        var tmp = '';

        // Reset container
        obj.style = [];

        // Go through the columns to get the data
        for (var j = 0; j < y; j++) {
            col = [];
            colLabel = [];

            for (var i = 0; i < x; i++) {
                // If cell is highlighted
                if (! highlighted || obj.records[j][i].classList.contains('highlight')) {
                    // Values
                    var value = obj.options.data[j][i];
                    if (value.match && (value.match(/,/g) || value.match(/\n/) || value.match(/\"/))) {
                        value = value.replace(new RegExp('"', 'g'), '""');
                        value = '"' + value + '"';
                    }
                    col.push(value);

                    // Labels
                    if (obj.options.columns[i].type == 'checkbox' || obj.options.columns[i].type == 'radio') {
                        var label = value;
                    } else {
                        var label = obj.records[j][i].innerHTML;
                        if (label.match && (label.match(/,/g) || label.match(/\n/) || label.match(/\"/))) {
                            // Scape double quotes
                            label = label.replace(new RegExp('"', 'g'), '""');
                            label = '"' + label + '"';
                        }
                    }
                    colLabel.push(label);

                    // Get style
                    tmp = obj.records[j][i].getAttribute('style');
                    obj.style.push(tmp ? tmp : '');
                }
            }

            if (col.length) {
                row.push(col.join(delimiter));
            }
            if (colLabel.length) {
                rowLabel.push(colLabel.join(delimiter));
            }
        }

        // Final string
        var str = row.join("\r\n");
        var strLabel = rowLabel.join("\r\n");

        // Create a hidden textarea to copy the values
        if (! returnData) {
            if (obj.options.copyCompatibility == true) {
                obj.textarea.value = strLabel;
            } else {
                obj.textarea.value = str;
            }
            obj.textarea.select();
            document.execCommand("copy");
        }

        // Keep data
        if (obj.options.copyCompatibility == true) {
            obj.data = strLabel;
        } else {
        obj.data = str;
        }
        // Keep non visible information
        obj.hashString = obj.hash(obj.data);

        return obj.data;
    }

    /**
     * jExcel paste method
     * 
     * @param integer row number
     * @return string value
     */
    obj.paste = function(x, y, data) {
        // Paste filter
        if (typeof(obj.options.onbeforepaste) == 'function') {
            var data = obj.options.onbeforepaste(el, data);
        }

        // Controls
        var hash = obj.hash(data);
        var style = (hash == obj.hashString) ? obj.style : null;

        // Depending on the behavior
        if (obj.options.copyCompatibility == true && hash == obj.hashString) {
            var data = obj.data;
        }

        // Split new line
        var data = obj.parseCSV(data, "\t");

        if (x != null && y != null && data) {
            // Records
            var i = 0;
            var j = 0;
            var records = [];
            var newStyle = {};
            var oldStyle = {};
            var styleIndex = 0;

            // Index
            var colIndex = parseInt(x);
            var rowIndex = parseInt(y);
            var row = null;

            // Go through the columns to get the data
            while (row = data[j]) {
                i = 0;
                colIndex = parseInt(x);

                while (row[i] != null) {
                    // Update and keep history
                    var record = obj.updateCell(colIndex, rowIndex, row[i]);
                    // Keep history
                    records.push(record);
                    // Update all formulas in the chain
                    obj.updateFormulaChain(colIndex, rowIndex, records);
                    // Style
                    if (style && style[styleIndex]) {
                        var columnName = jexcel.getColumnNameFromId([colIndex, rowIndex]);
                        newStyle[columnName] = style[styleIndex];
                        oldStyle[columnName] = obj.getStyle(columnName);
                        obj.records[rowIndex][colIndex].setAttribute('style', style[styleIndex]);
                        styleIndex++
                    }
                    i++;
                    if (row[i] != null) {
                        if (colIndex >= obj.headers.length - 1) {
                            obj.insertColumn();
                        }
                        colIndex = obj.right.get(colIndex, rowIndex);
                    }
                }

                j++;
                if (data[j]) {
                    if (rowIndex >= obj.rows.length-1) {
                        obj.insertRow();
                    }
                    rowIndex = obj.down.get(x, rowIndex);
                }
            }

            // Select the new cells
            obj.updateSelectionFromCoords(x, y, colIndex, rowIndex);

            // Update history
            obj.setHistory({
                action:'setValue',
                records:records,
                selection:obj.selectedCell,
                newStyle:newStyle,
                oldStyle:oldStyle,
            });

            // Update table
            obj.updateTable();

            // Paste event
            if (typeof(obj.options.onpaste) == 'function') {
                obj.options.onpaste(el, records);
            }

            // On after changes
            obj.onafterchanges(el, records);
        }
    }

    /**
     * Process row
     */
    obj.historyProcessRow = function(type, historyRecord) {
        var rowIndex = (! historyRecord.insertBefore) ? historyRecord.rowNumber + 1 : historyRecord.rowNumber;

        if (obj.options.search == true) {
            if (obj.results && obj.results.length != obj.rows.length) {
                obj.resetSearch();
            }
        }

        // Remove row
        if (type == 1) {
            var numOfRows = historyRecord.numOfRows;
            // Remove nodes
            for (var j = rowIndex; j < (numOfRows + rowIndex); j++) {
                obj.rows[j].parentNode.removeChild(obj.rows[j]);
            }
            // Remove references
            obj.records.splice(rowIndex, numOfRows);
            obj.options.data.splice(rowIndex, numOfRows);
            obj.rows.splice(rowIndex, numOfRows);

            obj.conditionalSelectionUpdate(1, rowIndex, (numOfRows + rowIndex) - 1);
        } else {
            // Insert data
            obj.records = jexcel.injectArray(obj.records, rowIndex, historyRecord.rowRecords);
            obj.options.data = jexcel.injectArray(obj.options.data, rowIndex, historyRecord.rowData);
            obj.rows = jexcel.injectArray(obj.rows, rowIndex, historyRecord.rowNode);
            // Insert nodes
            var index = 0
            for (var j = rowIndex; j < (historyRecord.numOfRows + rowIndex); j++) {
                obj.tbody.insertBefore(historyRecord.rowNode[index], obj.tbody.children[j]);
                index++;
            }
        }

        // Respect pagination
        if (obj.options.pagination > 0) {
            obj.page(obj.pageNumber);
        }

        obj.updateTableReferences();
    }

    /**
     * Process column
     */
    obj.historyProcessColumn = function(type, historyRecord) {
        var columnIndex = (! historyRecord.insertBefore) ? historyRecord.columnNumber + 1 : historyRecord.columnNumber;

        // Remove column
        if (type == 1) {
            var numOfColumns = historyRecord.numOfColumns;

            obj.options.columns.splice(columnIndex, numOfColumns);
            for (var i = columnIndex; i < (numOfColumns + columnIndex); i++) {
                obj.headers[i].parentNode.removeChild(obj.headers[i]);
                obj.colgroup[i].parentNode.removeChild(obj.colgroup[i]);
            }
            obj.headers.splice(columnIndex, numOfColumns);
            obj.colgroup.splice(columnIndex, numOfColumns);
            for (var j = 0; j < historyRecord.data.length; j++) {
                for (var i = columnIndex; i < (numOfColumns + columnIndex); i++) {
                    obj.records[j][i].parentNode.removeChild(obj.records[j][i]);
                }
                obj.records[j].splice(columnIndex, numOfColumns);
                obj.options.data[j].splice(columnIndex, numOfColumns);
            }

            obj.conditionalSelectionUpdate(0, columnIndex, (numOfColumns + columnIndex) - 1);
        } else {
            // Insert data
            obj.options.columns = jexcel.injectArray(obj.options.columns, columnIndex, historyRecord.columns);
            obj.headers = jexcel.injectArray(obj.headers, columnIndex, historyRecord.headers);
            obj.colgroup = jexcel.injectArray(obj.colgroup, columnIndex, historyRecord.colgroup);

            var index = 0
            for (var i = columnIndex; i < (historyRecord.numOfColumns + columnIndex); i++) {
                obj.headerContainer.insertBefore(historyRecord.headers[index], obj.headerContainer.children[i+1]);
                obj.colgroupContainer.insertBefore(historyRecord.colgroup[index], obj.colgroupContainer.children[i+1]);
                index++;
            }

            for (var j = 0; j < historyRecord.data.length; j++) {
                obj.options.data[j] = jexcel.injectArray(obj.options.data[j], columnIndex, historyRecord.data[j]);
                obj.records[j] = jexcel.injectArray(obj.records[j], columnIndex, historyRecord.records[j]);
                var index = 0
                for (var i = columnIndex; i < (historyRecord.numOfColumns + columnIndex); i++) {
                    obj.rows[j].insertBefore(historyRecord.records[j][index], obj.rows[j].children[i+1]);
                    index++;
                }
            }
        }

        // Adjust nested headers
        if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0) {
            // Flexible way to handle nestedheaders
            if (obj.options.nestedHeaders[0] && obj.options.nestedHeaders[0][0]) {
                for (var j = 0; j < obj.options.nestedHeaders.length; j++) {
                    if (type == 1) {
                        var colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan) - historyRecord.numOfColumns;
                    } else {
                        var colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan) + historyRecord.numOfColumns;
                    }
                    obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan = colspan;
                    obj.thead.children[j].children[obj.thead.children[j].children.length-1].setAttribute('colspan', colspan);
                }
            } else {
                if (type == 1) {
                    var colspan = parseInt(obj.options.nestedHeaders[0].colspan) - historyRecord.numOfColumns;
                } else {
                    var colspan = parseInt(obj.options.nestedHeaders[0].colspan) + historyRecord.numOfColumns;
                }
                obj.options.nestedHeaders[0].colspan = colspan;
                obj.thead.children[0].children[obj.thead.children[0].children.length-1].setAttribute('colspan', colspan);
            }
        }

        obj.updateTableReferences();
    }

    /**
     * Undo last action
     */
    obj.undo = function() {
        // Ignore events and history
        var ignoreEvents = obj.ignoreEvents ? true : false;
        var ignoreHistory = obj.ignoreHistory ? true : false;

        obj.ignoreEvents = true;
        obj.ignoreHistory = true;

        // Records
        var records = [];

        // Update cells
        if (obj.historyIndex >= 0) {
            // History
            var historyRecord = obj.history[obj.historyIndex--];

            if (historyRecord.action == 'insertRow') {
                obj.historyProcessRow(1, historyRecord);
            } else if (historyRecord.action == 'deleteRow') {
                obj.historyProcessRow(0, historyRecord);
            } else if (historyRecord.action == 'insertColumn') {
                obj.historyProcessColumn(1, historyRecord);
            } else if (historyRecord.action == 'deleteColumn') {
                obj.historyProcessColumn(0, historyRecord);
            } else if (historyRecord.action == 'moveRow') {
                obj.moveRow(historyRecord.newValue, historyRecord.oldValue);
            } else if (historyRecord.action == 'moveColumn') {
                obj.moveColumn(historyRecord.newValue, historyRecord.oldValue);
            } else if (historyRecord.action == 'setMerge') {
                obj.removeMerge(historyRecord.column, historyRecord.data);
            } else if (historyRecord.action == 'setStyle') {
                obj.setStyle(historyRecord.oldValue, null, null, 1);
            } else if (historyRecord.action == 'setWidth') {
                obj.setWidth(historyRecord.column, historyRecord.oldValue);
            } else if (historyRecord.action == 'setHeight') {
                obj.setHeight(historyRecord.row, historyRecord.oldValue);
            } else if (historyRecord.action == 'setHeader') {
                obj.setHeader(historyRecord.column, historyRecord.oldValue);
            } else if (historyRecord.action == 'setComments') {
                obj.setComments(historyRecord.column, historyRecord.oldValue[0], historyRecord.oldValue[1]);
            } else if (historyRecord.action == 'orderBy') {
                var rows = [];
                for (var j = 0; j < historyRecord.rows.length; j++) {
                    rows[historyRecord.rows[j]] = j;
                }
                obj.updateOrderArrow(historyRecord.column, historyRecord.order ? 0 : 1);
                obj.updateOrder(rows);
            } else if (historyRecord.action == 'setValue') {
                // Redo for changes in cells
                for (var i = 0; i < historyRecord.records.length; i++) {
                    obj.updateCell(historyRecord.records[i].col, historyRecord.records[i].row, historyRecord.records[i].oldValue);
                    obj.updateFormulaChain(historyRecord.records[i].col, historyRecord.records[i].row, records);
                    if (historyRecord.oldStyle) {
                        obj.resetStyle(historyRecord.oldStyle, true);
                    }
                }
                // Update selection
                if (! historyRecord.selection) {
                    historyRecord.selection = [historyRecord.records[0].col, historyRecord.records[0].row];
                }
                obj.updateSelectionFromCoords(historyRecord.selection[0], historyRecord.selection[1], historyRecord.selection[2], historyRecord.selection[3]);
                // Update table
                obj.updateTable();
            }
        }
        obj.ignoreEvents = ignoreEvents;
        obj.ignoreHistory = ignoreHistory;

        if (typeof(obj.options.onundo) == 'function') {
            obj.options.onundo(el, historyRecord);
        }
    }

    /**
     * Redo previously undone action
     */
    obj.redo = function() {
        // Ignore events and history
        var ignoreEvents = obj.ignoreEvents ? true : false;
        var ignoreHistory = obj.ignoreHistory ? true : false;

        obj.ignoreEvents = true;
        obj.ignoreHistory = true;

        // Records
        var records = [];

        // Update cells
        if (obj.historyIndex < obj.history.length - 1) {
            // History
            var historyRecord = obj.history[++obj.historyIndex];

            if (historyRecord.action == 'insertRow') {
                obj.historyProcessRow(0, historyRecord);
            } else if (historyRecord.action == 'deleteRow') {
                obj.historyProcessRow(1, historyRecord);
            } else if (historyRecord.action == 'insertColumn') {
                obj.historyProcessColumn(0, historyRecord);
            } else if (historyRecord.action == 'deleteColumn') {
                obj.historyProcessColumn(1, historyRecord);
            } else if (historyRecord.action == 'moveRow') {
                obj.moveRow(historyRecord.oldValue, historyRecord.newValue);
            } else if (historyRecord.action == 'moveColumn') {
                obj.moveColumn(historyRecord.oldValue, historyRecord.newValue);
            } else if (historyRecord.action == 'setMerge') {
                obj.setMerge(historyRecord.column, historyRecord.colspan, historyRecord.rowspan, 1);
            } else if (historyRecord.action == 'setStyle') {
                obj.setStyle(historyRecord.newValue, null, null, 1);
            } else if (historyRecord.action == 'setWidth') {
                obj.setWidth(historyRecord.column, historyRecord.newValue);
            } else if (historyRecord.action == 'setHeight') {
                obj.setHeight(historyRecord.row, historyRecord.newValue);
            } else if (historyRecord.action == 'setHeader') {
                obj.setHeader(historyRecord.column, historyRecord.newValue);
            } else if (historyRecord.action == 'setComments') {
                obj.setComments(historyRecord.column, historyRecord.newValue[0], historyRecord.newValue[1]);
            } else if (historyRecord.action == 'orderBy') {
                obj.updateOrderArrow(historyRecord.column, historyRecord.order);
                obj.updateOrder(historyRecord.rows);
            } else if (historyRecord.action == 'setValue') {
                // Redo for changes in cells
                for (var i = 0; i < historyRecord.records.length; i++) {
                    obj.updateCell(historyRecord.records[i].col, historyRecord.records[i].row, historyRecord.records[i].newValue);
                    obj.updateFormulaChain(historyRecord.records[i].col, historyRecord.records[i].row, records);
                    if (historyRecord.newStyle) {
                        obj.resetStyle(historyRecord.newStyle, true);
                    }
                }

                // Update selection
                if (! historyRecord.selection) {
                    historyRecord.selection = [historyRecord.records[0].col, historyRecord.records[0].row];
                }
                obj.updateSelectionFromCoords(historyRecord.selection[0], historyRecord.selection[1], historyRecord.selection[2], historyRecord.selection[3]);
                // Update table
                obj.updateTable();
            }
        }
        obj.ignoreEvents = ignoreEvents;
        obj.ignoreHistory = ignoreHistory;

        if (typeof(obj.options.onredo) == 'function') {
            obj.options.onredo(el, historyRecord);
        }
    }

    /**
     * Get dropdown value from key
     */
    obj.getDropDownValue = function(column, key) {
        var value = [];

        if (obj.options.columns[column] && obj.options.columns[column].source) {
            // Create array from source
            var combo = [];
            var source = obj.options.columns[column].source;

            for (var i = 0; i < source.length; i++) {
                if (typeof(source[i]) == 'object') {
                    combo[source[i].id] = source[i].name;
                } else {
                    combo[source[i]] = source[i];
                }
            }

            // Garante single multiple compatibily
            var keys = ('' + key).split(';')

            for (var i = 0; i < keys.length; i++) {
                if (combo[keys[i]]) {
                    value.push(combo[keys[i]]);
                }
            }
        } else {
            console.error('Invalid column');
        }

        return (value.length > 0) ? value.join('; ') : '';
    }

    /**
     * From starckoverflow contributions
     */
    obj.parseCSV = function(str, delimiter) {
        // Remove last line break
        str = str.replace(/\r?\n$|\r$|\n$/g, "");
        // Last caracter is the delimiter
        if (str.charCodeAt(str.length-1) == 9) {
            str += "\0";
        }
        // user-supplied delimeter or default comma
        delimiter = (delimiter || ",");

        var arr = [];
        var quote = false;  // true means we're inside a quoted field
        // iterate over each character, keep track of current row and column (of the returned array)
        for (var row = 0, col = 0, c = 0; c < str.length; c++) {
            var cc = str[c], nc = str[c+1];
            arr[row] = arr[row] || [];
            arr[row][col] = arr[row][col] || '';

            // If the current character is a quotation mark, and we're inside a quoted field, and the next character is also a quotation mark, add a quotation mark to the current column and skip the next character
            if (cc == '"' && quote && nc == '"') { arr[row][col] += cc; ++c; continue; }  

            // If it's just one quotation mark, begin/end quoted field
            if (cc == '"') { quote = !quote; continue; }

            // If it's a comma and we're not in a quoted field, move on to the next column
            if (cc == delimiter && !quote) { ++col; continue; }

            // If it's a newline (CRLF) and we're not in a quoted field, skip the next character and move on to the next row and move to column 0 of that new row
            if (cc == '\r' && nc == '\n' && !quote) { ++row; col = 0; ++c; continue; }

            // If it's a newline (LF or CR) and we're not in a quoted field, move on to the next row and move to column 0 of that new row
            if (cc == '\n' && !quote) { ++row; col = 0; continue; }
            if (cc == '\r' && !quote) { ++row; col = 0; continue; }

            // Otherwise, append the current character to the current column
            arr[row][col] += cc;
        }
        return arr;
    }

    obj.hash = function(str) {
        var hash = 0, i, chr;

        if (str.length === 0) {
            return hash;
        } else {
            for (i = 0; i < str.length; i++) {
              chr = str.charCodeAt(i);
              hash = ((hash << 5) - hash) + chr;
              hash |= 0;
            }
        }
        return hash;
    }

    obj.onafterchanges = function(el, records) {
        if (! obj.ignoreEvents) {
            // On after changes
            if (typeof(obj.options.onafterchanges) == 'function') {
                obj.options.onafterchanges(el, records);
            }
        }
    }

    obj.destroy = function() {
        jexcel.destroy(el);
    }

    /**
     * Initialization method
     */
    obj.init = function() {
        jexcel.current = obj;

        // Build handlers
        if (typeof(jexcel.build) == 'function') {
            jexcel.build();
            jexcel.build = null;
        }

        // Load the table data based on an CSV file
        if (obj.options.csv) {
            // Loading
            if (obj.options.loadingSpin == true) {
                jSuites.loading.show();
            }

            // Load CSV file
            jSuites.ajax({
                url: obj.options.csv,
                method: 'GET',
                dataType: 'text',
                success: function(result) {
                    // Convert data
                    var newData = obj.parseCSV(result, obj.options.csvDelimiter)

                    // Headers
                    if (obj.options.csvHeaders == true && newData.length > 0) {
                        var headers = newData.shift();
                        for(var i = 0; i < headers.length; i++) {
                            if (! obj.options.columns[i]) {
                                obj.options.columns[i] = { type:'text', align:obj.options.defaultColAlign, width:obj.options.defaultColWidth };
                            }
                            // Precedence over pre-configurated titles
                            if (typeof obj.options.columns[i].title === 'undefined') {
                              obj.options.columns[i].title = headers[i];
                            }
                        }
                    }
                    // Data
                    obj.options.data = newData;
                    // Prepare table
                    obj.prepareTable();
                    // Hide spin
                    if (obj.options.loadingSpin == true) {
                        jSuites.loading.hide();
                    }
                }
            });
        } else if (obj.options.url) {
            // Loading
            if (obj.options.loadingSpin == true) {
                jSuites.loading.show();
            }

            jSuites.ajax({
                url: obj.options.url,
                method: 'GET',
                dataType: 'json',
                success: function(result) {
                    // Data
                    obj.options.data = (result.data) ? result.data : result;
                    // Prepare table
                    obj.prepareTable();
                    // Hide spin
                    if (obj.options.loadingSpin == true) {
                        jSuites.loading.hide();
                    }
                }
            });
        } else {
            // Prepare table
            obj.prepareTable();
        }
    }

    // Context menu
    if (options && options.contextMenu != null) {
        obj.options.contextMenu = options.contextMenu;
    } else {
        obj.options.contextMenu = function(el, x, y, e) {
            var items = [];

            if (y == null) {
                // Insert a new column
                if (obj.options.allowInsertColumn == true) {
                    items.push({
                        title:obj.options.text.insertANewColumnBefore,
                        onclick:function() {
                            obj.insertColumn(1, parseInt(x), 1);
                        }
                    });
                }

                if (obj.options.allowInsertColumn == true) {
                    items.push({
                        title:obj.options.text.insertANewColumnAfter,
                        onclick:function() {
                            obj.insertColumn(1, parseInt(x), 0);
                        }
                    });
                }

                // Delete a column
                if (obj.options.allowDeleteColumn == true) {
                    items.push({
                        title:obj.options.text.deleteSelectedColumns,
                        onclick:function() {
                            obj.deleteColumn(obj.getSelectedColumns().length ? undefined : parseInt(x));
                        }
                    });
                }

                // Rename column
                if (obj.options.allowRenameColumn == true) {
                    items.push({
                        title:obj.options.text.renameThisColumn,
                        onclick:function() {
                            obj.setHeader(x);
                        }
                    });
                }

                // Sorting
                if (obj.options.columnSorting == true) {
                    // Line
                    items.push({ type:'line' });

                    items.push({
                        title:obj.options.text.orderAscending,
                        onclick:function() {
                            obj.orderBy(x, 0);
                        }
                    });
                    items.push({
                        title:obj.options.text.orderDescending,
                        onclick:function() {
                            obj.orderBy(x, 1);
                        }
                    });
                }
            } else {
                // Insert new row
                if (obj.options.allowInsertRow == true) {
                    items.push({
                        title:obj.options.text.insertANewRowBefore,
                        onclick:function() {
                            obj.insertRow(1, parseInt(y), 1);
                        }
                    });

                    items.push({
                        title:obj.options.text.insertANewRowAfter,
                        onclick:function() {
                            obj.insertRow(1, parseInt(y));
                        }
                    });
                }

                if (obj.options.allowDeleteRow == true) {
                    items.push({
                        title:obj.options.text.deleteSelectedRows,
                        onclick:function() {
                            obj.deleteRow(obj.getSelectedRows().length ? undefined : parseInt(y));
                        }
                    });
                }

                if (x) {
                    if (obj.options.allowComments == true) {
                        items.push({ type:'line' });

                        var title = obj.records[y][x].getAttribute('title') || '';

                        items.push({
                            title: title ? obj.options.text.editComments : obj.options.text.addComments,
                            onclick:function() {
                                var comment = prompt(obj.options.text.comments, title);
                                if (comment) {
                                    obj.setComments([ x, y ], comment);
                                }
                            }
                        });

                        if (title) {
                            items.push({
                                title:obj.options.text.clearComments,
                                onclick:function() {
                                    obj.setComments([ x, y ], '');
                                }
                            });
                        }
                    }
                }
            }

            // Line
            items.push({ type:'line' });

            // Copy
            items.push({
                title:obj.options.text.copy,
                shortcut:'Ctrl + C',
                onclick:function() {
                    obj.copy(true);
                }
            });

            // Paste
            if (navigator && navigator.clipboard) {
                items.push({
                    title:obj.options.text.paste,
                    shortcut:'Ctrl + V',
                    onclick:function() {
                        if (obj.selectedCell) {
                            navigator.clipboard.readText().then(function(text) {
                                if (text) {
                                    jexcel.current.paste(obj.selectedCell[0], obj.selectedCell[1], text);
                                }
                            });
                        }
                    }
                });
            }

            // Save
            if (obj.options.allowExport) {
                items.push({
                    title: obj.options.text.saveAs,
                    shortcut: 'Ctrl + S',
                    onclick: function () {
                        obj.download();
                    }
                });
            }

            // About
            if (obj.options.about) {
                items.push({
                    title:obj.options.text.about,
                    onclick:function() {
                        alert(obj.options.about);
                    }
                });
            }

            return items;
        }
    }

    obj.scrollControls = function(e) {
        if (obj.options.lazyLoading == true) {
            if (jexcel.timeControlLoading == null) {
                jexcel.timeControlLoading = setTimeout(function() {
                    if (obj.content.scrollTop + obj.content.clientHeight >= obj.content.scrollHeight) {
                        if (obj.loadDown()) {
                            if (obj.content.scrollTop + obj.content.clientHeight > obj.content.scrollHeight - 10) {
                                obj.content.scrollTop = obj.content.scrollTop - obj.content.clientHeight;
                            }
                            obj.updateCornerPosition();
                        }
                    } else if (obj.content.scrollTop <= obj.content.clientHeight) {
                        if (obj.loadUp()) {
                            if (obj.content.scrollTop < 10) {
                                obj.content.scrollTop = obj.content.scrollTop + obj.content.clientHeight;
                            }
                            obj.updateCornerPosition();
                        }
                    }

                    jexcel.timeControlLoading = null;
                }, 100);
            }
        }

        // Close editor
        if (obj.options.lazyLoading == true || obj.options.tableOverflow == true) {
            if (obj.edition && e.target.className.substr(0,9) != 'jdropdown') {
                obj.closeEditor(obj.edition[0], true);
            }
        }
    }

    el.addEventListener("DOMMouseScroll", obj.scrollControls);
    el.addEventListener("mousewheel", obj.scrollControls);

    el.jexcel = obj;

    obj.init();

    return obj;
});

jexcel.current = null;
jexcel.timeControl = null;
jexcel.timeControlLoading = null;

jexcel.destroy = function(element, destroyEventHandlers) {
    if (element.jexcel) {
        element.removeEventListener("DOMMouseScroll", element.jexcel.scrollControls);
        element.removeEventListener("mousewheel", element.jexcel.scrollControls);
        element.jexcel = null;
        element.innerHTML = '';

        if (destroyEventHandlers) {
            document.removeEventListener("keydown", jexcel.keyDownControls);
            document.removeEventListener("mouseup", jexcel.mouseUpControls);
            document.removeEventListener("mousedown", jexcel.mouseDownControls);
            document.removeEventListener("mousemove", jexcel.mouseMoveControls);
            document.removeEventListener("mouseover", jexcel.mouseOverControls);
            document.removeEventListener("dblclick", jexcel.doubleClickControls);
            document.removeEventListener("paste", jexcel.pasteControls);
            document.removeEventListener("contextmenu", jexcel.contextMenuControls);
            document.removeEventListener("touchstart", jexcel.touchStartControls);
            document.removeEventListener("touchend", jexcel.touchEndControls);
            document.removeEventListener("touchcancel", jexcel.touchEndControls);
            jexcel = null;
        }
    }
}

jexcel.build = function() {
    document.addEventListener("keydown", jexcel.keyDownControls);
    document.addEventListener("mouseup", jexcel.mouseUpControls);
    document.addEventListener("mousedown", jexcel.mouseDownControls);
    document.addEventListener("mousemove", jexcel.mouseMoveControls);
    document.addEventListener("mouseover", jexcel.mouseOverControls);
    document.addEventListener("dblclick", jexcel.doubleClickControls);
    document.addEventListener("paste", jexcel.pasteControls);
    document.addEventListener("contextmenu", jexcel.contextMenuControls);
    document.addEventListener("touchstart", jexcel.touchStartControls);
    document.addEventListener("touchend", jexcel.touchEndControls);
    document.addEventListener("touchcancel", jexcel.touchEndControls);
    document.addEventListener("touchmove", jexcel.touchEndControls);
}

/**
 * Events
 */
jexcel.keyDownControls = function(e) {
    if (jexcel.current) {
        if (jexcel.current.edition) {
            if (e.which == 27) {
                // Escape
                if (jexcel.current.edition) {
                    // Exit without saving
                    jexcel.current.closeEditor(jexcel.current.edition[0], false);
                }
                e.preventDefault();
            } else if (e.which == 13) {
                // Enter
                if (jexcel.current.options.columns[jexcel.current.edition[2]].type == 'calendar') {
                    jexcel.current.editor[0].children[0].calendar.close(1);
                } else if (jexcel.current.options.columns[jexcel.current.edition[2]].type == 'dropdown' ||
                           jexcel.current.options.columns[jexcel.current.edition[2]].type == 'autocomplete') {
                    // Do nothing
                } else {
                    // Alt enter -> do not close editor
                    if ((jexcel.current.options.wordWrap == true ||
                         jexcel.current.options.columns[jexcel.current.edition[2]].wordWrap == true ||
                         jexcel.current.options.data[jexcel.current.edition[3]][jexcel.current.edition[2]].length > 200) && e.altKey) {
                        // Add new line to the editor
                        var editorTextarea = jexcel.current.edition[0].children[0];
                        var editorValue = jexcel.current.edition[0].children[0].value;
                        var editorIndexOf = editorTextarea.selectionStart;
                        editorValue = editorValue.slice(0, editorIndexOf) + "\n" + editorValue.slice(editorIndexOf);
                        editorTextarea.value = editorValue;
                        editorTextarea.focus();
                        editorTextarea.selectionStart = editorIndexOf + 1;
                        editorTextarea.selectionEnd = editorIndexOf + 1;
                    } else {
                        jexcel.current.edition[0].children[0].blur();
                    }
                }
            } else if (e.which == 9) {
                // Tab
                if (jexcel.current.options.columns[jexcel.current.edition[2]].type == 'calendar') {
                    jexcel.current.edition[0].children[0].calendar.close(1);
                } else {
                    jexcel.current.edition[0].children[0].blur();
                }
            }
        }

        if (! jexcel.current.edition && jexcel.current.selectedCell) {
            // Which key
            if (e.which == 37) {
                jexcel.current.left(e.shiftKey, e.ctrlKey);
                e.preventDefault();
            } else if (e.which == 39) {
                jexcel.current.right(e.shiftKey, e.ctrlKey);
                e.preventDefault();
            } else if (e.which == 38) {
                jexcel.current.up(e.shiftKey, e.ctrlKey);
                e.preventDefault();
            } else if (e.which == 40) {
                jexcel.current.down(e.shiftKey, e.ctrlKey);
                e.preventDefault();
            } else if (e.which == 36) {
                jexcel.current.first(e.shiftKey, e.ctrlKey);
                e.preventDefault();
            } else if (e.which == 35) {
                jexcel.current.last(e.shiftKey, e.ctrlKey);
                e.preventDefault();
            } else if (e.which == 32) {
                if (jexcel.current.options.editable == true) {
                    jexcel.current.setCheckRadioValue();
                }
                e.preventDefault();
            } else if (e.which == 46) {
                // Delete
                if (jexcel.current.options.editable == true) {
                    if (jexcel.current.selectedRow) {
                        if (jexcel.current.options.allowDeleteRow == true) {
                            if (confirm(jexcel.current.options.text.areYouSureToDeleteTheSelectedRows)) {
                                jexcel.current.deleteRow();
                            }
                        }
                    } else if (jexcel.current.selectedHeader) {
                        if (jexcel.current.options.allowDeleteColumn == true) {
                            if (confirm(jexcel.current.options.text.areYouSureToDeleteTheSelectedColumns)) {
                                jexcel.current.deleteColumn();
                            }
                        }
                    } else {
                        // Change value
                        jexcel.current.setValue(jexcel.current.highlighted, '');
                    }
                }
            } else if (e.which == 13) {
                // Move cursor
                if (e.shiftKey) {
                    jexcel.current.up();
                } else {
                    if (jexcel.current.options.allowInsertRow == true) {
                        if (jexcel.current.options.allowManualInsertRow == true) {
                            if (jexcel.current.selectedCell[1] == jexcel.current.options.data.length - 1) {
                                // New record in case selectedCell in the last row
                                jexcel.current.insertRow();
                            }
                        }
                    }

                    jexcel.current.down();
                }
                e.preventDefault();
            } else if (e.which == 9) {
                // Tab
                if (e.shiftKey) {
                    jexcel.current.left();
                } else {
                    if (jexcel.current.options.allowInsertColumn == true) {
                        if (jexcel.current.options.allowManualInsertColumn == true) {
                            if (jexcel.current.selectedCell[0] == jexcel.current.options.data[0].length - 1) {
                                // New record in case selectedCell in the last column
                                jexcel.current.insertColumn();
                            }
                        }
                    }

                    jexcel.current.right();
                }
                e.preventDefault();
            } else {
                if ((e.ctrlKey || e.metaKey) && ! e.shiftKey) {
                    if (e.which == 65) {
                        // Ctrl + A
                        jexcel.current.selectAll();
                        e.preventDefault();
                    } else if (e.which == 83) {
                        // Ctrl + S
                        jexcel.current.download();
                        e.preventDefault();
                    } else if (e.which == 89) {
                        // Ctrl + Y
                        jexcel.current.redo();
                        e.preventDefault();
                    } else if (e.which == 90) {
                        // Ctrl + Z
                        jexcel.current.undo();
                        e.preventDefault();
                    } else if (e.which == 67) {
                        // Ctrl + C
                        jexcel.current.copy(true);
                        e.preventDefault();
                    } else if (e.which == 67) {
                        // Ctrl + C
                        jexcel.current.copy(true);
                        e.preventDefault();
                    } else if (e.which == 88) {
                        // Ctrl + X
                        if (jexcel.current.options.editable == true) {
                            jexcel.cutControls();
                        } else {
                            jexcel.copyControls();
                        }
                        e.preventDefault();
                    } else if (e.which == 86) {
                        // Ctrl + V
                        jexcel.pasteControls();
                    }
                } else {
                    if (jexcel.current.selectedCell) {
                        if (jexcel.current.options.editable == true) {
                            var rowId = jexcel.current.selectedCell[1];
                            var columnId = jexcel.current.selectedCell[0];

                            // If is not readonly
                            if (jexcel.current.options.columns[columnId].type != 'readonly') {
                                // Characters able to start a edition
                                if (e.keyCode == 32) {
                                    // Space
                                    if (jexcel.current.options.columns[columnId].type == 'checkbox' ||
                                        jexcel.current.options.columns[columnId].type == 'radio') {
                                        e.preventDefault();
                                    } else {
                                        // Start edition
                                        jexcel.current.openEditor(jexcel.current.records[rowId][columnId], true);
                                    }
                                } else if (e.keyCode == 113) {
                                    // Start edition with current content F2
                                    jexcel.current.openEditor(jexcel.current.records[rowId][columnId], false);
                                } else if ((e.keyCode == 8) ||
                                           (e.keyCode >= 48 && e.keyCode <= 57) ||
                                           (e.keyCode >= 96 && e.keyCode <= 111) ||
                                           (e.keyCode == 187) ||
                                           (e.keyCode == 189) ||
                                           ((String.fromCharCode(e.keyCode) == e.key || String.fromCharCode(e.keyCode).toLowerCase() == e.key.toLowerCase()) && jexcel.validLetter(String.fromCharCode(e.keyCode)))) {
                                    // Start edition
                                    jexcel.current.openEditor(jexcel.current.records[rowId][columnId], true);
                                    // Prevent entries in the calendar
                                    if (jexcel.current.options.columns[columnId].type == 'calendar') {
                                        e.preventDefault();
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } else {
            if (e.target.classList.contains('jexcel_search')) {
                if (jexcel.timeControl) {
                    clearTimeout(jexcel.timeControl);
                }

                jexcel.timeControl = setTimeout(function() {
                    jexcel.current.search(e.target.value);
                }, 200);
            }
        }
    }
}

jexcel.isMouseAction = false;

jexcel.mouseDownControls = function(e) {
    e = e || window.event;
    if (e.buttons) {
        var mouseButton = e.buttons;
    } else if (e.button) {
        var mouseButton = e.button;
    } else {
        var mouseButton = e.which;
    }

    // Get elements
    var jexcelTable = jexcel.getElement(e.target);

    if (jexcelTable[0]) {
        if (jexcel.current != jexcelTable[0].jexcel) {
            if (jexcel.current) {
                jexcel.current.resetSelection();
            }
            jexcel.current = jexcelTable[0].jexcel;
        }
    } else {
        if (jexcel.current) {
            jexcel.current.resetSelection(true);
            jexcel.current = null;
        }
    }

    if (jexcel.current && mouseButton == 1) {
        if (e.target.classList.contains('jexcel_selectall')) {
            if (jexcel.current) {
                jexcel.current.selectAll();
            }
        } else if (e.target.classList.contains('jexcel_corner')) {
            if (jexcel.current.options.editable == true) {
                jexcel.current.selectedCorner = true;
            }
        } else {
            // Header found
            if (jexcelTable[1] == 1) {
                var columnId = e.target.getAttribute('data-x');
                if (columnId) {
                    // Update cursor
                    var info = e.target.getBoundingClientRect();
                    if (jexcel.current.options.columnResize == true && info.width - e.offsetX < 6) {
                        // Resize helper
                        jexcel.current.resizing = {
                            mousePosition: e.pageX,
                            column: columnId,
                            width: info.width,
                        };

                        // Border indication
                        jexcel.current.headers[columnId].classList.add('resizing');
                        for (var j = 0; j < jexcel.current.records.length; j++) {
                            if (jexcel.current.records[j][columnId]) {
                                jexcel.current.records[j][columnId].classList.add('resizing');
                            }
                        }
                    } else if (jexcel.current.options.columnDrag == true && info.height - e.offsetY < 6) {
                        if (jexcel.current.isColMerged(columnId).length) {
                            console.error('JEXCEL: This column is part of a merged cell.');
                        } else {
                            // Reset selection
                            jexcel.current.resetSelection();
                            // Drag helper
                            jexcel.current.dragging = {
                                element: e.target,
                                column:columnId,
                                destination:columnId,
                            };
                            // Border indication
                            jexcel.current.headers[columnId].classList.add('dragging');
                            for (var j = 0; j < jexcel.current.records.length; j++) {
                                if (jexcel.current.records[j][columnId]) {
                                    jexcel.current.records[j][columnId].classList.add('dragging');
                                }
                            }
                        }
                    } else {
                        if (jexcel.current.selectedHeader && (e.shiftKey || e.ctrlKey)) {
                            var o = jexcel.current.selectedHeader;
                            var d = columnId;
                        } else {
                            // Press to rename
                            if (jexcel.current.selectedHeader == columnId && jexcel.current.options.allowRenameColumn == true) {
                                jexcel.timeControl = setTimeout(function() {
                                    jexcel.current.setHeader(columnId);
                                }, 800);
                            }

                            // Keep track of which header was selected first
                            jexcel.current.selectedHeader = columnId;

                            // Update selection single column
                            var o = columnId;
                            var d = columnId;
                        }

                        // Update selection
                        jexcel.current.updateSelectionFromCoords(o, 0, d, jexcel.current.options.data.length - 1);
                    }
                } else {
                    if (e.target.parentNode.classList.contains('jexcel_nested')) {
                        if (e.target.getAttribute('data-column')) {
                            var column = e.target.getAttribute('data-column').split(',');
                            var c1 = parseInt(column[0]);
                            var c2 = parseInt(column[column.length-1]);
                        } else {
                            var c1 = 0;
                            var c2 = jexcel.current.options.columns.length - 1;
                        }
                        jexcel.current.updateSelectionFromCoords(c1, 0, c2, jexcel.current.options.data.length - 1);
                    }
                }
            } else {
                jexcel.current.selectedHeader = false;
            }

            // Body found
            if (jexcelTable[1] == 2) {
                var rowId = e.target.getAttribute('data-y');

                if (e.target.classList.contains('jexcel_row')) {
                    var info = e.target.getBoundingClientRect();
                    if (jexcel.current.options.rowResize == true && info.height - e.offsetY < 6) {
                        // Resize helper
                        jexcel.current.resizing = {
                            element: e.target.parentNode,
                            mousePosition: e.pageY,
                            row: rowId,
                            height: info.height,
                        };
                        // Border indication
                        e.target.parentNode.classList.add('resizing');
                    } else if (jexcel.current.options.rowDrag == true && info.width - e.offsetX < 6) {
                        if (jexcel.current.isRowMerged(rowId).length) {
                            console.error('JEXCEL: This row is part of a merged cell');
                        } else if (jexcel.current.options.search == true && jexcel.current.results) {
                            console.error('JEXCEL: Please clear your search before perform this action');
                        } else {
                            // Reset selection
                            jexcel.current.resetSelection();
                            // Drag helper
                            jexcel.current.dragging = {
                                element: e.target.parentNode,
                                row:rowId,
                                destination:rowId,
                            };
                            // Border indication
                            e.target.parentNode.classList.add('dragging');
                        }
                    } else {
                        if (jexcel.current.selectedRow && (e.shiftKey || e.ctrlKey)) {
                            var o = jexcel.current.selectedRow;
                            var d = rowId;
                        } else {
                            // Keep track of which header was selected first
                            jexcel.current.selectedRow = rowId;

                            // Update selection single column
                            var o = rowId;
                            var d = rowId;
                        }

                        // Update selection
                        jexcel.current.updateSelectionFromCoords(0, o, jexcel.current.options.data[0].length - 1, d);
                    }
                } else {
                    // Jclose
                    if (e.target.classList.contains('jclose') && e.target.clientWidth - e.offsetX < 50 && e.offsetY < 50) {
                        jexcel.current.closeEditor(jexcel.current.edition[0], true);
                    } else {
                        var getCellCoords = function(element) {
                            var x = element.getAttribute('data-x');
                            var y = element.getAttribute('data-y');
                            if (x && y) {
                                return [x, y];
                            } else {
                                if (element.parentNode) {
                                    return getCellCoords(element.parentNode);
                                }
                            }
                        };

                        var position = getCellCoords(e.target);
                        if (position) {
                            var columnId = position[0];
                            var rowId = position[1];
                            // Close edition
                            if (jexcel.current.edition) {
                                if (jexcel.current.edition[2] != columnId || jexcel.current.edition[3] != rowId) {
                                    jexcel.current.closeEditor(jexcel.current.edition[0], true);
                                }
                            }

                            if (! jexcel.current.edition) {
                                // Update cell selection
                                if (e.shiftKey) {
                                    jexcel.current.updateSelectionFromCoords(jexcel.current.selectedCell[0], jexcel.current.selectedCell[1], columnId, rowId);
                                } else {
                                    jexcel.current.updateSelectionFromCoords(columnId, rowId);
                                }
                            }

                            // No full row selected
                            jexcel.current.selectedHeader = null;
                            jexcel.current.selectedRow = null;
                        }
                    }
                }
            } else {
                jexcel.current.selectedRow = false;
            }

            // Pagination
            if (e.target.classList.contains('jexcel_page')) {
                if (e.target.innerText == '<') {
                    jexcel.current.page(0);
                } else if (e.target.innerText == '>') {
                    jexcel.current.page(e.target.getAttribute('title') - 1);
                } else {
                    jexcel.current.page(e.target.innerText - 1);
                }
            }
        }

        if (jexcel.current.edition) {
            jexcel.isMouseAction = false;
        } else {
            jexcel.isMouseAction = true;
        }
    } else {
        jexcel.isMouseAction = false;
    }
}

jexcel.mouseUpControls = function(e) {
    if (jexcel.current) {
        // Update cell size
        if (jexcel.current.resizing) {
            // Columns to be updated
            if (jexcel.current.resizing.column) {
                // Remove Class
                jexcel.current.headers[jexcel.current.resizing.column].classList.remove('resizing');
                var newWidth = jexcel.current.colgroup[jexcel.current.resizing.column].getAttribute('width');
                jexcel.current.setWidth(jexcel.current.resizing.column, newWidth, jexcel.current.resizing.width);
                // Remove border
                jexcel.current.headers[jexcel.current.resizing.column].classList.remove('resizing');
                for (var j = 0; j < jexcel.current.records.length; j++) {
                    if (jexcel.current.records[j][jexcel.current.resizing.column]){
                        jexcel.current.records[j][jexcel.current.resizing.column].classList.remove('resizing');
                    }
                }
            } else {
                // Remove Class
                jexcel.current.rows[jexcel.current.resizing.row].children[0].classList.remove('resizing');
                var newHeight = jexcel.current.rows[jexcel.current.resizing.row].getAttribute('height');
                jexcel.current.setHeight(jexcel.current.resizing.row, newHeight, jexcel.current.resizing.height);
                // Remove border
                jexcel.current.resizing.element.classList.remove('resizing');
            }
            // Reset resizing helper
            jexcel.current.resizing = null;
        } else if (jexcel.current.dragging) {
            // Reset dragging helper
            if (jexcel.current.dragging) {
                if (jexcel.current.dragging.column) {
                    // Target
                    var columnId = e.target.getAttribute('data-x');
                    // Remove move style
                    jexcel.current.headers[jexcel.current.dragging.column].classList.remove('dragging');
                    for (var j = 0; j < jexcel.current.rows.length; j++) {
                        if (jexcel.current.records[j][jexcel.current.dragging.column]) {
                            jexcel.current.records[j][jexcel.current.dragging.column].classList.remove('dragging');
                        }
                    }
                    for (var i = 0; i < jexcel.current.headers.length; i++) {
                        jexcel.current.headers[i].classList.remove('dragging-left');
                        jexcel.current.headers[i].classList.remove('dragging-right');
                    }
                    // Update position
                    if (columnId) {
                        if (jexcel.current.dragging.column != jexcel.current.dragging.destination) {
                            jexcel.current.moveColumn(jexcel.current.dragging.column, jexcel.current.dragging.destination);
                        }
                    }
                } else {
                    var position = Array.prototype.indexOf.call(jexcel.current.dragging.element.parentNode.children, jexcel.current.dragging.element);
                    if (jexcel.current.dragging.row != position) {
                        jexcel.current.moveRow(jexcel.current.dragging.row, position, true);
                    }
                    jexcel.current.dragging.element.classList.remove('dragging');
                }
                jexcel.current.dragging = null;
            }
        } else {
            // Close any corner selection
            if (jexcel.current.selectedCorner) {
                jexcel.current.selectedCorner = false;

                // Data to be copied
                if (jexcel.current.selection.length > 0) {
                    // Copy data
                    jexcel.current.copyData(jexcel.current.selection[0], jexcel.current.selection[jexcel.current.selection.length - 1]);

                    // Remove selection
                    jexcel.current.removeCopySelection();
                }
            }
        }
    }

    // Clear any time control
    if (jexcel.timeControl) {
        clearTimeout(jexcel.timeControl);
        jexcel.timeControl = null;
    }

    // Mouse up
    jexcel.isMouseAction = false;
}

// Mouse move controls
jexcel.mouseMoveControls = function(e) {
    e = e || window.event;
    if (e.buttons) {
        var mouseButton = e.buttons;
    } else if (e.button) {
        var mouseButton = e.button;
    } else {
        var mouseButton = e.which;
    }

    if (! mouseButton) {
        jexcel.isMouseAction = false;
    }

    if (jexcel.current) {
        if (jexcel.isMouseAction == true) {
            // Resizing is ongoing
            if (jexcel.current.resizing) {
                if (jexcel.current.resizing.column) {
                    var width = e.pageX - jexcel.current.resizing.mousePosition;

                    if (jexcel.current.resizing.width + width > 0) {
                        var tempWidth = jexcel.current.resizing.width + width;
                        jexcel.current.colgroup[jexcel.current.resizing.column].setAttribute('width', tempWidth);

                        jexcel.current.updateCornerPosition();
                    }
                } else {
                    var height = e.pageY - jexcel.current.resizing.mousePosition;

                    if (jexcel.current.resizing.height + height > 0) {
                        var tempHeight = jexcel.current.resizing.height + height;
                        jexcel.current.rows[jexcel.current.resizing.row].setAttribute('height', tempHeight);

                        jexcel.current.updateCornerPosition();
                    }
                }
            }
        } else {
            var x = e.target.getAttribute('data-x');
            var y = e.target.getAttribute('data-y');
            var rect = e.target.getBoundingClientRect();

            if (jexcel.current.cursor) {
                jexcel.current.cursor.style.cursor = '';
                jexcel.current.cursor = null;
            }

            if (e.target.parentNode.parentNode && e.target.parentNode.parentNode.className) {
                if (e.target.parentNode.parentNode.classList.contains('resizable')) {
                    if (e.target && x && ! y && (rect.width - (e.clientX - rect.left) < 6)) {
                        jexcel.current.cursor = e.target;
                        jexcel.current.cursor.style.cursor = 'col-resize';
                    } else if (e.target && ! x && y && (rect.height - (e.clientY - rect.top) < 6)) {
                        jexcel.current.cursor = e.target;
                        jexcel.current.cursor.style.cursor = 'row-resize';
                    }
                }

                if (e.target.parentNode.parentNode.classList.contains('draggable')) {
                    if (e.target && ! x && y && (rect.width - (e.clientX - rect.left) < 6)) {
                        jexcel.current.cursor = e.target;
                        jexcel.current.cursor.style.cursor = 'move';
                    } else if (e.target && x && ! y && (rect.height - (e.clientY - rect.top) < 6)) {
                        jexcel.current.cursor = e.target;
                        jexcel.current.cursor.style.cursor = 'move';
                    }
                }
            }
        }
    }
}

jexcel.mouseOverControls = function(e) {
    e = e || window.event;
    if (e.buttons) {
        var mouseButton = e.buttons;
    } else if (e.button) {
        var mouseButton = e.button;
    } else {
        var mouseButton = e.which;
    }

    if (! mouseButton) {
        jexcel.isMouseAction = false;
    }

    if (jexcel.current && jexcel.isMouseAction == true) {
        // Get elements
        var jexcelTable = jexcel.getElement(e.target);

        if (jexcelTable[0]) {
            // Avoid cross reference
            if (jexcel.current != jexcelTable[0].jexcel) {
                if (jexcel.current) {
                    return false;
                }
            }

            var columnId = e.target.getAttribute('data-x');
            var rowId = e.target.getAttribute('data-y');

            if (jexcel.current.dragging) {
                if (jexcel.current.dragging.column) {
                    if (columnId) {
                        if (jexcel.current.isColMerged(columnId).length) {
                            console.error('JEXCEL: This column is part of a merged cell.');
                        } else {
                            for (var i = 0; i < jexcel.current.headers.length; i++) {
                                jexcel.current.headers[i].classList.remove('dragging-left');
                                jexcel.current.headers[i].classList.remove('dragging-right');
                            }

                            if (jexcel.current.dragging.column == columnId) {
                                jexcel.current.dragging.destination = parseInt(columnId);
                            } else {
                                if (e.target.clientWidth / 2 > e.offsetX) {
                                    if (jexcel.current.dragging.column < columnId) {
                                        jexcel.current.dragging.destination = parseInt(columnId) - 1;
                                    } else {
                                        jexcel.current.dragging.destination = parseInt(columnId);
                                    }
                                    jexcel.current.headers[columnId].classList.add('dragging-left');
                                } else {
                                    if (jexcel.current.dragging.column < columnId) {
                                        jexcel.current.dragging.destination = parseInt(columnId);
                                    } else {
                                        jexcel.current.dragging.destination = parseInt(columnId) + 1;
                                    }
                                    jexcel.current.headers[columnId].classList.add('dragging-right');
                                }
                            }
                        }
                    }
                } else {
                    if (rowId) {
                        if (jexcel.current.isRowMerged(rowId).length) {
                            console.error('JEXCEL: This row is part of a merged cell.');
                        } else {
                            var target = (e.target.clientHeight / 2 > e.offsetY) ? e.target.parentNode.nextSibling : e.target.parentNode;
                            e.target.parentNode.parentNode.insertBefore(jexcel.current.dragging.element, target);
                        }
                    }
                }
            } else if (jexcel.current.resizing) {
            } else {
                // Header found
                if (jexcelTable[1] == 1) {
                    if (jexcel.current.selectedHeader) {
                        var columnId = e.target.getAttribute('data-x');
                        var o = jexcel.current.selectedHeader;
                        var d = columnId;
                        // Update selection
                        jexcel.current.updateSelectionFromCoords(o, 0, d, jexcel.current.options.data.length - 1);
                    }
                }

                // Body found
                if (jexcelTable[1] == 2) {
                    if (e.target.classList.contains('jexcel_row')) {
                        if (jexcel.current.selectedRow) {
                            var o = jexcel.current.selectedRow;
                            var d = rowId;
                            // Update selection
                            jexcel.current.updateSelectionFromCoords(0, o, jexcel.current.options.data[0].length - 1, d);
                        }
                    } else {
                        // Do not select edtion is in progress
                        if (! jexcel.current.edition) {
                            if (columnId && rowId) {
                                if (jexcel.current.selectedCorner) {
                                    jexcel.current.updateCopySelection(columnId, rowId);
                                } else {
                                    if (jexcel.current.selectedCell) {
                                        jexcel.current.updateSelectionFromCoords(jexcel.current.selectedCell[0], jexcel.current.selectedCell[1], columnId, rowId);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    // Clear any time control
    if (jexcel.timeControl) {
        clearTimeout(jexcel.timeControl);
        jexcel.timeControl = null;
    }
}

/**
 * Double click event handler: controls the double click in the corner, cell edition or column re-ordering.
 */
jexcel.doubleClickControls = function(e) {
    // Jexcel is selected
    if (jexcel.current) {
        // Corner action
        if (e.target.classList.contains('jexcel_corner')) {
            // Any selected cells
            if (jexcel.current.highlighted.length > 0) {
                // Copy from this
                var x1 = jexcel.current.highlighted[0].getAttribute('data-x');
                var y1 = parseInt(jexcel.current.highlighted[jexcel.current.highlighted.length - 1].getAttribute('data-y')) + 1;
                // Until this
                var x2 = jexcel.current.highlighted[jexcel.current.highlighted.length - 1].getAttribute('data-x');
                var y2 = jexcel.current.records.length - 1
                // Execute copy
                jexcel.current.copyData(jexcel.current.records[y1][x1], jexcel.current.records[y2][x2]);
            }
        } else {
            // Get table
            var jexcelTable = jexcel.getElement(e.target);

            // Double click over header
            if (jexcelTable[1] == 1 && jexcel.current.options.columnSorting == true) {
                // Check valid column header coords
                var columnId = e.target.getAttribute('data-x');
                if (columnId) {
                    jexcel.current.orderBy(columnId);
                }
            }

            // Double click over body
            if (jexcelTable[1] == 2 && jexcel.current.options.editable == true) {
                if (! jexcel.current.edition) {
                    var getCellCoords = function(element) {
                        if (element.parentNode) {
                            var x = element.getAttribute('data-x');
                            var y = element.getAttribute('data-y');
                            if (x && y) {
                                return element;
                            } else {
                                return getCellCoords(element.parentNode);
                            }
                        }
                    }
                    var cell = getCellCoords(e.target);
                    if (cell && cell.classList.contains('highlight')) {
                        jexcel.current.openEditor(cell);
                    }
                }
            }
        }
    }
}

jexcel.copyControls = function(e) {
    if (jexcel.current && jexcel.copyControls.enabled) {
        if (! jexcel.current.edition) {
            jexcel.current.copy(true);
        }
    }
}

jexcel.cutControls = function(e) {
    if (jexcel.current) {
        if (! jexcel.current.edition) {
            jexcel.current.copy(true);
            if (jexcel.current.options.editable == true) {
                jexcel.current.setValue(jexcel.current.highlighted, '');
            }
        }
    }
}

jexcel.pasteControls = function(e) {
    if (jexcel.current && jexcel.current.selectedCell) {
        if (! jexcel.current.edition) {
            if (jexcel.current.options.editable == true) {
                if (e && e.clipboardData) {
                    jexcel.current.paste(jexcel.current.selectedCell[0], jexcel.current.selectedCell[1], e.clipboardData.getData('text'));
                    e.preventDefault();
                } else if (window.clipboardData) {
                    jexcel.current.paste(jexcel.current.selectedCell[0], jexcel.current.selectedCell[1], window.clipboardData.getData('text'));
                }
            }
        }
    }
}

jexcel.contextMenuControls = function(e) {
    e = e || window.event;
    if ("buttons" in e) {
        var mouseButton = e.buttons;
    } else {
        var mouseButton = e.which || e.button;
    }

    if (jexcel.current) {
        if (jexcel.current.edition) {
            e.preventDefault();
        } else if (jexcel.current.options.contextMenu) {
            jexcel.current.contextMenu.contextmenu.close();

            if (jexcel.current) {
                var x = e.target.getAttribute('data-x');
                var y = e.target.getAttribute('data-y');

                if (x || y) {
                    // Table found
                    var items = jexcel.current.options.contextMenu(jexcel.current, x, y, e);
                    // The id is depending on header and body
                    jexcel.current.contextMenu.contextmenu.open(e, items);
                    // Avoid the real one
                    e.preventDefault();
                }
            }
        }
    }
}

jexcel.touchStartControls = function(e) {
    var jexcelTable = jexcel.getElement(e.target);

    if (jexcelTable[0]) {
        if (jexcel.current != jexcelTable[0].jexcel) {
            if (jexcel.current) {
                jexcel.current.resetSelection();
            }
            jexcel.current = jexcelTable[0].jexcel;
        }
    } else {
        if (jexcel.current) {
            jexcel.current.resetSelection();
            jexcel.current = null;
        }
    }

    if (jexcel.current) {
        if (! jexcel.current.edition) {
            var columnId = e.target.getAttribute('data-x');
            var rowId = e.target.getAttribute('data-y');

            if (columnId && rowId) {
                jexcel.current.updateSelectionFromCoords(columnId, rowId);

                jexcel.timeControl = setTimeout(function() {
                    // Keep temporary reference to the element
                    if (jexcel.current.options.columns[columnId].type == 'color') {
                        jexcel.tmpElement = null;
                    } else {
                        jexcel.tmpElement = e.target;
                    }
                    jexcel.current.openEditor(e.target, false, e);
                }, 500);
            }
        }
    }
}

jexcel.touchEndControls = function(e) {
    // Clear any time control
    if (jexcel.timeControl) {
        clearTimeout(jexcel.timeControl);
        jexcel.timeControl = null;
        // Element
        if (jexcel.tmpElement && jexcel.tmpElement.children[0].tagName == 'INPUT') {
            jexcel.tmpElement.children[0].focus();
        }
        jexcel.tmpElement = null;
    }
}

/**
 * Jexcel extensions
 */

jexcel.createTabs = function(tabs, result) {
    // Create tab container
    tabs.innerHTML = '';
    tabs.classList.add('jexcel_tabs');
    var spreadsheet = []
    var link = [];
    for (var i = 0; i < result.length; i++) {
        // Spreadsheet container
        spreadsheet[i] = document.createElement('div');
        spreadsheet[i].classList.add('jexcel_tab');
        // Tab link
        link[i] = document.createElement('div');
        link[i].classList.add('jexcel_tab_link');
        link[i].setAttribute('data-spreadsheet', i);
        link[i].innerHTML = result[i].sheetName;
        link[i].onclick = function() {
            for (var j = 0; j < spreadsheet.length; j++) {
                spreadsheet[j].style.display = 'none';
                link[j].classList.remove('selected');
            }
            var i = this.getAttribute('data-spreadsheet');
            spreadsheet[i].style.display = 'block';
            link[i].classList.add('selected')
        }
        tabs.appendChild(link[i]);
    }

    // Append spreadsheet
    for (var i = 0; i < spreadsheet.length - 1; i++) {
        tabs.appendChild(spreadsheet[i]);
        jexcel(spreadsheet[i], result[i]);
    }

    // First tab
    spreadsheet[0].style.display = 'block';
    link[0].classList.add('selected')
}

jexcel.fromSpreadsheet = function(file, __callback) {
    var convert = function(workbook) {
        var spreadsheets = [];
        workbook.SheetNames.forEach(function(sheetName) {
            var spreadsheet = {};
            spreadsheet.rows = [];
            spreadsheet.columns = [];
            spreadsheet.data = [];
            spreadsheet.style = {};
            spreadsheet.sheetName = sheetName;

            // Column widths
            var temp = workbook.Sheets[sheetName]['!cols'];
            if (temp && temp.length) {
                for (var i = 0; i < temp.length; i++) {
                    spreadsheet.columns[i] = {};
                    if (temp[i] && temp[i].wpx) {
                        spreadsheet.columns[i].width = temp[i].wpx + 'px';
                    }
                }
            }
            // Rows heights
            var temp = workbook.Sheets[sheetName]['!rows'];
            if (temp && temp.length) {
                for (var i = 0; i < temp.length; i++) {
                    if (temp[i] && temp[i].hpx) {
                        spreadsheet.rows[i] = {};
                        spreadsheet.rows[i].height = temp[i].hpx + 'px';
                    }
                }
            }
            // Merge cells
            var temp = workbook.Sheets[sheetName]['!merges'];
            if (temp && temp.length > 0) {
                spreadsheet.mergeCells = [];
                for (var i = 0; i < temp.length; i++) {
                    var x1 = temp[i].s.c;
                    var y1 = temp[i].s.r;
                    var x2 = temp[i].e.c;
                    var y2 = temp[i].e.r;
                    var key = jexcel.getColumnNameFromId([x1,y1]);
                    spreadsheet.mergeCells[key] = [ x2-x1+1, y2-y1+1 ];
                }
            }
            // Data container
            var max_x = 0;
            var max_y = 0;
            var temp = Object.keys(workbook.Sheets[sheetName]);
            for (var i = 0; i < temp.length; i++) {
                if (temp[i].substr(0,1) != '!') {
                    var cell = workbook.Sheets[sheetName][temp[i]];
                    var info = jexcel.getIdFromColumnName(temp[i], true);
                    if (! spreadsheet.data[info[1]]) {
                        spreadsheet.data[info[1]] = [];
                    }
                    spreadsheet.data[info[1]][info[0]] = cell.f ? '=' + cell.f : cell.w;
                    if (max_x < info[0]) {
                        max_x = info[0];
                    }
                    if (max_y < info[1]) {
                        max_y = info[1];
                    }
                    // Style
                    if (cell.style && Object.keys(cell.style).length > 0) {
                        spreadsheet.style[temp[i]] = cell.style;
                    }
                    if (cell.s && cell.s.fgColor) {
                        if (spreadsheet.style[temp[i]]) {
                            spreadsheet.style[temp[i]] += ';';
                        }
                        spreadsheet.style[temp[i]] += 'background-color:#' + cell.s.fgColor.rgb;
                    }
                }
            }
            var numColumns = spreadsheet.columns;
            for (var j = 0; j <= max_y; j++) {
                for (var i = 0; i <= max_x; i++) {
                    if (! spreadsheet.data[j]) {
                        spreadsheet.data[j] = [];
                    }
                    if (! spreadsheet.data[j][i]) {
                        if (numColumns < i) {
                            spreadsheet.data[j][i] = '';
                        }
                    }
                }
            }
            spreadsheets.push(spreadsheet);
        });

        return spreadsheets;
    }

    var oReq;
    oReq = new XMLHttpRequest();
    oReq.open("GET", file, true);

    if(typeof Uint8Array !== 'undefined') {
        oReq.responseType = "arraybuffer";
        oReq.onload = function(e) {
            var arraybuffer = oReq.response;
            var data = new Uint8Array(arraybuffer);
            var wb = XLSX.read(data, {type:"array", cellFormula:true, cellStyles:true });
            __callback(convert(wb))
        };
    } else {
        oReq.setRequestHeader("Accept-Charset", "x-user-defined");  
        oReq.onreadystatechange = function() { if(oReq.readyState == 4 && oReq.status == 200) {
            var ff = convertResponseBodyToText(oReq.responseBody);
            var wb = XLSX.read(ff, {type:"binary", cellFormula:true, cellStyles:true });
            __callback(convert(wb))
        }};
    }

    oReq.send();
}

/**
 * Valid international letter
 */

jexcel.validLetter = function (text) {
    var regex = /([\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\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\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\u2183\u2184\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\u3006\u3031-\u3035\u303B\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-\uA6E5\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-\u0400-\u04FF']+)/g;
    return text.match(regex) ? 1 : 0;
}

/**
 * Helper injectArray
 */
jexcel.injectArray = function(o, idx, arr) {
    return o.slice(0, idx).concat(arr).concat(o.slice(idx));
}

/**
 * Get letter based on a number
 * 
 * @param integer i
 * @return string letter
 */
jexcel.getColumnName = function(i) {
    var letter = '';
    if (i > 701) {
        letter += String.fromCharCode(64 + parseInt(i / 676));
        letter += String.fromCharCode(64 + parseInt((i % 676) / 26));
    } else if (i > 25) {
        letter += String.fromCharCode(64 + parseInt(i / 26));
    }
    letter += String.fromCharCode(65 + (i % 26));

    return letter;
}

/**
 * Convert excel like column to jexcel id
 * 
 * @param string id
 * @return string id
 */
jexcel.getIdFromColumnName = function (id, arr) {
    // Get the letters
    var t = /^[a-zA-Z]+/.exec(id);

    if (t) {
        // Base 26 calculation
        var code = 0;
        for (var i = 0; i < t[0].length; i++) {
            code += parseInt(t[0].charCodeAt(i) - 64) * Math.pow(26, (t[0].length - 1 - i));
        }
        code--;
        // Make sure jexcel starts on zero
        if (code < 0) {
            code = 0;
        }

        // Number
        var number = parseInt(/[0-9]+$/.exec(id));
        if (number > 0) {
            number--;
        }

        if (arr == true) {
            id = [ code, number ];
        } else {
            id = code + '-' + number;
        }
    }

    return id;
}

/**
 * Convert jexcel id to excel like column name
 * 
 * @param string id
 * @return string id
 */
jexcel.getColumnNameFromId = function (cellId) {
    if (! Array.isArray(cellId)) {
        cellId = cellId.split('-');
    }

    return jexcel.getColumnName(parseInt(cellId[0])) + (parseInt(cellId[1]) + 1);
}

/**
 * Verify element inside jexcel table
 * 
 * @param string id
 * @return string id
 */
jexcel.getElement = function(element) {
    var jexcelSection = 0;
    var jexcelElement = 0;

    function path (element) {
        if (element.className) {
            if (element.classList.contains('jexcel_container')) {
                jexcelElement = element;
            }
        }

        if (element.tagName == 'THEAD') {
            jexcelSection = 1;
        } else if (element.tagName == 'TBODY') {
            jexcelSection = 2;
        }

        if (element.parentNode) {
            path(element.parentNode);
        }
    }

    path(element);

    return [ jexcelElement, jexcelSection ];
}

jexcel.doubleDigitFormat = function(v) {
    v = ''+v;
    if (v.length == 1) {
        v = '0'+v;
    }
    return v;
}

/**
 * Jquery Support
 */
if (typeof(jQuery) != 'undefined') {
    (function($){
        $.fn.jexcel = function(method) {
            var spreadsheetContainer = $(this).get(0);
            if (! spreadsheetContainer.jexcel) {
                return jexcel($(this).get(0), arguments[0]);
            } else {
                return spreadsheetContainer.jexcel[method].apply(this, Array.prototype.slice.call( arguments, 1 ));
            }
        };

    })(jQuery);
}


// Based on sutoiku work (https://github.com/sutoiku)

var error = (function() {
    var exports = {};

    exports.nil = new Error('#NULL!');
    exports.div0 = new Error('#DIV/0!');
    exports.value = new Error('#VALUE!');
    exports.ref = new Error('#REF!');
    exports.name = new Error('#NAME?');
    exports.num = new Error('#NUM!');
    exports.na = new Error('#N/A');
    exports.error = new Error('#ERROR!');
    exports.data = new Error('#GETTING_DATA');

    return exports;
})();

var utils = (function() {
    var exports = {};

    exports.flattenShallow = function(array) {
        if (!array || !array.reduce) {
            return array;
        }

        return array.reduce(function(a, b) {
            var aIsArray = Array.isArray(a);
            var bIsArray = Array.isArray(b);

            if (aIsArray && bIsArray) {
                return a.concat(b);
            }
            if (aIsArray) {
                a.push(b);

                return a;
            }
            if (bIsArray) {
                return [ a ].concat(b);
            }

            return [ a, b ];
        });
    };

    exports.isFlat = function(array) {
        if (!array) {
            return false;
        }

        for (var i = 0; i < array.length; ++i) {
            if (Array.isArray(array[i])) {
                return false;
            }
        }

        return true;
    };

    exports.flatten = function() {
        var result = exports.argsToArray.apply(null, arguments);

        while (!exports.isFlat(result)) {
            result = exports.flattenShallow(result);
        }

        return result;
    };

    exports.argsToArray = function(args) {
        var result = [];

        exports.arrayEach(args, function(value) {
            result.push(value);
        });

        return result;
    };

    exports.numbers = function() {
        var possibleNumbers = this.flatten.apply(null, arguments);
        return possibleNumbers.filter(function(el) {
            return typeof el === 'number';
        });
    };

    exports.cleanFloat = function(number) {
        var power = 1e14;
        return Math.round(number * power) / power;
    };

    exports.parseBool = function(bool) {
        if (typeof bool === 'boolean') {
            return bool;
        }

        if (bool instanceof Error) {
            return bool;
        }

        if (typeof bool === 'number') {
            return bool !== 0;
        }

        if (typeof bool === 'string') {
            var up = bool.toUpperCase();
            if (up === 'TRUE') {
                return true;
            }

            if (up === 'FALSE') {
                return false;
            }
        }

        if (bool instanceof Date && !isNaN(bool)) {
            return true;
        }

        return error.value;
    };

    exports.parseNumber = function(string) {
        if (string === undefined || string === '') {
            return error.value;
        }
        if (!isNaN(string)) {
            return parseFloat(string);
        }

        return error.value;
    };

    exports.parseNumberArray = function(arr) {
        var len;

        if (!arr || (len = arr.length) === 0) {
            return error.value;
        }

        var parsed;

        while (len--) {
            parsed = exports.parseNumber(arr[len]);
            if (parsed === error.value) {
                return parsed;
            }
            arr[len] = parsed;
        }

        return arr;
    };

    exports.parseMatrix = function(matrix) {
        var n;

        if (!matrix || (n = matrix.length) === 0) {
            return error.value;
        }
        var pnarr;

        for (var i = 0; i < matrix.length; i++) {
            pnarr = exports.parseNumberArray(matrix[i]);
            matrix[i] = pnarr;

            if (pnarr instanceof Error) {
                return pnarr;
            }
        }

        return matrix;
    };

    var d1900 = new Date(Date.UTC(1900, 0, 1));
    exports.parseDate = function(date) {
        if (!isNaN(date)) {
            if (date instanceof Date) {
                return new Date(date);
            }
            var d = parseInt(date, 10);
            if (d < 0) {
                return error.num;
            }
            if (d <= 60) {
                return new Date(d1900.getTime() + (d - 1) * 86400000);
            }
            return new Date(d1900.getTime() + (d - 2) * 86400000);
        }
        if (typeof date === 'string') {
            date = new Date(date);
            if (!isNaN(date)) {
                return date;
            }
        }
        return error.value;
    };

    exports.parseDateArray = function(arr) {
        var len = arr.length;
        var parsed;
        while (len--) {
            parsed = this.parseDate(arr[len]);
            if (parsed === error.value) {
                return parsed;
            }
            arr[len] = parsed;
        }
        return arr;
    };

    exports.anyIsError = function() {
        var n = arguments.length;
        while (n--) {
            if (arguments[n] instanceof Error) {
                return true;
            }
        }
        return false;
    };

    exports.arrayValuesToNumbers = function(arr) {
        var n = arr.length;
        var el;
        while (n--) {
            el = arr[n];
            if (typeof el === 'number') {
                continue;
            }
            if (el === true) {
                arr[n] = 1;
                continue;
            }
            if (el === false) {
                arr[n] = 0;
                continue;
            }
            if (typeof el === 'string') {
                var number = this.parseNumber(el);
                if (number instanceof Error) {
                    arr[n] = 0;
                } else {
                    arr[n] = number;
                }
            }
        }
        return arr;
    };

    exports.rest = function(array, idx) {
        idx = idx || 1;
        if (!array || typeof array.slice !== 'function') {
            return array;
        }
        return array.slice(idx);
    };

    exports.initial = function(array, idx) {
        idx = idx || 1;
        if (!array || typeof array.slice !== 'function') {
            return array;
        }
        return array.slice(0, array.length - idx);
    };

    exports.arrayEach = function(array, iteratee) {
        var index = -1, length = array.length;

        while (++index < length) {
            if (iteratee(array[index], index, array) === false) {
                break;
            }
        }

        return array;
    };

    exports.transpose = function(matrix) {
        if (!matrix) {
            return error.value;
        }

        return matrix[0].map(function(col, i) {
            return matrix.map(function(row) {
                return row[i];
            });
        });
    };

    return exports;
})();

jexcel.methods = {};

jexcel.methods.datetime = (function() {
    var exports = {};

    var d1900 = new Date(1900, 0, 1);
    var WEEK_STARTS = [
        undefined,
        0,
        1,
        undefined,
        undefined,
        undefined,
        undefined,
        undefined,
        undefined,
        undefined,
        undefined,
        undefined,
        1,
        2,
        3,
        4,
        5,
        6,
        0
    ];
    var WEEK_TYPES = [
        [],
        [1, 2, 3, 4, 5, 6, 7],
        [7, 1, 2, 3, 4, 5, 6],
        [6, 0, 1, 2, 3, 4, 5],
        [],
        [],
        [],
        [],
        [],
        [],
        [],
        [7, 1, 2, 3, 4, 5, 6],
        [6, 7, 1, 2, 3, 4, 5],
        [5, 6, 7, 1, 2, 3, 4],
        [4, 5, 6, 7, 1, 2, 3],
        [3, 4, 5, 6, 7, 1, 2],
        [2, 3, 4, 5, 6, 7, 1],
        [1, 2, 3, 4, 5, 6, 7]
    ];
    var WEEKEND_TYPES = [
        [],
        [6, 0],
        [0, 1],
        [1, 2],
        [2, 3],
        [3, 4],
        [4, 5],
        [5, 6],
        undefined,
        undefined,
        undefined, [0, 0],
        [1, 1],
        [2, 2],
        [3, 3],
        [4, 4],
        [5, 5],
        [6, 6]
    ];

    exports.DATE = function(year, month, day) {
        year = utils.parseNumber(year);
        month = utils.parseNumber(month);
        day = utils.parseNumber(day);
        if (utils.anyIsError(year, month, day)) {
            return error.value;
        }
        if (year < 0 || month < 0 || day < 0) {
            return error.num;
        }
        var date = new Date(year, month - 1, day);
        return date;
    };

    exports.DATEVALUE = function(date_text) {
        if (typeof date_text !== 'string') {
            return error.value;
        }
        var date = Date.parse(date_text);
        if (isNaN(date)) {
            return error.value;
        }
        if (date <= -2203891200000) {
            return (date - d1900) / 86400000 + 1;
        }
        return (date - d1900) / 86400000 + 2;
    };

    exports.DAY = function(serial_number) {
        var date = utils.parseDate(serial_number);
        if (date instanceof Error) {
            return date;
        }
        return date.getDate();
    };

    exports.DAYS = function(end_date, start_date) {
        end_date = utils.parseDate(end_date);
        start_date = utils.parseDate(start_date);
        if (end_date instanceof Error) {
            return end_date;
        }
        if (start_date instanceof Error) {
            return start_date;
        }
        return serial(end_date) - serial(start_date);
    };

    exports.DAYS360 = function(start_date, end_date, method) {
    };

    exports.EDATE = function(start_date, months) {
        start_date = utils.parseDate(start_date);
        if (start_date instanceof Error) {
            return start_date;
        }
        if (isNaN(months)) {
            return error.value;
        }
        months = parseInt(months, 10);
        start_date.setMonth(start_date.getMonth() + months);
        return serial(start_date);
    };

    exports.EOMONTH = function(start_date, months) {
        start_date = utils.parseDate(start_date);
        if (start_date instanceof Error) {
            return start_date;
        }
        if (isNaN(months)) {
            return error.value;
        }
        months = parseInt(months, 10);
        return serial(new Date(start_date.getFullYear(), start_date.getMonth() + months + 1, 0));
    };

    exports.HOUR = function(serial_number) {
        serial_number = utils.parseDate(serial_number);
        if (serial_number instanceof Error) {
            return serial_number;
        }
        return serial_number.getHours();
    };

    exports.INTERVAL = function(second) {
        if (typeof second !== 'number' && typeof second !== 'string') {
            return error.value;
        } else {
            second = parseInt(second, 10);
        }

        var year  = Math.floor(second/946080000);
        second    = second%946080000;
        var month = Math.floor(second/2592000);
        second    = second%2592000;
        var day   = Math.floor(second/86400);
        second    = second%86400;

        var hour  = Math.floor(second/3600);
        second    = second%3600;
        var min   = Math.floor(second/60);
        second    = second%60;
        var sec   = second;

        year  = (year  > 0) ? year  + 'Y' : '';
        month = (month > 0) ? month + 'M' : '';
        day   = (day   > 0) ? day   + 'D' : '';
        hour  = (hour  > 0) ? hour  + 'H' : '';
        min   = (min   > 0) ? min   + 'M' : '';
        sec   = (sec   > 0) ? sec   + 'S' : '';

        return 'P' + year + month + day + 'T' + hour + min + sec;
    };

    exports.ISOWEEKNUM = function(date) {
        date = utils.parseDate(date);
        if (date instanceof Error) {
            return date;
        }

        date.setHours(0, 0, 0);
        date.setDate(date.getDate() + 4 - (date.getDay() || 7));
        var yearStart = new Date(date.getFullYear(), 0, 1);
        return Math.ceil((((date - yearStart) / 86400000) + 1) / 7);
    };

    exports.MINUTE = function(serial_number) {
        serial_number = utils.parseDate(serial_number);
        if (serial_number instanceof Error) {
            return serial_number;
        }
        return serial_number.getMinutes();
    };

    exports.MONTH = function(serial_number) {
        serial_number = utils.parseDate(serial_number);
        if (serial_number instanceof Error) {
            return serial_number;
        }
        return serial_number.getMonth() + 1;
    };

    exports.NETWORKDAYS = function(start_date, end_date, holidays) {
    };

    exports.NETWORKDAYS.INTL = function(start_date, end_date, weekend, holidays) {
    };

    exports.NOW = function() {
        return new Date();
    };

    exports.SECOND = function(serial_number) {
        serial_number = utils.parseDate(serial_number);
        if (serial_number instanceof Error) {
            return serial_number;
        }
        return serial_number.getSeconds();
    };

    exports.TIME = function(hour, minute, second) {
        hour = utils.parseNumber(hour);
        minute = utils.parseNumber(minute);
        second = utils.parseNumber(second);
        if (utils.anyIsError(hour, minute, second)) {
            return error.value;
        }
        if (hour < 0 || minute < 0 || second < 0) {
            return error.num;
        }
        return (3600 * hour + 60 * minute + second) / 86400;
    };

    exports.TIMEVALUE = function(time_text) {
        time_text = utils.parseDate(time_text);
        if (time_text instanceof Error) {
            return time_text;
        }
        return (3600 * time_text.getHours() + 60 * time_text.getMinutes() + time_text.getSeconds()) / 86400;
    };

    exports.TODAY = function() {
        return new Date();
    };

    exports.WEEKDAY = function(serial_number, return_type) {
        serial_number = utils.parseDate(serial_number);
        if (serial_number instanceof Error) {
            return serial_number;
        }
        if (return_type === undefined) {
            return_type = 1;
        }
        var day = serial_number.getDay();
        return WEEK_TYPES[return_type][day];
    };

    exports.WEEKNUM = function(serial_number, return_type) {
    };

    exports.WORKDAY = function(start_date, days, holidays) {
    };

    exports.WORKDAY.INTL = function(start_date, days, weekend, holidays) {
    };

    exports.YEAR = function(serial_number) {
        serial_number = utils.parseDate(serial_number);
        if (serial_number instanceof Error) {
            return serial_number;
        }
        return serial_number.getFullYear();
    };

    function isLeapYear(year) {
        return new Date(year, 1, 29).getMonth() === 1;
    }

    exports.YEARFRAC = function(start_date, end_date, basis) {
    };

    function serial(date) {
        var addOn = (date > -2203891200000)?2:1;
        return (date - d1900) / 86400000 + addOn;
    }

    return exports;
})();

jexcel.methods.database = (function() {
    var exports = {};

    function compact(array) {
        if (!array) {
            return array;
        }
        var result = [];
        for (var i = 0; i < array.length; ++i) {
            if (!array[i]) {
                continue;
            }
            result.push(array[i]);
        }
        return result;
    }

    exports.FINDFIELD = function(database, title) {
        var index = null;
        for (var i = 0; i < database.length; i++) {
            if (database[i][0] === title) {
                index = i;
                break;
            }
        }

        // Return error if the input field title is incorrect
        if (index == null) {
            return error.value;
        }
        return index;
    };

    function findResultIndex(database, criterias) {
        var matches = {};
        for (var i = 1; i < database[0].length; ++i) {
            matches[i] = true;
        }
        var maxCriteriaLength = criterias[0].length;
        for (i = 1; i < criterias.length; ++i) {
            if (criterias[i].length > maxCriteriaLength) {
                maxCriteriaLength = criterias[i].length;
            }
        }

        for (var k = 1; k < database.length; ++k) {
            for (var l = 1; l < database[k].length; ++l) {
                var currentCriteriaResult = false;
                var hasMatchingCriteria = false;
                for (var j = 0; j < criterias.length; ++j) {
                    var criteria = criterias[j];
                    if (criteria.length < maxCriteriaLength) {
                        continue;
                    }

                    var criteriaField = criteria[0];
                    if (database[k][0] !== criteriaField) {
                        continue;
                    }
                    hasMatchingCriteria = true;
                    for (var p = 1; p < criteria.length; ++p) {
                        currentCriteriaResult = currentCriteriaResult
                                || eval(database[k][l] + criteria[p]); // jshint
                                                                        // ignore:line
                    }
                }
                if (hasMatchingCriteria) {
                    matches[l] = matches[l] && currentCriteriaResult;
                }
            }
        }

        var result = [];
        for (var n = 0; n < database[0].length; ++n) {
            if (matches[n]) {
                result.push(n - 1);
            }
        }
        return result;
    }

    // Database functions
    exports.DAVERAGE = function(database, field, criteria) {
        // Return error if field is not a number and not a string
        if (isNaN(field) && (typeof field !== "string")) {
            return error.value;
        }
        var resultIndexes = findResultIndex(database, criteria);
        var targetFields = [];
        if (typeof field === "string") {
            var index = exports.FINDFIELD(database, field);
            targetFields = utils.rest(database[index]);
        } else {
            targetFields = utils.rest(database[field]);
        }
        var sum = 0;
        for (var i = 0; i < resultIndexes.length; i++) {
            sum += targetFields[resultIndexes[i]];
        }
        return resultIndexes.length === 0 ? error.div0 : sum / resultIndexes.length;
    };

    exports.DCOUNT = function(database, field, criteria) {
    };

    exports.DCOUNTA = function(database, field, criteria) {
    };

    exports.DGET = function(database, field, criteria) {
        // Return error if field is not a number and not a string
        if (isNaN(field) && (typeof field !== "string")) {
            return error.value;
        }
        var resultIndexes = findResultIndex(database, criteria);
        var targetFields = [];
        if (typeof field === "string") {
            var index = exports.FINDFIELD(database, field);
            targetFields = utils.rest(database[index]);
        } else {
            targetFields = utils.rest(database[field]);
        }
        // Return error if no record meets the criteria
        if (resultIndexes.length === 0) {
            return error.value;
        }
        // Returns the #NUM! error value because more than one record meets the
        // criteria
        if (resultIndexes.length > 1) {
            return error.num;
        }

        return targetFields[resultIndexes[0]];
    };

    exports.DMAX = function(database, field, criteria) {
        // Return error if field is not a number and not a string
        if (isNaN(field) && (typeof field !== "string")) {
            return error.value;
        }
        var resultIndexes = findResultIndex(database, criteria);
        var targetFields = [];
        if (typeof field === "string") {
            var index = exports.FINDFIELD(database, field);
            targetFields = utils.rest(database[index]);
        } else {
            targetFields = utils.rest(database[field]);
        }
        var maxValue = targetFields[resultIndexes[0]];
        for (var i = 1; i < resultIndexes.length; i++) {
            if (maxValue < targetFields[resultIndexes[i]]) {
                maxValue = targetFields[resultIndexes[i]];
            }
        }
        return maxValue;
    };

    exports.DMIN = function(database, field, criteria) {
        // Return error if field is not a number and not a string
        if (isNaN(field) && (typeof field !== "string")) {
            return error.value;
        }
        var resultIndexes = findResultIndex(database, criteria);
        var targetFields = [];
        if (typeof field === "string") {
            var index = exports.FINDFIELD(database, field);
            targetFields = utils.rest(database[index]);
        } else {
            targetFields = utils.rest(database[field]);
        }
        var minValue = targetFields[resultIndexes[0]];
        for (var i = 1; i < resultIndexes.length; i++) {
            if (minValue > targetFields[resultIndexes[i]]) {
                minValue = targetFields[resultIndexes[i]];
            }
        }
        return minValue;
    };

    exports.DPRODUCT = function(database, field, criteria) {
        // Return error if field is not a number and not a string
        if (isNaN(field) && (typeof field !== "string")) {
            return error.value;
        }
        var resultIndexes = findResultIndex(database, criteria);
        var targetFields = [];
        if (typeof field === "string") {
            var index = exports.FINDFIELD(database, field);
            targetFields = utils.rest(database[index]);
        } else {
            targetFields = utils.rest(database[field]);
        }
        var targetValues = [];
        for (var i = 0; i < resultIndexes.length; i++) {
            targetValues[i] = targetFields[resultIndexes[i]];
        }
        targetValues = compact(targetValues);
        var result = 1;
        for (i = 0; i < targetValues.length; i++) {
            result *= targetValues[i];
        }
        return result;
    };

    exports.DSTDEV = function(database, field, criteria) {
    };

    exports.DSTDEVP = function(database, field, criteria) {
    };

    exports.DSUM = function(database, field, criteria) {
    };

    exports.DVAR = function(database, field, criteria) {
    };

    exports.DVARP = function(database, field, criteria) {
    };

    exports.MATCH = function(lookupValue, lookupArray, matchType) {
        if (!lookupValue && !lookupArray) {
            return error.na;
        }
        if (arguments.length === 2) {
            matchType = 1;
        }
        if (!(lookupArray instanceof Array)) {
            return error.na;
        }
        if (matchType !== -1 && matchType !== 0 && matchType !== 1) {
            return error.na;
        }

        var index;
        var indexValue;

        for (var idx = 0; idx < lookupArray.length; idx++) {
            if (matchType === 1) {
                if (lookupArray[idx] === lookupValue) {
                    return idx + 1;
                } else if (lookupArray[idx] < lookupValue) {
                    if (!indexValue) {
                        index = idx + 1;
                        indexValue = lookupArray[idx];
                    } else if (lookupArray[idx] > indexValue) {
                        index = idx + 1;
                        indexValue = lookupArray[idx];
                    }
                }
            } else if (matchType === 0) {
                if (typeof lookupValue === 'string') {
                    lookupValue = lookupValue.replace(/\?/g, '.');
                    if (lookupArray[idx].toLowerCase().match(lookupValue.toLowerCase())) {
                        return idx + 1;
                    }
                } else {
                    if (lookupArray[idx] === lookupValue) {
                        return idx + 1;
                    }
                }
            } else if (matchType === -1) {
                if (lookupArray[idx] === lookupValue) {
                    return idx + 1;
                } else if (lookupArray[idx] > lookupValue) {
                    if (!indexValue) {
                        index = idx + 1;
                        indexValue = lookupArray[idx];
                    } else if (lookupArray[idx] < indexValue) {
                        index = idx + 1;
                        indexValue = lookupArray[idx];
                    }
                }
            }
        }

        return index ? index : error.na;
    };

    return exports;
})();

jexcel.methods.engineering = (function() {
    var exports = {};

    function isValidBinaryNumber(number) {
        return (/^[01]{1,10}$/).test(number);
    }

    exports.BESSELI = function(x, n) {
    };

    exports.BESSELJ = function(x, n) {
    };

    exports.BESSELK = function(x, n) {
    };

    exports.BESSELY = function(x, n) {
    };

    exports.BIN2DEC = function(number) {
        // Return error if number is not binary or contains more than 10
        // characters (10 digits)
        if (!isValidBinaryNumber(number)) {
            return error.num;
        }

        // Convert binary number to decimal
        var result = parseInt(number, 2);

        // Handle negative numbers
        var stringified = number.toString();
        if (stringified.length === 10 && stringified.substring(0, 1) === '1') {
            return parseInt(stringified.substring(1), 2) - 512;
        } else {
            return result;
        }
    };

    exports.BIN2HEX = function(number, places) {
        // Return error if number is not binary or contains more than 10
        // characters (10 digits)
        if (!isValidBinaryNumber(number)) {
            return error.num;
        }

        // Ignore places and return a 10-character hexadecimal number if number
        // is negative
        var stringified = number.toString();
        if (stringified.length === 10 && stringified.substring(0, 1) === '1') {
            return (1099511627264 + parseInt(stringified.substring(1), 2)).toString(16);
        }

        // Convert binary number to hexadecimal
        var result = parseInt(number, 2).toString(16);

        // Return hexadecimal number using the minimum number of characters
        // necessary if places is undefined
        if (places === undefined) {
            return result;
        } else {
            // Return error if places is nonnumeric
            if (isNaN(places)) {
              return error.value;
            }

            // Return error if places is negative
            if (places < 0) {
              return error.num;
            }

            // Truncate places in case it is not an integer
            places = Math.floor(places);

            // Pad return value with leading 0s (zeros) if necessary (using
            // Underscore.string)
            return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
        }
    };

    exports.BIN2OCT = function(number, places) {
        // Return error if number is not binary or contains more than 10
        // characters (10 digits)
        if (!isValidBinaryNumber(number)) {
            return error.num;
        }

        // Ignore places and return a 10-character octal number if number is
        // negative
        var stringified = number.toString();
        if (stringified.length === 10 && stringified.substring(0, 1) === '1') {
            return (1073741312 + parseInt(stringified.substring(1), 2)).toString(8);
        }

        // Convert binary number to octal
        var result = parseInt(number, 2).toString(8);

        // Return octal number using the minimum number of characters necessary
        // if places is undefined
        if (places === undefined) {
            return result;
        } else {
            // Return error if places is nonnumeric
            if (isNaN(places)) {
              return error.value;
            }

            // Return error if places is negative
            if (places < 0) {
              return error.num;
            }

            // Truncate places in case it is not an integer
            places = Math.floor(places);

            // Pad return value with leading 0s (zeros) if necessary (using
            // Underscore.string)
            return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
        }
    };

    exports.BITAND = function(number1, number2) {
        // Return error if either number is a non-numeric value
        number1 = utils.parseNumber(number1);
        number2 = utils.parseNumber(number2);
        if (utils.anyIsError(number1, number2)) {
            return error.value;
        }

        // Return error if either number is less than 0
        if (number1 < 0 || number2 < 0) {
            return error.num;
        }

        // Return error if either number is a non-integer
        if (Math.floor(number1) !== number1 || Math.floor(number2) !== number2) {
            return error.num;
        }

        // Return error if either number is greater than (2^48)-1
        if (number1 > 281474976710655 || number2 > 281474976710655) {
            return error.num;
        }

        // Return bitwise AND of two numbers
        return number1 & number2;
    };

    exports.BITLSHIFT = function(number, shift) {
        number = utils.parseNumber(number);
        shift = utils.parseNumber(shift);
        if (utils.anyIsError(number, shift)) {
            return error.value;
        }

        // Return error if number is less than 0
        if (number < 0) {
            return error.num;
        }

        // Return error if number is a non-integer
        if (Math.floor(number) !== number) {
            return error.num;
        }

        // Return error if number is greater than (2^48)-1
        if (number > 281474976710655) {
            return error.num;
        }

        // Return error if the absolute value of shift is greater than 53
        if (Math.abs(shift) > 53) {
            return error.num;
        }

        // Return number shifted by shift bits to the left or to the right if
        // shift is negative
        return (shift >= 0) ? number << shift : number >> -shift;
    };

    exports.BITOR = function(number1, number2) {
        number1 = utils.parseNumber(number1);
        number2 = utils.parseNumber(number2);
        if (utils.anyIsError(number1, number2)) {
            return error.value;
        }

        // Return error if either number is less than 0
        if (number1 < 0 || number2 < 0) {
            return error.num;
        }

        // Return error if either number is a non-integer
        if (Math.floor(number1) !== number1 || Math.floor(number2) !== number2) {
            return error.num;
        }

        // Return error if either number is greater than (2^48)-1
        if (number1 > 281474976710655 || number2 > 281474976710655) {
            return error.num;
        }

        // Return bitwise OR of two numbers
        return number1 | number2;
    };

    exports.BITRSHIFT = function(number, shift) {
        number = utils.parseNumber(number);
        shift = utils.parseNumber(shift);
        if (utils.anyIsError(number, shift)) {
            return error.value;
        }

        // Return error if number is less than 0
        if (number < 0) {
            return error.num;
        }

        // Return error if number is a non-integer
        if (Math.floor(number) !== number) {
            return error.num;
        }

        // Return error if number is greater than (2^48)-1
        if (number > 281474976710655) {
            return error.num;
        }

        // Return error if the absolute value of shift is greater than 53
        if (Math.abs(shift) > 53) {
            return error.num;
        }

        // Return number shifted by shift bits to the right or to the left if
        // shift is negative
        return (shift >= 0) ? number >> shift : number << -shift;
    };

    exports.BITXOR = function(number1, number2) {
        number1 = utils.parseNumber(number1);
        number2 = utils.parseNumber(number2);
        if (utils.anyIsError(number1, number2)) {
            return error.value;
        }

        // Return error if either number is less than 0
        if (number1 < 0 || number2 < 0) {
            return error.num;
        }

        // Return error if either number is a non-integer
        if (Math.floor(number1) !== number1 || Math.floor(number2) !== number2) {
            return error.num;
        }

        // Return error if either number is greater than (2^48)-1
        if (number1 > 281474976710655 || number2 > 281474976710655) {
            return error.num;
        }

        // Return bitwise XOR of two numbers
        return number1 ^ number2;
    };

    exports.COMPLEX = function(real, imaginary, suffix) {
        real = utils.parseNumber(real);
        imaginary = utils.parseNumber(imaginary);
        if (utils.anyIsError(real, imaginary)) {
            return real;
        }

        // Set suffix
        suffix = (suffix === undefined) ? 'i' : suffix;

        // Return error if suffix is neither "i" nor "j"
        if (suffix !== 'i' && suffix !== 'j') {
            return error.value;
        }

        // Return complex number
        if (real === 0 && imaginary === 0) {
            return 0;
        } else if (real === 0) {
            return (imaginary === 1) ? suffix : imaginary.toString() + suffix;
        } else if (imaginary === 0) {
            return real.toString();
        } else {
            var sign = (imaginary > 0) ? '+' : '';
            return real.toString() + sign + ((imaginary === 1) ? suffix : imaginary.toString() + suffix);
        }
    };

    exports.CONVERT = function(number, from_unit, to_unit) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }

        // List of units supported by CONVERT and units defined by the
        // International System of Units
        // [Name, Symbol, Alternate symbols, Quantity, ISU, CONVERT, Conversion
        // ratio]
        var units = [
            ["a.u. of action", "?", null, "action", false, false, 1.05457168181818e-34],
            ["a.u. of charge", "e", null, "electric_charge", false, false, 1.60217653141414e-19],
            ["a.u. of energy", "Eh", null, "energy", false, false, 4.35974417757576e-18],
            ["a.u. of length", "a?", null, "length", false, false, 5.29177210818182e-11],
            ["a.u. of mass", "m?", null, "mass", false, false, 9.10938261616162e-31],
            ["a.u. of time", "?/Eh", null, "time", false, false, 2.41888432650516e-17],
            ["admiralty knot", "admkn", null, "speed", false, true, 0.514773333],
            ["ampere", "A", null, "electric_current", true, false, 1],
            ["ampere per meter", "A/m", null, "magnetic_field_intensity", true, false, 1],
            ["ångström", "Å", ["ang"], "length", false, true, 1e-10],
            ["are", "ar", null, "area", false, true, 100],
            ["astronomical unit", "ua", null, "length", false, false, 1.49597870691667e-11],
            ["bar", "bar", null, "pressure", false, false, 100000],
            ["barn", "b", null, "area", false, false, 1e-28],
            ["becquerel", "Bq", null, "radioactivity", true, false, 1],
            ["bit", "bit", ["b"], "information", false, true, 1],
            ["btu", "BTU", ["btu"], "energy", false, true, 1055.05585262],
            ["byte", "byte", null, "information", false, true, 8],
            ["candela", "cd", null, "luminous_intensity", true, false, 1],
            ["candela per square metre", "cd/m?", null, "luminance", true, false, 1],
            ["coulomb", "C", null, "electric_charge", true, false, 1],
            ["cubic ångström", "ang3", ["ang^3"], "volume", false, true, 1e-30],
            ["cubic foot", "ft3", ["ft^3"], "volume", false, true, 0.028316846592],
            ["cubic inch", "in3", ["in^3"], "volume", false, true, 0.000016387064],
            ["cubic light-year", "ly3", ["ly^3"], "volume", false, true, 8.46786664623715e-47],
            ["cubic metre", "m?", null, "volume", true, true, 1],
            ["cubic mile", "mi3", ["mi^3"], "volume", false, true, 4168181825.44058],
            ["cubic nautical mile", "Nmi3", ["Nmi^3"], "volume", false, true, 6352182208],
            ["cubic Pica", "Pica3", ["Picapt3", "Pica^3", "Picapt^3"], "volume", false, true, 7.58660370370369e-8],
            ["cubic yard", "yd3", ["yd^3"], "volume", false, true, 0.764554857984],
            ["cup", "cup", null, "volume", false, true, 0.0002365882365],
            ["dalton", "Da", ["u"], "mass", false, false, 1.66053886282828e-27],
            ["day", "d", ["day"], "time", false, true, 86400],
            ["degree", "°", null, "angle", false, false, 0.0174532925199433],
            ["degrees Rankine", "Rank", null, "temperature", false, true, 0.555555555555556],
            ["dyne", "dyn", ["dy"], "force", false, true, 0.00001],
            ["electronvolt", "eV", ["ev"], "energy", false, true, 1.60217656514141],
            ["ell", "ell", null, "length", false, true, 1.143],
            ["erg", "erg", ["e"], "energy", false, true, 1e-7],
            ["farad", "F", null, "electric_capacitance", true, false, 1],
            ["fluid ounce", "oz", null, "volume", false, true, 0.0000295735295625],
            ["foot", "ft", null, "length", false, true, 0.3048],
            ["foot-pound", "flb", null, "energy", false, true, 1.3558179483314],
            ["gal", "Gal", null, "acceleration", false, false, 0.01],
            ["gallon", "gal", null, "volume", false, true, 0.003785411784],
            ["gauss", "G", ["ga"], "magnetic_flux_density", false, true, 1],
            ["grain", "grain", null, "mass", false, true, 0.0000647989],
            ["gram", "g", null, "mass", false, true, 0.001],
            ["gray", "Gy", null, "absorbed_dose", true, false, 1],
            ["gross registered ton", "GRT", ["regton"], "volume", false, true, 2.8316846592],
            ["hectare", "ha", null, "area", false, true, 10000],
            ["henry", "H", null, "inductance", true, false, 1],
            ["hertz", "Hz", null, "frequency", true, false, 1],
            ["horsepower", "HP", ["h"], "power", false, true, 745.69987158227],
            ["horsepower-hour", "HPh", ["hh", "hph"], "energy", false, true, 2684519.538],
            ["hour", "h", ["hr"], "time", false, true, 3600],
            ["imperial gallon (U.K.)", "uk_gal", null, "volume", false, true, 0.00454609],
            ["imperial hundredweight", "lcwt", ["uk_cwt", "hweight"], "mass", false, true, 50.802345],
            ["imperial quart (U.K)", "uk_qt", null, "volume", false, true, 0.0011365225],
            ["imperial ton", "brton", ["uk_ton", "LTON"], "mass", false, true, 1016.046909],
            ["inch", "in", null, "length", false, true, 0.0254],
            ["international acre", "uk_acre", null, "area", false, true, 4046.8564224],
            ["IT calorie", "cal", null, "energy", false, true, 4.1868],
            ["joule", "J", null, "energy", true, true, 1],
            ["katal", "kat", null, "catalytic_activity", true, false, 1],
            ["kelvin", "K", ["kel"], "temperature", true, true, 1],
            ["kilogram", "kg", null, "mass", true, true, 1],
            ["knot", "kn", null, "speed", false, true, 0.514444444444444],
            ["light-year", "ly", null, "length", false, true, 9460730472580800],
            ["litre", "L", ["l", "lt"], "volume", false, true, 0.001],
            ["lumen", "lm", null, "luminous_flux", true, false, 1],
            ["lux", "lx", null, "illuminance", true, false, 1],
            ["maxwell", "Mx", null, "magnetic_flux", false, false, 1e-18],
            ["measurement ton", "MTON", null, "volume", false, true, 1.13267386368],
            ["meter per hour", "m/h", ["m/hr"], "speed", false, true, 0.00027777777777778],
            ["meter per second", "m/s", ["m/sec"], "speed", true, true, 1],
            ["meter per second squared", "m?s??", null, "acceleration", true, false, 1],
            ["parsec", "pc", ["parsec"], "length", false, true, 30856775814671900],
            ["meter squared per second", "m?/s", null, "kinematic_viscosity", true, false, 1],
            ["metre", "m", null, "length", true, true, 1],
            ["miles per hour", "mph", null, "speed", false, true, 0.44704],
            ["millimetre of mercury", "mmHg", null, "pressure", false, false, 133.322],
            ["minute", "?", null, "angle", false, false, 0.000290888208665722],
            ["minute", "min", ["mn"], "time", false, true, 60],
            ["modern teaspoon", "tspm", null, "volume", false, true, 0.000005],
            ["mole", "mol", null, "amount_of_substance", true, false, 1],
            ["morgen", "Morgen", null, "area", false, true, 2500],
            ["n.u. of action", "?", null, "action", false, false, 1.05457168181818e-34],
            ["n.u. of mass", "m?", null, "mass", false, false, 9.10938261616162e-31],
            ["n.u. of speed", "c?", null, "speed", false, false, 299792458],
            ["n.u. of time", "?/(me?c??)", null, "time", false, false, 1.28808866778687e-21],
            ["nautical mile", "M", ["Nmi"], "length", false, true, 1852],
            ["newton", "N", null, "force", true, true, 1],
            ["œrsted", "Oe ", null, "magnetic_field_intensity", false, false, 79.5774715459477],
            ["ohm", "Ω", null, "electric_resistance", true, false, 1],
            ["ounce mass", "ozm", null, "mass", false, true, 0.028349523125],
            ["pascal", "Pa", null, "pressure", true, false, 1],
            ["pascal second", "Pa?s", null, "dynamic_viscosity", true, false, 1],
            ["pferdestärke", "PS", null, "power", false, true, 735.49875],
            ["phot", "ph", null, "illuminance", false, false, 0.0001],
            ["pica (1/6 inch)", "pica", null, "length", false, true, 0.00035277777777778],
            ["pica (1/72 inch)", "Pica", ["Picapt"], "length", false, true, 0.00423333333333333],
            ["poise", "P", null, "dynamic_viscosity", false, false, 0.1],
            ["pond", "pond", null, "force", false, true, 0.00980665],
            ["pound force", "lbf", null, "force", false, true, 4.4482216152605],
            ["pound mass", "lbm", null, "mass", false, true, 0.45359237],
            ["quart", "qt", null, "volume", false, true, 0.000946352946],
            ["radian", "rad", null, "angle", true, false, 1],
            ["second", "?", null, "angle", false, false, 0.00000484813681109536],
            ["second", "s", ["sec"], "time", true, true, 1],
            ["short hundredweight", "cwt", ["shweight"], "mass", false, true, 45.359237],
            ["siemens", "S", null, "electrical_conductance", true, false, 1],
            ["sievert", "Sv", null, "equivalent_dose", true, false, 1],
            ["slug", "sg", null, "mass", false, true, 14.59390294],
            ["square ångström", "ang2", ["ang^2"], "area", false, true, 1e-20],
            ["square foot", "ft2", ["ft^2"], "area", false, true, 0.09290304],
            ["square inch", "in2", ["in^2"], "area", false, true, 0.00064516],
            ["square light-year", "ly2", ["ly^2"], "area", false, true, 8.95054210748189e+31],
            ["square meter", "m?", null, "area", true, true, 1],
            ["square mile", "mi2", ["mi^2"], "area", false, true, 2589988.110336],
            ["square nautical mile", "Nmi2", ["Nmi^2"], "area", false, true, 3429904],
            ["square Pica", "Pica2", ["Picapt2", "Pica^2", "Picapt^2"], "area", false, true, 0.00001792111111111],
            ["square yard", "yd2", ["yd^2"], "area", false, true, 0.83612736],
            ["statute mile", "mi", null, "length", false, true, 1609.344],
            ["steradian", "sr", null, "solid_angle", true, false, 1],
            ["stilb", "sb", null, "luminance", false, false, 0.0001],
            ["stokes", "St", null, "kinematic_viscosity", false, false, 0.0001],
            ["stone", "stone", null, "mass", false, true, 6.35029318],
            ["tablespoon", "tbs", null, "volume", false, true, 0.0000147868],
            ["teaspoon", "tsp", null, "volume", false, true, 0.00000492892],
            ["tesla", "T", null, "magnetic_flux_density", true, true, 1],
            ["thermodynamic calorie", "c", null, "energy", false, true, 4.184],
            ["ton", "ton", null, "mass", false, true, 907.18474],
            ["tonne", "t", null, "mass", false, false, 1000],
            ["U.K. pint", "uk_pt", null, "volume", false, true, 0.00056826125],
            ["U.S. bushel", "bushel", null, "volume", false, true, 0.03523907],
            ["U.S. oil barrel", "barrel", null, "volume", false, true, 0.158987295],
            ["U.S. pint", "pt", ["us_pt"], "volume", false, true, 0.000473176473],
            ["U.S. survey mile", "survey_mi", null, "length", false, true, 1609.347219],
            ["U.S. survey/statute acre", "us_acre", null, "area", false, true, 4046.87261],
            ["volt", "V", null, "voltage", true, false, 1],
            ["watt", "W", null, "power", true, true, 1],
            ["watt-hour", "Wh", ["wh"], "energy", false, true, 3600],
            ["weber", "Wb", null, "magnetic_flux", true, false, 1],
            ["yard", "yd", null, "length", false, true, 0.9144],
            ["year", "yr", null, "time", false, true, 31557600]
        ];

        // Binary prefixes
        // [Name, Prefix power of 2 value, Previx value, Abbreviation, Derived
        // from]
        var binary_prefixes = {
            Yi: ["yobi", 80, 1208925819614629174706176, "Yi", "yotta"],
            Zi: ["zebi", 70, 1180591620717411303424, "Zi", "zetta"],
            Ei: ["exbi", 60, 1152921504606846976, "Ei", "exa"],
            Pi: ["pebi", 50, 1125899906842624, "Pi", "peta"],
            Ti: ["tebi", 40, 1099511627776, "Ti", "tera"],
            Gi: ["gibi", 30, 1073741824, "Gi", "giga"],
            Mi: ["mebi", 20, 1048576, "Mi", "mega"],
            ki: ["kibi", 10, 1024, "ki", "kilo"]
        };

        // Unit prefixes
        // [Name, Multiplier, Abbreviation]
        var unit_prefixes = {
            Y: ["yotta", 1e+24, "Y"],
            Z: ["zetta", 1e+21, "Z"],
            E: ["exa", 1e+18, "E"],
            P: ["peta", 1e+15, "P"],
            T: ["tera", 1e+12, "T"],
            G: ["giga", 1e+09, "G"],
            M: ["mega", 1e+06, "M"],
            k: ["kilo", 1e+03, "k"],
            h: ["hecto", 1e+02, "h"],
            e: ["dekao", 1e+01, "e"],
            d: ["deci", 1e-01, "d"],
            c: ["centi", 1e-02, "c"],
            m: ["milli", 1e-03, "m"],
            u: ["micro", 1e-06, "u"],
            n: ["nano", 1e-09, "n"],
            p: ["pico", 1e-12, "p"],
            f: ["femto", 1e-15, "f"],
            a: ["atto", 1e-18, "a"],
            z: ["zepto", 1e-21, "z"],
            y: ["yocto", 1e-24, "y"]
        };

        // Initialize units and multipliers
        var from = null;
        var to = null;
        var base_from_unit = from_unit;
        var base_to_unit = to_unit;
        var from_multiplier = 1;
        var to_multiplier = 1;
        var alt;

        // Lookup from and to units
        for (var i = 0; i < units.length; i++) {
            alt = (units[i][2] === null) ? [] : units[i][2];
            if (units[i][1] === base_from_unit || alt.indexOf(base_from_unit) >= 0) {
              from = units[i];
            }
            if (units[i][1] === base_to_unit || alt.indexOf(base_to_unit) >= 0) {
              to = units[i];
            }
        }

        // Lookup from prefix
        if (from === null) {
            var from_binary_prefix = binary_prefixes[from_unit.substring(0, 2)];
            var from_unit_prefix = unit_prefixes[from_unit.substring(0, 1)];

            // Handle dekao unit prefix (only unit prefix with two characters)
            if (from_unit.substring(0, 2) === 'da') {
              from_unit_prefix = ["dekao", 1e+01, "da"];
            }

            // Handle binary prefixes first (so that 'Yi' is processed before
            // 'Y')
            if (from_binary_prefix) {
              from_multiplier = from_binary_prefix[2];
              base_from_unit = from_unit.substring(2);
            } else if (from_unit_prefix) {
              from_multiplier = from_unit_prefix[1];
              base_from_unit = from_unit.substring(from_unit_prefix[2].length);
            }

            // Lookup from unit
            for (var j = 0; j < units.length; j++) {
              alt = (units[j][2] === null) ? [] : units[j][2];
              if (units[j][1] === base_from_unit || alt.indexOf(base_from_unit) >= 0) {
                  from = units[j];
              }
            }
        }

        // Lookup to prefix
        if (to === null) {
            var to_binary_prefix = binary_prefixes[to_unit.substring(0, 2)];
            var to_unit_prefix = unit_prefixes[to_unit.substring(0, 1)];

            // Handle dekao unit prefix (only unit prefix with two characters)
            if (to_unit.substring(0, 2) === 'da') {
              to_unit_prefix = ["dekao", 1e+01, "da"];
            }

            // Handle binary prefixes first (so that 'Yi' is processed before
            // 'Y')
            if (to_binary_prefix) {
              to_multiplier = to_binary_prefix[2];
              base_to_unit = to_unit.substring(2);
            } else if (to_unit_prefix) {
              to_multiplier = to_unit_prefix[1];
              base_to_unit = to_unit.substring(to_unit_prefix[2].length);
            }

            // Lookup to unit
            for (var k = 0; k < units.length; k++) {
              alt = (units[k][2] === null) ? [] : units[k][2];
              if (units[k][1] === base_to_unit || alt.indexOf(base_to_unit) >= 0) {
                  to = units[k];
              }
            }
        }

        // Return error if a unit does not exist
        if (from === null || to === null) {
            return error.na;
        }

        // Return error if units represent different quantities
        if (from[3] !== to[3]) {
            return error.na;
        }

        // Return converted number
        return number * from[6] * from_multiplier / (to[6] * to_multiplier);
    };

    exports.DEC2BIN = function(number, places) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }

        // Return error if number is not decimal, is lower than -512, or is
        // greater than 511
        if (!/^-?[0-9]{1,3}$/.test(number) || number < -512 || number > 511) {
            return error.num;
        }

        // Ignore places and return a 10-character binary number if number is
        // negative
        if (number < 0) {
            return '1' + REPT('0', 9 - (512 + number).toString(2).length) + (512 + number).toString(2);
        }

        // Convert decimal number to binary
        var result = parseInt(number, 10).toString(2);

        // Return binary number using the minimum number of characters necessary
        // if places is undefined
        if (typeof places === 'undefined') {
            return result;
        } else {
            // Return error if places is nonnumeric
            if (isNaN(places)) {
              return error.value;
            }

            // Return error if places is negative
            if (places < 0) {
              return error.num;
            }

            // Truncate places in case it is not an integer
            places = Math.floor(places);

            // Pad return value with leading 0s (zeros) if necessary (using
            // Underscore.string)
            return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
        }
    };

    exports.DEC2HEX = function(number, places) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }

        // Return error if number is not decimal, is lower than -549755813888,
        // or is greater than 549755813887
        if (!/^-?[0-9]{1,12}$/.test(number) || number < -549755813888 || number > 549755813887) {
            return error.num;
        }

        // Ignore places and return a 10-character hexadecimal number if number
        // is negative
        if (number < 0) {
            return (1099511627776 + number).toString(16);
        }

        // Convert decimal number to hexadecimal
        var result = parseInt(number, 10).toString(16);

        // Return hexadecimal number using the minimum number of characters
        // necessary if places is undefined
        if (typeof places === 'undefined') {
            return result;
        } else {
            // Return error if places is nonnumeric
            if (isNaN(places)) {
              return error.value;
            }

            // Return error if places is negative
            if (places < 0) {
              return error.num;
            }

            // Truncate places in case it is not an integer
            places = Math.floor(places);

            // Pad return value with leading 0s (zeros) if necessary (using
            // Underscore.string)
            return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
        }
    };

    exports.DEC2OCT = function(number, places) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }

        // Return error if number is not decimal, is lower than -549755813888,
        // or is greater than 549755813887
        if (!/^-?[0-9]{1,9}$/.test(number) || number < -536870912 || number > 536870911) {
            return error.num;
        }

        // Ignore places and return a 10-character octal number if number is
        // negative
        if (number < 0) {
            return (1073741824 + number).toString(8);
        }

        // Convert decimal number to octal
        var result = parseInt(number, 10).toString(8);

        // Return octal number using the minimum number of characters necessary
        // if places is undefined
        if (typeof places === 'undefined') {
            return result;
        } else {
            // Return error if places is nonnumeric
            if (isNaN(places)) {
              return error.value;
            }

            // Return error if places is negative
            if (places < 0) {
              return error.num;
            }

            // Truncate places in case it is not an integer
            places = Math.floor(places);

            // Pad return value with leading 0s (zeros) if necessary (using
            // Underscore.string)
            return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
        }
    };

    exports.DELTA = function(number1, number2) {
        // Set number2 to zero if undefined
        number2 = (number2 === undefined) ? 0 : number2;
        number1 = utils.parseNumber(number1);
        number2 = utils.parseNumber(number2);
        if (utils.anyIsError(number1, number2)) {
            return error.value;
        }

        // Return delta
        return (number1 === number2) ? 1 : 0;
    };

    exports.ERF = function(lower_bound, upper_bound) {
    };

    exports.ERF.PRECISE = function() {
    };

    exports.ERFC = function(x) {
    };

    exports.ERFC.PRECISE = function() {
    };

    exports.GESTEP = function(number, step) {
        step = step || 0;
        number = utils.parseNumber(number);
        if (utils.anyIsError(step, number)) {
            return number;
        }

        // Return delta
        return (number >= step) ? 1 : 0;
    };

    exports.HEX2BIN = function(number, places) {
        // Return error if number is not hexadecimal or contains more than ten
        // characters (10 digits)
        if (!/^[0-9A-Fa-f]{1,10}$/.test(number)) {
            return error.num;
        }

        // Check if number is negative
        var negative = (number.length === 10 && number.substring(0, 1).toLowerCase() === 'f') ? true : false;

        // Convert hexadecimal number to decimal
        var decimal = (negative) ? parseInt(number, 16) - 1099511627776 : parseInt(number, 16);

        // Return error if number is lower than -512 or greater than 511
        if (decimal < -512 || decimal > 511) {
            return error.num;
        }

        // Ignore places and return a 10-character binary number if number is
        // negative
        if (negative) {
            return '1' + REPT('0', 9 - (512 + decimal).toString(2).length) + (512 + decimal).toString(2);
        }

        // Convert decimal number to binary
        var result = decimal.toString(2);

        // Return binary number using the minimum number of characters necessary
        // if places is undefined
        if (places === undefined) {
            return result;
        } else {
            // Return error if places is nonnumeric
            if (isNaN(places)) {
              return error.value;
            }

            // Return error if places is negative
            if (places < 0) {
              return error.num;
            }

            // Truncate places in case it is not an integer
            places = Math.floor(places);

            // Pad return value with leading 0s (zeros) if necessary (using
            // Underscore.string)
            return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
        }
    };

    exports.HEX2DEC = function(number) {
        // Return error if number is not hexadecimal or contains more than ten
        // characters (10 digits)
        if (!/^[0-9A-Fa-f]{1,10}$/.test(number)) {
            return error.num;
        }

        // Convert hexadecimal number to decimal
        var decimal = parseInt(number, 16);

        // Return decimal number
        return (decimal >= 549755813888) ? decimal - 1099511627776 : decimal;
    };

    exports.HEX2OCT = function(number, places) {
        // Return error if number is not hexadecimal or contains more than ten
        // characters (10 digits)
        if (!/^[0-9A-Fa-f]{1,10}$/.test(number)) {
            return error.num;
        }

        // Convert hexadecimal number to decimal
        var decimal = parseInt(number, 16);

        // Return error if number is positive and greater than 0x1fffffff
        // (536870911)
        if (decimal > 536870911 && decimal < 1098974756864) {
            return error.num;
        }

        // Ignore places and return a 10-character octal number if number is
        // negative
        if (decimal >= 1098974756864) {
            return (decimal - 1098437885952).toString(8);
        }

        // Convert decimal number to octal
        var result = decimal.toString(8);

        // Return octal number using the minimum number of characters necessary
        // if places is undefined
        if (places === undefined) {
            return result;
        } else {
            // Return error if places is nonnumeric
            if (isNaN(places)) {
              return error.value;
            }

            // Return error if places is negative
            if (places < 0) {
              return error.num;
            }

            // Truncate places in case it is not an integer
            places = Math.floor(places);

            // Pad return value with leading 0s (zeros) if necessary (using
            // Underscore.string)
            return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
        }
    };

    exports.IMABS = function(inumber) {
        // Lookup real and imaginary coefficients using exports.js
        // [http://formulajs.org]
        var x = exports.IMREAL(inumber);
        var y = exports.IMAGINARY(inumber);

        // Return error if either coefficient is not a number
        if (utils.anyIsError(x, y)) {
            return error.value;
        }

        // Return absolute value of complex number
        return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
    };

    exports.IMAGINARY = function(inumber) {
        if (inumber === undefined || inumber === true || inumber === false) {
            return error.value;
        }

        // Return 0 if inumber is equal to 0
        if (inumber === 0 || inumber === '0') {
            return 0;
        }

        // Handle special cases
        if (['i', 'j'].indexOf(inumber) >= 0) {
            return 1;
        }

        // Normalize imaginary coefficient
        inumber = inumber.replace('+i', '+1i').replace('-i', '-1i').replace('+j', '+1j').replace('-j', '-1j');

        // Lookup sign
        var plus = inumber.indexOf('+');
        var minus = inumber.indexOf('-');
        if (plus === 0) {
            plus = inumber.indexOf('+', 1);
        }

        if (minus === 0) {
            minus = inumber.indexOf('-', 1);
        }

        // Lookup imaginary unit
        var last = inumber.substring(inumber.length - 1, inumber.length);
        var unit = (last === 'i' || last === 'j');

        if (plus >= 0 || minus >= 0) {
            // Return error if imaginary unit is neither i nor j
            if (!unit) {
              return error.num;
            }

            // Return imaginary coefficient of complex number
            if (plus >= 0) {
              return (isNaN(inumber.substring(0, plus)) || isNaN(inumber.substring(plus + 1, inumber.length - 1))) ?
                  error.num :
                  Number(inumber.substring(plus + 1, inumber.length - 1));
            } else {
              return (isNaN(inumber.substring(0, minus)) || isNaN(inumber.substring(minus + 1, inumber.length - 1))) ?
                  error.num :
                  -Number(inumber.substring(minus + 1, inumber.length - 1));
            }
        } else {
            if (unit) {
              return (isNaN(inumber.substring(0, inumber.length - 1))) ? error.num : inumber.substring(0, inumber.length - 1);
            } else {
              return (isNaN(inumber)) ? error.num : 0;
            }
        }
    };

    exports.IMARGUMENT = function(inumber) {
        // Lookup real and imaginary coefficients using exports.js
        // [http://formulajs.org]
        var x = exports.IMREAL(inumber);
        var y = exports.IMAGINARY(inumber);

        // Return error if either coefficient is not a number
        if (utils.anyIsError(x, y)) {
            return error.value;
        }

        // Return error if inumber is equal to zero
        if (x === 0 && y === 0) {
            return error.div0;
        }

        // Return PI/2 if x is equal to zero and y is positive
        if (x === 0 && y > 0) {
            return Math.PI / 2;
        }

        // Return -PI/2 if x is equal to zero and y is negative
        if (x === 0 && y < 0) {
            return -Math.PI / 2;
        }

        // Return zero if x is negative and y is equal to zero
        if (y === 0 && x > 0) {
            return 0;
        }

        // Return zero if x is negative and y is equal to zero
        if (y === 0 && x < 0) {
            return -Math.PI;
        }

        // Return argument of complex number
        if (x > 0) {
            return Math.atan(y / x);
        } else if (x < 0 && y >= 0) {
            return Math.atan(y / x) + Math.PI;
        } else {
            return Math.atan(y / x) - Math.PI;
        }
    };

    exports.IMCONJUGATE = function(inumber) {
        // Lookup real and imaginary coefficients using exports.js
        // [http://formulajs.org]
        var x = exports.IMREAL(inumber);
        var y = exports.IMAGINARY(inumber);

        if (utils.anyIsError(x, y)) {
            return error.value;
        }

        // Lookup imaginary unit
        var unit = inumber.substring(inumber.length - 1);
        unit = (unit === 'i' || unit === 'j') ? unit : 'i';

        // Return conjugate of complex number
        return (y !== 0) ? exports.COMPLEX(x, -y, unit) : inumber;
    };

    exports.IMCOS = function(inumber) {
        // Lookup real and imaginary coefficients using exports.js
        // [http://formulajs.org]
        var x = exports.IMREAL(inumber);
        var y = exports.IMAGINARY(inumber);

        if (utils.anyIsError(x, y)) {
            return error.value;
        }

        // Lookup imaginary unit
        var unit = inumber.substring(inumber.length - 1);
        unit = (unit === 'i' || unit === 'j') ? unit : 'i';

        // Return cosine of complex number
        return exports.COMPLEX(Math.cos(x) * (Math.exp(y) + Math.exp(-y)) / 2, -Math.sin(x) * (Math.exp(y) - Math.exp(-y)) / 2, unit);
    };

    exports.IMCOSH = function(inumber) {
        // Lookup real and imaginary coefficients using exports.js
        // [http://formulajs.org]
        var x = exports.IMREAL(inumber);
        var y = exports.IMAGINARY(inumber);

        if (utils.anyIsError(x, y)) {
            return error.value;
        }

        // Lookup imaginary unit
        var unit = inumber.substring(inumber.length - 1);
        unit = (unit === 'i' || unit === 'j') ? unit : 'i';

        // Return hyperbolic cosine of complex number
        return exports.COMPLEX(Math.cos(y) * (Math.exp(x) + Math.exp(-x)) / 2, Math.sin(y) * (Math.exp(x) - Math.exp(-x)) / 2, unit);
    };

    exports.IMCOT = function(inumber) {
        // Lookup real and imaginary coefficients using Formula.js
        // [http://formulajs.org]
        var x = exports.IMREAL(inumber);
        var y = exports.IMAGINARY(inumber);

        if (utils.anyIsError(x, y)) {
            return error.value;
        }

        // Return cotangent of complex number
        return exports.IMDIV(exports.IMCOS(inumber), exports.IMSIN(inumber));
    };

    exports.IMDIV = function(inumber1, inumber2) {
        // Lookup real and imaginary coefficients using Formula.js
        // [http://formulajs.org]
        var a = exports.IMREAL(inumber1);
        var b = exports.IMAGINARY(inumber1);
        var c = exports.IMREAL(inumber2);
        var d = exports.IMAGINARY(inumber2);

        if (utils.anyIsError(a, b, c, d)) {
            return error.value;
        }

        // Lookup imaginary unit
        var unit1 = inumber1.substring(inumber1.length - 1);
        var unit2 = inumber2.substring(inumber2.length - 1);
        var unit = 'i';
        if (unit1 === 'j') {
            unit = 'j';
        } else if (unit2 === 'j') {
            unit = 'j';
        }

        // Return error if inumber2 is null
        if (c === 0 && d === 0) {
            return error.num;
        }

        // Return exponential of complex number
        var den = c * c + d * d;
        return exports.COMPLEX((a * c + b * d) / den, (b * c - a * d) / den, unit);
    };

    exports.IMEXP = function(inumber) {
        // Lookup real and imaginary coefficients using Formula.js
        // [http://formulajs.org]
        var x = exports.IMREAL(inumber);
        var y = exports.IMAGINARY(inumber);

        if (utils.anyIsError(x, y)) {
            return error.value;
        }

        // Lookup imaginary unit
        var unit = inumber.substring(inumber.length - 1);
        unit = (unit === 'i' || unit === 'j') ? unit : 'i';

        // Return exponential of complex number
        var e = Math.exp(x);
        return exports.COMPLEX(e * Math.cos(y), e * Math.sin(y), unit);
    };

    exports.IMLN = function(inumber) {
        // Lookup real and imaginary coefficients using Formula.js
        // [http://formulajs.org]
        var x = exports.IMREAL(inumber);
        var y = exports.IMAGINARY(inumber);

        if (utils.anyIsError(x, y)) {
            return error.value;
        }

        // Lookup imaginary unit
        var unit = inumber.substring(inumber.length - 1);
        unit = (unit === 'i' || unit === 'j') ? unit : 'i';

        // Return exponential of complex number
        return exports.COMPLEX(Math.log(Math.sqrt(x * x + y * y)), Math.atan(y / x), unit);
    };

    exports.IMLOG10 = function(inumber) {
        // Lookup real and imaginary coefficients using Formula.js
        // [http://formulajs.org]
        var x = exports.IMREAL(inumber);
        var y = exports.IMAGINARY(inumber);

        if (utils.anyIsError(x, y)) {
            return error.value;
        }

        // Lookup imaginary unit
        var unit = inumber.substring(inumber.length - 1);
        unit = (unit === 'i' || unit === 'j') ? unit : 'i';

        // Return exponential of complex number
        return exports.COMPLEX(Math.log(Math.sqrt(x * x + y * y)) / Math.log(10), Math.atan(y / x) / Math.log(10), unit);
    };

    exports.IMLOG2 = function(inumber) {
        // Lookup real and imaginary coefficients using Formula.js
        // [http://formulajs.org]
        var x = exports.IMREAL(inumber);
        var y = exports.IMAGINARY(inumber);

        if (utils.anyIsError(x, y)) {
            return error.value;
        }

        // Lookup imaginary unit
        var unit = inumber.substring(inumber.length - 1);
        unit = (unit === 'i' || unit === 'j') ? unit : 'i';

        // Return exponential of complex number
        return exports.COMPLEX(Math.log(Math.sqrt(x * x + y * y)) / Math.log(2), Math.atan(y / x) / Math.log(2), unit);
    };

    exports.IMPOWER = function(inumber, number) {
        number = utils.parseNumber(number);
        var x = exports.IMREAL(inumber);
        var y = exports.IMAGINARY(inumber);
        if (utils.anyIsError(number, x, y)) {
            return error.value;
        }

        // Lookup imaginary unit
        var unit = inumber.substring(inumber.length - 1);
        unit = (unit === 'i' || unit === 'j') ? unit : 'i';

        // Calculate power of modulus
        var p = Math.pow(exports.IMABS(inumber), number);

        // Calculate argument
        var t = exports.IMARGUMENT(inumber);

        // Return exponential of complex number
        return exports.COMPLEX(p * Math.cos(number * t), p * Math.sin(number * t), unit);
    };

    exports.IMPRODUCT = function() {
        // Initialize result
        var result = arguments[0];

        // Loop on all numbers
        for (var i = 1; i < arguments.length; i++) {
            // Lookup coefficients of two complex numbers
            var a = exports.IMREAL(result);
            var b = exports.IMAGINARY(result);
            var c = exports.IMREAL(arguments[i]);
            var d = exports.IMAGINARY(arguments[i]);

            if (utils.anyIsError(a, b, c, d)) {
              return error.value;
            }

            // Complute product of two complex numbers
            result = exports.COMPLEX(a * c - b * d, a * d + b * c);
        }

        // Return product of complex numbers
        return result;
    };

    exports.IMREAL = function(inumber) {
        if (inumber === undefined || inumber === true || inumber === false) {
            return error.value;
        }

        // Return 0 if inumber is equal to 0
        if (inumber === 0 || inumber === '0') {
            return 0;
        }

        // Handle special cases
        if (['i', '+i', '1i', '+1i', '-i', '-1i', 'j', '+j', '1j', '+1j', '-j', '-1j'].indexOf(inumber) >= 0) {
            return 0;
        }

        // Lookup sign
        var plus = inumber.indexOf('+');
        var minus = inumber.indexOf('-');
        if (plus === 0) {
            plus = inumber.indexOf('+', 1);
        }
        if (minus === 0) {
            minus = inumber.indexOf('-', 1);
        }

        // Lookup imaginary unit
        var last = inumber.substring(inumber.length - 1, inumber.length);
        var unit = (last === 'i' || last === 'j');

        if (plus >= 0 || minus >= 0) {
            // Return error if imaginary unit is neither i nor j
            if (!unit) {
              return error.num;
            }

            // Return real coefficient of complex number
            if (plus >= 0) {
              return (isNaN(inumber.substring(0, plus)) || isNaN(inumber.substring(plus + 1, inumber.length - 1))) ?
                  error.num :
                  Number(inumber.substring(0, plus));
            } else {
              return (isNaN(inumber.substring(0, minus)) || isNaN(inumber.substring(minus + 1, inumber.length - 1))) ?
                  error.num :
                  Number(inumber.substring(0, minus));
            }
        } else {
            if (unit) {
              return (isNaN(inumber.substring(0, inumber.length - 1))) ? error.num : 0;
            } else {
              return (isNaN(inumber)) ? error.num : inumber;
            }
        }
    };

    exports.IMSEC = function(inumber) {
        // Return error if inumber is a logical value
        if (inumber === true || inumber === false) {
            return error.value;
        }

        // Lookup real and imaginary coefficients using Formula.js
        // [http://formulajs.org]
        var x = exports.IMREAL(inumber);
        var y = exports.IMAGINARY(inumber);

        if (utils.anyIsError(x, y)) {
            return error.value;
        }

        // Return secant of complex number
        return exports.IMDIV('1', exports.IMCOS(inumber));
    };

    exports.IMSECH = function(inumber) {
        // Lookup real and imaginary coefficients using Formula.js
        // [http://formulajs.org]
        var x = exports.IMREAL(inumber);
        var y = exports.IMAGINARY(inumber);

        if (utils.anyIsError(x, y)) {
            return error.value;
        }

        // Return hyperbolic secant of complex number
        return exports.IMDIV('1', exports.IMCOSH(inumber));
    };

    exports.IMSIN = function(inumber) {
        // Lookup real and imaginary coefficients using Formula.js
        // [http://formulajs.org]
        var x = exports.IMREAL(inumber);
        var y = exports.IMAGINARY(inumber);

        if (utils.anyIsError(x, y)) {
            return error.value;
        }

        // Lookup imaginary unit
        var unit = inumber.substring(inumber.length - 1);
        unit = (unit === 'i' || unit === 'j') ? unit : 'i';

        // Return sine of complex number
        return exports.COMPLEX(Math.sin(x) * (Math.exp(y) + Math.exp(-y)) / 2, Math.cos(x) * (Math.exp(y) - Math.exp(-y)) / 2, unit);
    };

    exports.IMSINH = function(inumber) {
        // Lookup real and imaginary coefficients using Formula.js
        // [http://formulajs.org]
        var x = exports.IMREAL(inumber);
        var y = exports.IMAGINARY(inumber);

        if (utils.anyIsError(x, y)) {
            return error.value;
        }

        // Lookup imaginary unit
        var unit = inumber.substring(inumber.length - 1);
        unit = (unit === 'i' || unit === 'j') ? unit : 'i';

        // Return hyperbolic sine of complex number
        return exports.COMPLEX(Math.cos(y) * (Math.exp(x) - Math.exp(-x)) / 2, Math.sin(y) * (Math.exp(x) + Math.exp(-x)) / 2, unit);
    };

    exports.IMSQRT = function(inumber) {
        // Lookup real and imaginary coefficients using Formula.js
        // [http://formulajs.org]
        var x = exports.IMREAL(inumber);
        var y = exports.IMAGINARY(inumber);

        if (utils.anyIsError(x, y)) {
            return error.value;
        }

        // Lookup imaginary unit
        var unit = inumber.substring(inumber.length - 1);
        unit = (unit === 'i' || unit === 'j') ? unit : 'i';

        // Calculate power of modulus
        var s = Math.sqrt(exports.IMABS(inumber));

        // Calculate argument
        var t = exports.IMARGUMENT(inumber);

        // Return exponential of complex number
        return exports.COMPLEX(s * Math.cos(t / 2), s * Math.sin(t / 2), unit);
    };

    exports.IMCSC = function (inumber) {
        // Return error if inumber is a logical value
        if (inumber === true || inumber === false) {
            return error.value;
        }

        // Lookup real and imaginary coefficients using Formula.js
        // [http://formulajs.org]
        var x = exports.IMREAL(inumber);
        var y = exports.IMAGINARY(inumber);

        // Return error if either coefficient is not a number
        if (utils.anyIsError(x, y)) {
            return error.num;
        }

        // Return cosecant of complex number
        return exports.IMDIV('1', exports.IMSIN(inumber));
    };

    exports.IMCSCH = function (inumber) {
        // Return error if inumber is a logical value
        if (inumber === true || inumber === false) {
            return error.value;
        }

        // Lookup real and imaginary coefficients using Formula.js
        // [http://formulajs.org]
        var x = exports.IMREAL(inumber);
        var y = exports.IMAGINARY(inumber);

        // Return error if either coefficient is not a number
        if (utils.anyIsError(x, y)) {
            return error.num;
        }

        // Return hyperbolic cosecant of complex number
        return exports.IMDIV('1', exports.IMSINH(inumber));
    };

    exports.IMSUB = function(inumber1, inumber2) {
        // Lookup real and imaginary coefficients using Formula.js
        // [http://formulajs.org]
        var a = this.IMREAL(inumber1);
        var b = this.IMAGINARY(inumber1);
        var c = this.IMREAL(inumber2);
        var d = this.IMAGINARY(inumber2);

        if (utils.anyIsError(a, b, c, d)) {
            return error.value;
        }

        // Lookup imaginary unit
        var unit1 = inumber1.substring(inumber1.length - 1);
        var unit2 = inumber2.substring(inumber2.length - 1);
        var unit = 'i';
        if (unit1 === 'j') {
            unit = 'j';
        } else if (unit2 === 'j') {
            unit = 'j';
        }

        // Return _ of two complex numbers
        return this.COMPLEX(a - c, b - d, unit);
    };

    exports.IMSUM = function() {
        var args = utils.flatten(arguments);

        // Initialize result
        var result = args[0];

        // Loop on all numbers
        for (var i = 1; i < args.length; i++) {
            // Lookup coefficients of two complex numbers
            var a = this.IMREAL(result);
            var b = this.IMAGINARY(result);
            var c = this.IMREAL(args[i]);
            var d = this.IMAGINARY(args[i]);

            if (utils.anyIsError(a, b, c, d)) {
              return error.value;
            }

            // Complute product of two complex numbers
            result = this.COMPLEX(a + c, b + d);
        }

        // Return sum of complex numbers
        return result;
    };

    exports.IMTAN = function(inumber) {
        // Return error if inumber is a logical value
        if (inumber === true || inumber === false) {
            return error.value;
        }

        // Lookup real and imaginary coefficients using Formula.js
        // [http://formulajs.org]
        var x = exports.IMREAL(inumber);
        var y = exports.IMAGINARY(inumber);

        if (utils.anyIsError(x, y)) {
            return error.value;
        }

        // Return tangent of complex number
        return this.IMDIV(this.IMSIN(inumber), this.IMCOS(inumber));
    };

    exports.OCT2BIN = function(number, places) {
        // Return error if number is not hexadecimal or contains more than ten
        // characters (10 digits)
        if (!/^[0-7]{1,10}$/.test(number)) {
            return error.num;
        }

        // Check if number is negative
        var negative = (number.length === 10 && number.substring(0, 1) === '7') ? true : false;

        // Convert octal number to decimal
        var decimal = (negative) ? parseInt(number, 8) - 1073741824 : parseInt(number, 8);

        // Return error if number is lower than -512 or greater than 511
        if (decimal < -512 || decimal > 511) {
            return error.num;
        }

        // Ignore places and return a 10-character binary number if number is
        // negative
        if (negative) {
            return '1' + REPT('0', 9 - (512 + decimal).toString(2).length) + (512 + decimal).toString(2);
        }

        // Convert decimal number to binary
        var result = decimal.toString(2);

        // Return binary number using the minimum number of characters necessary
        // if places is undefined
        if (typeof places === 'undefined') {
            return result;
        } else {
            // Return error if places is nonnumeric
            if (isNaN(places)) {
              return error.value;
            }

            // Return error if places is negative
            if (places < 0) {
              return error.num;
            }

            // Truncate places in case it is not an integer
            places = Math.floor(places);

            // Pad return value with leading 0s (zeros) if necessary (using
            // Underscore.string)
            return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
        }
    };

    exports.OCT2DEC = function(number) {
        // Return error if number is not octal or contains more than ten
        // characters (10 digits)
        if (!/^[0-7]{1,10}$/.test(number)) {
            return error.num;
        }

        // Convert octal number to decimal
        var decimal = parseInt(number, 8);

        // Return decimal number
        return (decimal >= 536870912) ? decimal - 1073741824 : decimal;
    };

    exports.OCT2HEX = function(number, places) {
        // Return error if number is not octal or contains more than ten
        // characters (10 digits)
        if (!/^[0-7]{1,10}$/.test(number)) {
            return error.num;
        }

        // Convert octal number to decimal
        var decimal = parseInt(number, 8);

        // Ignore places and return a 10-character octal number if number is
        // negative
        if (decimal >= 536870912) {
            return 'ff' + (decimal + 3221225472).toString(16);
        }

        // Convert decimal number to hexadecimal
        var result = decimal.toString(16);

        // Return hexadecimal number using the minimum number of characters
        // necessary if places is undefined
        if (places === undefined) {
            return result;
        } else {
            // Return error if places is nonnumeric
            if (isNaN(places)) {
              return error.value;
            }

            // Return error if places is negative
            if (places < 0) {
              return error.num;
            }

            // Truncate places in case it is not an integer
            places = Math.floor(places);

            // Pad return value with leading 0s (zeros) if necessary (using
            // Underscore.string)
            return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
        }
    };

    return exports;
})();

jexcel.methods.financial = (function() {
    var exports = {};

    function validDate(d) {
        return d && d.getTime && !isNaN(d.getTime());
    }

    function ensureDate(d) {
        return (d instanceof Date)?d:new Date(d);
    }

    exports.ACCRINT = function(issue, first, settlement, rate, par, frequency, basis) {
        // Return error if either date is invalid
        issue        = ensureDate(issue);
        first        = ensureDate(first);
        settlement = ensureDate(settlement);
        if (!validDate(issue) || !validDate(first) || !validDate(settlement)) {
            return '#VALUE!';
        }

        // Return error if either rate or par are lower than or equal to zero
        if (rate <= 0 || par <= 0) {
            return '#NUM!';
        }

        // Return error if frequency is neither 1, 2, or 4
        if ([1, 2, 4].indexOf(frequency) === -1) {
            return '#NUM!';
        }

        // Return error if basis is neither 0, 1, 2, 3, or 4
        if ([0, 1, 2, 3, 4].indexOf(basis) === -1) {
            return '#NUM!';
        }

        // Return error if settlement is before or equal to issue
        if (settlement <= issue) {
            return '#NUM!';
        }

        // Set default values
        par   = par   || 0;
        basis = basis || 0;

        // Compute accrued interest
        return par * rate * YEARFRAC(issue, settlement, basis);
    };

    exports.ACCRINTM = null;

    exports.AMORDEGRC = null;

    exports.AMORLINC = null;

    exports.COUPDAYBS = null;

    exports.COUPDAYS = null;

    exports.COUPDAYSNC = null;

    exports.COUPNCD = null;

    exports.COUPNUM = null;

    exports.COUPPCD = null;

    exports.CUMIPMT = function(rate, periods, value, start, end, type) {
        // Credits: algorithm inspired by Apache OpenOffice
        // Credits: Hannes Stiebitzhofer for the translations of function and
            // variable names
        // Requires exports.FV() and exports.PMT() from exports.js
            // [http://stoic.com/exports/]

        rate = utils.parseNumber(rate);
        periods = utils.parseNumber(periods);
        value = utils.parseNumber(value);
        if (utils.anyIsError(rate, periods, value)) {
            return error.value;
        }

        // Return error if either rate, periods, or value are lower than or
            // equal to zero
        if (rate <= 0 || periods <= 0 || value <= 0) {
            return error.num;
        }

        // Return error if start < 1, end < 1, or start > end
        if (start < 1 || end < 1 || start > end) {
            return error.num;
        }

        // Return error if type is neither 0 nor 1
        if (type !== 0 && type !== 1) {
            return error.num;
        }

        // Compute cumulative interest
        var payment = exports.PMT(rate, periods, value, 0, type);
        var interest = 0;

        if (start === 1) {
            if (type === 0) {
                interest = -value;
                start++;
            }
        }

        for (var i = start; i <= end; i++) {
            if (type === 1) {
                interest += exports.FV(rate, i - 2, payment, value, 1) - payment;
            } else {
                interest += exports.FV(rate, i - 1, payment, value, 0);
            }
        }
        interest *= rate;

        // Return cumulative interest
        return interest;
    };

    exports.CUMPRINC = function(rate, periods, value, start, end, type) {
        // Credits: algorithm inspired by Apache OpenOffice
        // Credits: Hannes Stiebitzhofer for the translations of function and
            // variable names

        rate = utils.parseNumber(rate);
        periods = utils.parseNumber(periods);
        value = utils.parseNumber(value);
        if (utils.anyIsError(rate, periods, value)) {
            return error.value;
        }

        // Return error if either rate, periods, or value are lower than or
            // equal to zero
        if (rate <= 0 || periods <= 0 || value <= 0) {
            return error.num;
        }

        // Return error if start < 1, end < 1, or start > end
        if (start < 1 || end < 1 || start > end) {
            return error.num;
        }

        // Return error if type is neither 0 nor 1
        if (type !== 0 && type !== 1) {
            return error.num;
        }

        // Compute cumulative principal
        var payment = exports.PMT(rate, periods, value, 0, type);
        var principal = 0;
        if (start === 1) {
            if (type === 0) {
                principal = payment + value * rate;
            } else {
                principal = payment;
            }
            start++;
        }
        for (var i = start; i <= end; i++) {
            if (type > 0) {
                principal += payment - (exports.FV(rate, i - 2, payment, value, 1) - payment) * rate;
            } else {
                principal += payment - exports.FV(rate, i - 1, payment, value, 0) * rate;
            }
        }

        // Return cumulative principal
        return principal;
    };

    exports.DB = function(cost, salvage, life, period, month) {
        // Initialize month
        month = (month === undefined) ? 12 : month;

        cost = utils.parseNumber(cost);
        salvage = utils.parseNumber(salvage);
        life = utils.parseNumber(life);
        period = utils.parseNumber(period);
        month = utils.parseNumber(month);
        if (utils.anyIsError(cost, salvage, life, period, month)) {
            return error.value;
        }

        // Return error if any of the parameters is negative
        if (cost < 0 || salvage < 0 || life < 0 || period < 0) {
            return error.num;
        }

        // Return error if month is not an integer between 1 and 12
        if ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].indexOf(month) === -1) {
            return error.num;
        }

        // Return error if period is greater than life
        if (period > life) {
            return error.num;
        }

        // Return 0 (zero) if salvage is greater than or equal to cost
        if (salvage >= cost) {
            return 0;
        }

        // Rate is rounded to three decimals places
        var rate = (1 - Math.pow(salvage / cost, 1 / life)).toFixed(3);

        // Compute initial depreciation
        var initial = cost * rate * month / 12;

        // Compute total depreciation
        var total = initial;
        var current = 0;
        var ceiling = (period === life) ? life - 1 : period;
        for (var i = 2; i <= ceiling; i++) {
            current = (cost - total) * rate;
            total += current;
        }

        // Depreciation for the first and last periods are special cases
        if (period === 1) {
            // First period
            return initial;
        } else if (period === life) {
            // Last period
            return (cost - total) * rate;
        } else {
            return current;
        }
    };

    exports.DDB = function(cost, salvage, life, period, factor) {
        // Initialize factor
        factor = (factor === undefined) ? 2 : factor;

        cost = utils.parseNumber(cost);
        salvage = utils.parseNumber(salvage);
        life = utils.parseNumber(life);
        period = utils.parseNumber(period);
        factor = utils.parseNumber(factor);
        if (utils.anyIsError(cost, salvage, life, period, factor)) {
            return error.value;
        }

        // Return error if any of the parameters is negative or if factor is
            // null
        if (cost < 0 || salvage < 0 || life < 0 || period < 0 || factor <= 0) {
            return error.num;
        }

        // Return error if period is greater than life
        if (period > life) {
            return error.num;
        }

        // Return 0 (zero) if salvage is greater than or equal to cost
        if (salvage >= cost) {
            return 0;
        }

        // Compute depreciation
        var total = 0;
        var current = 0;
        for (var i = 1; i <= period; i++) {
            current = Math.min((cost - total) * (factor / life), (cost - salvage - total));
            total += current;
        }

        // Return depreciation
        return current;
    };

    exports.DISC = null;

    exports.DOLLARDE = function(dollar, fraction) {
        // Credits: algorithm inspired by Apache OpenOffice

        dollar = utils.parseNumber(dollar);
        fraction = utils.parseNumber(fraction);
        if (utils.anyIsError(dollar, fraction)) {
            return error.value;
        }

        // Return error if fraction is negative
        if (fraction < 0) {
            return error.num;
        }

        // Return error if fraction is greater than or equal to 0 and less than
            // 1
        if (fraction >= 0 && fraction < 1) {
            return error.div0;
        }

        // Truncate fraction if it is not an integer
        fraction = parseInt(fraction, 10);

        // Compute integer part
        var result = parseInt(dollar, 10);

        // Add decimal part
        result += (dollar % 1) * Math.pow(10, Math.ceil(Math.log(fraction) / Math.LN10)) / fraction;

        // Round result
        var power = Math.pow(10, Math.ceil(Math.log(fraction) / Math.LN2) + 1);
        result = Math.round(result * power) / power;

        // Return converted dollar price
        return result;
    };

    exports.DOLLARFR = function(dollar, fraction) {
        // Credits: algorithm inspired by Apache OpenOffice

        dollar = utils.parseNumber(dollar);
        fraction = utils.parseNumber(fraction);
        if (utils.anyIsError(dollar, fraction)) {
            return error.value;
        }

        // Return error if fraction is negative
        if (fraction < 0) {
            return error.num;
        }

        // Return error if fraction is greater than or equal to 0 and less than
            // 1
        if (fraction >= 0 && fraction < 1) {
            return error.div0;
        }

        // Truncate fraction if it is not an integer
        fraction = parseInt(fraction, 10);

        // Compute integer part
        var result = parseInt(dollar, 10);

        // Add decimal part
        result += (dollar % 1) * Math.pow(10, -Math.ceil(Math.log(fraction) / Math.LN10)) * fraction;

        // Return converted dollar price
        return result;
    };

    exports.DURATION = null;

    exports.EFFECT = function(rate, periods) {
        rate = utils.parseNumber(rate);
        periods = utils.parseNumber(periods);
        if (utils.anyIsError(rate, periods)) {
            return error.value;
        }

        // Return error if rate <=0 or periods < 1
        if (rate <= 0 || periods < 1) {
            return error.num;
        }

        // Truncate periods if it is not an integer
        periods = parseInt(periods, 10);

        // Return effective annual interest rate
        return Math.pow(1 + rate / periods, periods) - 1;
    };

    exports.FV = function(rate, periods, payment, value, type) {
        // Credits: algorithm inspired by Apache OpenOffice

        value = value || 0;
        type = type || 0;

        rate = utils.parseNumber(rate);
        periods = utils.parseNumber(periods);
        payment = utils.parseNumber(payment);
        value = utils.parseNumber(value);
        type = utils.parseNumber(type);
        if (utils.anyIsError(rate, periods, payment, value, type)) {
            return error.value;
        }

        // Return future value
        var result;
        if (rate === 0) {
            result = value + payment * periods;
        } else {
            var term = Math.pow(1 + rate, periods);
            if (type === 1) {
                result = value * term + payment * (1 + rate) * (term - 1) / rate;
            } else {
                result = value * term + payment * (term - 1) / rate;
            }
        }
        return -result;
    };

    exports.FVSCHEDULE = function(principal, schedule) {
        principal = utils.parseNumber(principal);
        schedule = utils.parseNumberArray(utils.flatten(schedule));
        if (utils.anyIsError(principal, schedule)) {
            return error.value;
        }

        var n = schedule.length;
        var future = principal;

        // Apply all interests in schedule
        for (var i = 0; i < n; i++) {
            // Apply scheduled interest
            future *= 1 + schedule[i];
        }

        // Return future value
        return future;
    };

    exports.INTRATE = null;

    exports.IPMT = function(rate, period, periods, present, future, type) {
        // Credits: algorithm inspired by Apache OpenOffice

        future = future || 0;
        type = type || 0;

        rate = utils.parseNumber(rate);
        period = utils.parseNumber(period);
        periods = utils.parseNumber(periods);
        present = utils.parseNumber(present);
        future = utils.parseNumber(future);
        type = utils.parseNumber(type);
        if (utils.anyIsError(rate, period, periods, present, future, type)) {
            return error.value;
        }

        // Compute payment
        var payment = exports.PMT(rate, periods, present, future, type);

        // Compute interest
        var interest;
        if (period === 1) {
            if (type === 1) {
                interest = 0;
            } else {
                interest = -present;
            }
        } else {
            if (type === 1) {
                interest = exports.FV(rate, period - 2, payment, present, 1) - payment;
            } else {
                interest = exports.FV(rate, period - 1, payment, present, 0);
            }
        }

        // Return interest
        return interest * rate;
    };

    exports.IRR = function(values, guess) {
        // Credits: algorithm inspired by Apache OpenOffice

        guess = guess || 0;

        values = utils.parseNumberArray(utils.flatten(values));
        guess = utils.parseNumber(guess);
        if (utils.anyIsError(values, guess)) {
            return error.value;
        }

        // Calculates the resulting amount
        var irrResult = function(values, dates, rate) {
            var r = rate + 1;
            var result = values[0];
            for (var i = 1; i < values.length; i++) {
                result += values[i] / Math.pow(r, (dates[i] - dates[0]) / 365);
            }
            return result;
        };

        // Calculates the first derivation
        var irrResultDeriv = function(values, dates, rate) {
            var r = rate + 1;
            var result = 0;
            for (var i = 1; i < values.length; i++) {
                var frac = (dates[i] - dates[0]) / 365;
                result -= frac * values[i] / Math.pow(r, frac + 1);
            }
            return result;
        };

        // Initialize dates and check that values contains at least one positive
            // value and one negative value
        var dates = [];
        var positive = false;
        var negative = false;
        for (var i = 0; i < values.length; i++) {
            dates[i] = (i === 0) ? 0 : dates[i - 1] + 365;
            if (values[i] > 0) {
                positive = true;
            }
            if (values[i] < 0) {
                negative = true;
            }
        }

        // Return error if values does not contain at least one positive value
            // and one negative value
        if (!positive || !negative) {
            return error.num;
        }

        // Initialize guess and resultRate
        guess = (guess === undefined) ? 0.1 : guess;
        var resultRate = guess;

        // Set maximum epsilon for end of iteration
        var epsMax = 1e-10;

        // Implement Newton's method
        var newRate, epsRate, resultValue;
        var contLoop = true;
        do {
            resultValue = irrResult(values, dates, resultRate);
            newRate = resultRate - resultValue / irrResultDeriv(values, dates, resultRate);
            epsRate = Math.abs(newRate - resultRate);
            resultRate = newRate;
            contLoop = (epsRate > epsMax) && (Math.abs(resultValue) > epsMax);
        } while (contLoop);

        // Return internal rate of return
        return resultRate;
    };

    exports.ISPMT = function(rate, period, periods, value) {
        rate = utils.parseNumber(rate);
        period = utils.parseNumber(period);
        periods = utils.parseNumber(periods);
        value = utils.parseNumber(value);
        if (utils.anyIsError(rate, period, periods, value)) {
            return error.value;
        }

        // Return interest
        return value * rate * (period / periods - 1);
    };

    exports.MDURATION = null;

    exports.MIRR = function(values, finance_rate, reinvest_rate) {
        values = utils.parseNumberArray(utils.flatten(values));
        finance_rate = utils.parseNumber(finance_rate);
        reinvest_rate = utils.parseNumber(reinvest_rate);
        if (utils.anyIsError(values, finance_rate, reinvest_rate)) {
            return error.value;
        }

        // Initialize number of values
        var n = values.length;

        // Lookup payments (negative values) and incomes (positive values)
        var payments = [];
        var incomes = [];
        for (var i = 0; i < n; i++) {
            if (values[i] < 0) {
                payments.push(values[i]);
            } else {
                incomes.push(values[i]);
            }
        }

        // Return modified internal rate of return
        var num = -exports.NPV(reinvest_rate, incomes) * Math.pow(1 + reinvest_rate, n - 1);
        var den = exports.NPV(finance_rate, payments) * (1 + finance_rate);
        return Math.pow(num / den, 1 / (n - 1)) - 1;
    };

    exports.NOMINAL = function(rate, periods) {
        rate = utils.parseNumber(rate);
        periods = utils.parseNumber(periods);
        if (utils.anyIsError(rate, periods)) {
            return error.value;
        }

        // Return error if rate <=0 or periods < 1
        if (rate <= 0 || periods < 1) {
            return error.num;
        }

        // Truncate periods if it is not an integer
        periods = parseInt(periods, 10);

        // Return nominal annual interest rate
        return (Math.pow(rate + 1, 1 / periods) - 1) * periods;
    };

    exports.NPER = function(rate, payment, present, future, type) {
        type = (type === undefined) ? 0 : type;
        future = (future === undefined) ? 0 : future;

        rate = utils.parseNumber(rate);
        payment = utils.parseNumber(payment);
        present = utils.parseNumber(present);
        future = utils.parseNumber(future);
        type = utils.parseNumber(type);
        if (utils.anyIsError(rate, payment, present, future, type)) {
            return error.value;
        }

        // Return number of periods
        var num = payment * (1 + rate * type) - future * rate;
        var den = (present * rate + payment * (1 + rate * type));
        return Math.log(num / den) / Math.log(1 + rate);
    };

    exports.NPV = function() {
        var args = utils.parseNumberArray(utils.flatten(arguments));
        if (args instanceof Error) {
            return args;
        }

        // Lookup rate
        var rate = args[0];

        // Initialize net present value
        var value = 0;

        // Loop on all values
        for (var j = 1; j < args.length; j++) {
            value += args[j] / Math.pow(1 + rate, j);
        }

        // Return net present value
        return value;
    };

    exports.ODDFPRICE = null;

    exports.ODDFYIELD = null;

    exports.ODDLPRICE = null;

    exports.ODDLYIELD = null;

    exports.PDURATION = function(rate, present, future) {
        rate = utils.parseNumber(rate);
        present = utils.parseNumber(present);
        future = utils.parseNumber(future);
        if (utils.anyIsError(rate, present, future)) {
            return error.value;
        }

        // Return error if rate <=0
        if (rate <= 0) {
            return error.num;
        }

        // Return number of periods
        return (Math.log(future) - Math.log(present)) / Math.log(1 + rate);
    };

    exports.PMT = function(rate, periods, present, future, type) {
        // Credits: algorithm inspired by Apache OpenOffice

        future = future || 0;
        type = type || 0;

        rate = utils.parseNumber(rate);
        periods = utils.parseNumber(periods);
        present = utils.parseNumber(present);
        future = utils.parseNumber(future);
        type = utils.parseNumber(type);
        if (utils.anyIsError(rate, periods, present, future, type)) {
            return error.value;
        }

        // Return payment
        var result;
        if (rate === 0) {
            result = (present + future) / periods;
        } else {
            var term = Math.pow(1 + rate, periods);
            if (type === 1) {
                result = (future * rate / (term - 1) + present * rate / (1 - 1 / term)) / (1 + rate);
            } else {
                result = future * rate / (term - 1) + present * rate / (1 - 1 / term);
            }
        }
        return -result;
    };

    exports.PPMT = function(rate, period, periods, present, future, type) {
        future = future || 0;
        type = type || 0;

        rate = utils.parseNumber(rate);
        periods = utils.parseNumber(periods);
        present = utils.parseNumber(present);
        future = utils.parseNumber(future);
        type = utils.parseNumber(type);
        if (utils.anyIsError(rate, periods, present, future, type)) {
            return error.value;
        }

        return exports.PMT(rate, periods, present, future, type) - exports.IPMT(rate, period, periods, present, future, type);
    };

    exports.PRICE = null;

    exports.PRICEDISC = null;

    exports.PRICEMAT = null;

    exports.PV = function(rate, periods, payment, future, type) {
        future = future || 0;
        type = type || 0;

        rate = utils.parseNumber(rate);
        periods = utils.parseNumber(periods);
        payment = utils.parseNumber(payment);
        future = utils.parseNumber(future);
        type = utils.parseNumber(type);
        if (utils.anyIsError(rate, periods, payment, future, type)) {
            return error.value;
        }

        // Return present value
        if (rate === 0) {
            return -payment * periods - future;
        } else {
            return (((1 - Math.pow(1 + rate, periods)) / rate) * payment * (1 + rate * type) - future) / Math.pow(1 + rate, periods);
        }
    };

    exports.RATE = function(periods, payment, present, future, type, guess) {
        // Credits: rabugento

        guess = (guess === undefined) ? 0.01 : guess;
        future = (future === undefined) ? 0 : future;
        type = (type === undefined) ? 0 : type;

        periods = utils.parseNumber(periods);
        payment = utils.parseNumber(payment);
        present = utils.parseNumber(present);
        future = utils.parseNumber(future);
        type = utils.parseNumber(type);
        guess = utils.parseNumber(guess);
        if (utils.anyIsError(periods, payment, present, future, type, guess)) {
            return error.value;
        }

        // Set maximum epsilon for end of iteration
        var epsMax = 1e-6;

        // Set maximum number of iterations
        var iterMax = 100;
        var iter = 0;
        var close = false;
        var rate = guess;

        while (iter < iterMax && !close) {
            var t1 = Math.pow(rate + 1, periods);
            var t2 = Math.pow(rate + 1, periods - 1);

            var f1 = future + t1 * present + payment * (t1 - 1) * (rate * type + 1) / rate;
            var f2 = periods * t2 * present - payment * (t1 - 1) *(rate * type + 1) / Math.pow(rate,2);
            var f3 = periods * payment * t2 * (rate * type + 1) / rate + payment * (t1 - 1) * type / rate;

            var newRate = rate - f1 / (f2 + f3);

            if (Math.abs(newRate - rate) < epsMax) close = true;
            iter++
            rate = newRate;
        }

        if (!close) return Number.NaN + rate;
        return rate;
    };

    // TODO
    exports.RECEIVED = null;

    exports.RRI = function(periods, present, future) {
        periods = utils.parseNumber(periods);
        present = utils.parseNumber(present);
        future = utils.parseNumber(future);
        if (utils.anyIsError(periods, present, future)) {
            return error.value;
        }

        // Return error if periods or present is equal to 0 (zero)
        if (periods === 0 || present === 0) {
            return error.num;
        }

        // Return equivalent interest rate
        return Math.pow(future / present, 1 / periods) - 1;
    };

    exports.SLN = function(cost, salvage, life) {
        cost = utils.parseNumber(cost);
        salvage = utils.parseNumber(salvage);
        life = utils.parseNumber(life);
        if (utils.anyIsError(cost, salvage, life)) {
            return error.value;
        }

        // Return error if life equal to 0 (zero)
        if (life === 0) {
            return error.num;
        }

        // Return straight-line depreciation
        return (cost - salvage) / life;
    };

    exports.SYD = function(cost, salvage, life, period) {
        // Return error if any of the parameters is not a number
        cost = utils.parseNumber(cost);
        salvage = utils.parseNumber(salvage);
        life = utils.parseNumber(life);
        period = utils.parseNumber(period);
        if (utils.anyIsError(cost, salvage, life, period)) {
            return error.value;
        }

        // Return error if life equal to 0 (zero)
        if (life === 0) {
            return error.num;
        }

        // Return error if period is lower than 1 or greater than life
        if (period < 1 || period > life) {
            return error.num;
        }

        // Truncate period if it is not an integer
        period = parseInt(period, 10);

        // Return straight-line depreciation
        return ((cost - salvage) * (life - period + 1) * 2) / (life * (life + 1));
    };

    exports.TBILLEQ = function(settlement, maturity, discount) {
        settlement = utils.parseDate(settlement);
        maturity = utils.parseDate(maturity);
        discount = utils.parseNumber(discount);
        if (utils.anyIsError(settlement, maturity, discount)) {
            return error.value;
        }

        // Return error if discount is lower than or equal to zero
        if (discount <= 0) {
            return error.num;
        }

        // Return error if settlement is greater than maturity
        if (settlement > maturity) {
            return error.num;
        }

        // Return error if maturity is more than one year after settlement
        if (maturity - settlement > 365 * 24 * 60 * 60 * 1000) {
            return error.num;
        }

        // Return bond-equivalent yield
        return (365 * discount) / (360 - discount * DAYS360(settlement, maturity, false));
    };

    exports.TBILLPRICE = function(settlement, maturity, discount) {
        settlement = utils.parseDate(settlement);
        maturity = utils.parseDate(maturity);
        discount = utils.parseNumber(discount);
        if (utils.anyIsError(settlement, maturity, discount)) {
            return error.value;
        }

        // Return error if discount is lower than or equal to zero
        if (discount <= 0) {
            return error.num;
        }

        // Return error if settlement is greater than maturity
        if (settlement > maturity) {
            return error.num;
        }

        // Return error if maturity is more than one year after settlement
        if (maturity - settlement > 365 * 24 * 60 * 60 * 1000) {
            return error.num;
        }

        // Return bond-equivalent yield
        return 100 * (1 - discount * DAYS360(settlement, maturity, false) / 360);
    };

    exports.TBILLYIELD = function(settlement, maturity, price) {
        settlement = utils.parseDate(settlement);
        maturity = utils.parseDate(maturity);
        price = utils.parseNumber(price);
        if (utils.anyIsError(settlement, maturity, price)) {
            return error.value;
        }

        // Return error if price is lower than or equal to zero
        if (price <= 0) {
            return error.num;
        }

        // Return error if settlement is greater than maturity
        if (settlement > maturity) {
            return error.num;
        }

        // Return error if maturity is more than one year after settlement
        if (maturity - settlement > 365 * 24 * 60 * 60 * 1000) {
            return error.num;
        }

        // Return bond-equivalent yield
        return (100 - price) * 360 / (price * DAYS360(settlement, maturity, false));
    };

    exports.VDB = null;

    exports.XIRR = function(values, dates, guess) {
        // Credits: algorithm inspired by Apache OpenOffice

        values = utils.parseNumberArray(utils.flatten(values));
        dates = utils.parseDateArray(utils.flatten(dates));
        guess = utils.parseNumber(guess);
        if (utils.anyIsError(values, dates, guess)) {
            return error.value;
        }

        // Calculates the resulting amount
        var irrResult = function(values, dates, rate) {
            var r = rate + 1;
            var result = values[0];
            for (var i = 1; i < values.length; i++) {
                result += values[i] / Math.pow(r, DAYS(dates[i], dates[0]) / 365);
            }
            return result;
        };

        // Calculates the first derivation
        var irrResultDeriv = function(values, dates, rate) {
            var r = rate + 1;
            var result = 0;
            for (var i = 1; i < values.length; i++) {
                var frac = DAYS(dates[i], dates[0]) / 365;
                result -= frac * values[i] / Math.pow(r, frac + 1);
            }
            return result;
        };

        // Check that values contains at least one positive value and one
            // negative value
        var positive = false;
        var negative = false;
        for (var i = 0; i < values.length; i++) {
            if (values[i] > 0) {
                positive = true;
            }
            if (values[i] < 0) {
                negative = true;
            }
        }

        // Return error if values does not contain at least one positive value
            // and one negative value
        if (!positive || !negative) {
            return error.num;
        }

        // Initialize guess and resultRate
        guess = guess || 0.1;
        var resultRate = guess;

        // Set maximum epsilon for end of iteration
        var epsMax = 1e-10;

        // Implement Newton's method
        var newRate, epsRate, resultValue;
        var contLoop = true;
        do {
            resultValue = irrResult(values, dates, resultRate);
            newRate = resultRate - resultValue / irrResultDeriv(values, dates, resultRate);
            epsRate = Math.abs(newRate - resultRate);
            resultRate = newRate;
            contLoop = (epsRate > epsMax) && (Math.abs(resultValue) > epsMax);
        } while (contLoop);

        // Return internal rate of return
        return resultRate;
    };

    exports.XNPV = function(rate, values, dates) {
        rate = utils.parseNumber(rate);
        values = utils.parseNumberArray(utils.flatten(values));
        dates = utils.parseDateArray(utils.flatten(dates));
        if (utils.anyIsError(rate, values, dates)) {
            return error.value;
        }

        var result = 0;
        for (var i = 0; i < values.length; i++) {
            result += values[i] / Math.pow(1 + rate, DAYS(dates[i], dates[0]) / 365);
        }
        return result;
    };

    exports.YIELD = null;

    exports.YIELDDISC = null;

    exports.YIELDMAT = null;

    return exports;
})();

jexcel.methods.information = (function() {
    var exports = {};
    exports.CELL = null;

    exports.ERROR = {};
    exports.ERROR.TYPE = function(error_val) {
        switch (error_val) {
            case error.nil: return 1;
            case error.div0: return 2;
            case error.value: return 3;
            case error.ref: return 4;
            case error.name: return 5;
            case error.num: return 6;
            case error.na: return 7;
            case error.data: return 8;
        }
        return error.na;
    };

    exports.INFO = null;

    exports.ISBLANK = function(value) {
        return value === null;
    };

    exports.ISBINARY = function (number) {
        return (/^[01]{1,10}$/).test(number);
    };

    exports.ISERR = function(value) {
        return ([error.value, error.ref, error.div0, error.num, error.name, error.nil]).indexOf(value) >= 0 ||
            (typeof value === 'number' && (isNaN(value) || !isFinite(value)));
    };

    exports.ISERROR = function(value) {
        return exports.ISERR(value) || value === error.na;
    };

    exports.ISEVEN = function(number) {
        return (Math.floor(Math.abs(number)) & 1) ? false : true;
    };

    // TODO
    exports.ISFORMULA = null;

    exports.ISLOGICAL = function(value) {
        return value === true || value === false;
    };

    exports.ISNA = function(value) {
        return value === error.na;
    };

    exports.ISNONTEXT = function(value) {
        return typeof(value) !== 'string';
    };

    exports.ISNUMBER = function(value) {
        return typeof(value) === 'number' && !isNaN(value) && isFinite(value);
    };

    exports.ISODD = function(number) {
        return (Math.floor(Math.abs(number)) & 1) ? true : false;
    };

    exports.ISREF = null;

    exports.ISTEXT = function(value) {
        return typeof(value) === 'string';
    };

    exports.N = function(value) {
        if (this.ISNUMBER(value)) {
            return value;
        }
        if (value instanceof Date) {
            return value.getTime();
        }
        if (value === true) {
            return 1;
        }
        if (value === false) {
            return 0;
        }
        if (this.ISERROR(value)) {
            return value;
        }
        return 0;
    };

    exports.NA = function() {
        return error.na;
    };

    exports.SHEET = null;

    exports.SHEETS = null;

    exports.TYPE = function(value) {
        if (this.ISNUMBER(value)) {
            return 1;
        }
        if (this.ISTEXT(value)) {
            return 2;
        }
        if (this.ISLOGICAL(value)) {
            return 4;
        }
        if (this.ISERROR(value)) {
            return 16;
        }
        if (Array.isArray(value)) {
            return 64;
        }
    };

    return exports;
})();

jexcel.methods.logical = (function() {
    var exports = {};

    exports.AND = function() {
        var args = utils.flatten(arguments);
        var result = true;
        for (var i = 0; i < args.length; i++) {
            if (!args[i]) {
                result = false;
            }
        }
        return result;
    };

    exports.CHOOSE = function() {
        if (arguments.length < 2) {
            return error.na;
        }

        var index = arguments[0];
        if (index < 1 || index > 254) {
            return error.value;
        }

        if (arguments.length < index + 1) {
            return error.value;
        }

        return arguments[index];
    };

    exports.FALSE = function() {
        return false;
    };

    exports.IF = function(test, then_value, otherwise_value) {
        return test ? then_value : otherwise_value;
    };

    exports.IFERROR = function(value, valueIfError) {
        if (ISERROR(value)) {
            return valueIfError;
        }
        return value;
    };

    exports.IFNA = function(value, value_if_na) {
        return value === error.na ? value_if_na : value;
    };

    exports.NOT = function(logical) {
        return !logical;
    };

    exports.OR = function() {
        var args = utils.flatten(arguments);
        var result = false;
        for (var i = 0; i < args.length; i++) {
            if (args[i]) {
                result = true;
            }
        }
        return result;
    };

    exports.TRUE = function() {
        return true;
    };

    exports.XOR = function() {
        var args = utils.flatten(arguments);
        var result = 0;
        for (var i = 0; i < args.length; i++) {
            if (args[i]) {
                result++;
            }
        }
        return (Math.floor(Math.abs(result)) & 1) ? true : false;
    };

    exports.SWITCH = function() {
        var result;
        if (arguments.length > 0)  {
            var targetValue = arguments[0];
            var argc = arguments.length - 1;
            var switchCount = Math.floor(argc / 2);
            var switchSatisfied = false;
            var defaultClause = argc % 2 === 0 ? null : arguments[arguments.length - 1];

            if (switchCount) {
                for (var index = 0; index < switchCount; index++) {
                    if (targetValue === arguments[index * 2 + 1]) {
                      result = arguments[index * 2 + 2];
                      switchSatisfied = true;
                      break;
                    }
                }
            }

            if (!switchSatisfied && defaultClause) {
                result = defaultClause;
            }
        }

        return result;
    };

    return exports;
})();

jexcel.methods.math = (function() {
    var exports = {};

    exports.ABS = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return Math.abs(utils.parseNumber(number));
    };

    exports.ACOS = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return Math.acos(number);
    };

    exports.ACOSH = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return Math.log(number + Math.sqrt(number * number - 1));
    };

    exports.ACOT = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return Math.atan(1 / number);
    };

    exports.ACOTH = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return 0.5 * Math.log((number + 1) / (number - 1));
    };

    exports.AGGREGATE = null

    exports.ARABIC = function(text) {
        // Credits: Rafa? Kukawski
        if (!/^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/.test(text)) {
            return error.value;
        }
        var r = 0;
        text.replace(/[MDLV]|C[MD]?|X[CL]?|I[XV]?/g, function(i) {
            r += {
                M: 1000,
                CM: 900,
                D: 500,
                CD: 400,
                C: 100,
                XC: 90,
                L: 50,
                XL: 40,
                X: 10,
                IX: 9,
                V: 5,
                IV: 4,
                I: 1
            }[i];
        });
        return r;
    };

    exports.ASIN = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return Math.asin(number);
    };

    exports.ASINH = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return Math.log(number + Math.sqrt(number * number + 1));
    };

    exports.ATAN = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return Math.atan(number);
    };

    exports.ATAN2 = function(number_x, number_y) {
        number_x = utils.parseNumber(number_x);
        number_y = utils.parseNumber(number_y);
        if (utils.anyIsError(number_x, number_y)) {
            return error.value;
        }
        return Math.atan2(number_x, number_y);
    };

    exports.ATANH = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return Math.log((1 + number) / (1 - number)) / 2;
    };

    exports.BASE = function(number, radix, min_length) {
        min_length = min_length || 0;

        number = utils.parseNumber(number);
        radix = utils.parseNumber(radix);
        min_length = utils.parseNumber(min_length);
        if (utils.anyIsError(number, radix, min_length)) {
            return error.value;
        }
        min_length = (min_length === undefined) ? 0 : min_length;
        var result = number.toString(radix);
        return new Array(Math.max(min_length + 1 - result.length, 0)).join('0') + result;
    };

    exports.CEILING = function(number, significance, mode) {
        significance = (significance === undefined) ? 1 : significance;
        mode = (mode === undefined) ? 0 : mode;

        number = utils.parseNumber(number);
        significance = utils.parseNumber(significance);
        mode = utils.parseNumber(mode);
        if (utils.anyIsError(number, significance, mode)) {
            return error.value;
        }
        if (significance === 0) {
            return 0;
        }

        significance = Math.abs(significance);
        if (number >= 0) {
            return Math.ceil(number / significance) * significance;
        } else {
            if (mode === 0) {
                return -1 * Math.floor(Math.abs(number) / significance) * significance;
            } else {
                return -1 * Math.ceil(Math.abs(number) / significance) * significance;
            }
        }
    };

    exports.CEILING.MATH = exports.CEILING;

    exports.CEILING.PRECISE = exports.CEILING;

    exports.COMBIN = function(number, number_chosen) {
        number = utils.parseNumber(number);
        number_chosen = utils.parseNumber(number_chosen);
        if (utils.anyIsError(number, number_chosen)) {
            return error.value;
        }
        return exports.FACT(number) / (exports.FACT(number_chosen) * exports.FACT(number - number_chosen));
    };

    exports.COMBINA = function(number, number_chosen) {
        number = utils.parseNumber(number);
        number_chosen = utils.parseNumber(number_chosen);
        if (utils.anyIsError(number, number_chosen)) {
            return error.value;
        }
        return (number === 0 && number_chosen === 0) ? 1 : exports.COMBIN(number + number_chosen - 1, number - 1);
    };

    exports.COS = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return Math.cos(number);
    };

    exports.COSH = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return (Math.exp(number) + Math.exp(-number)) / 2;
    };

    exports.COT = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return 1 / Math.tan(number);
    };

    exports.COTH = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        var e2 = Math.exp(2 * number);
        return (e2 + 1) / (e2 - 1);
    };

    exports.CSC = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return 1 / Math.sin(number);
    };

    exports.CSCH = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return 2 / (Math.exp(number) - Math.exp(-number));
    };

    exports.DECIMAL = function(number, radix) {
        if (arguments.length < 1) {
            return error.value;
        }


        return parseInt(number, radix);
    };

    exports.DEGREES = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return number * 180 / Math.PI;
    };

    exports.EVEN = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return exports.CEILING(number, -2, -1);
    };

    exports.EXP = Math.exp;

    var MEMOIZED_FACT = [];
    exports.FACT = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        var n = Math.floor(number);
        if (n === 0 || n === 1) {
            return 1;
        } else if (MEMOIZED_FACT[n] > 0) {
            return MEMOIZED_FACT[n];
        } else {
            MEMOIZED_FACT[n] = exports.FACT(n - 1) * n;
            return MEMOIZED_FACT[n];
        }
    };

    exports.FACTDOUBLE = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        var n = Math.floor(number);
        if (n <= 0) {
            return 1;
        } else {
            return n * exports.FACTDOUBLE(n - 2);
        }
    };

    exports.FLOOR = function(number, significance, mode) {
        significance = (significance === undefined) ? 1 : significance;
        mode = (mode === undefined) ? 0 : mode;

        number = utils.parseNumber(number);
        significance = utils.parseNumber(significance);
        mode = utils.parseNumber(mode);
        if (utils.anyIsError(number, significance, mode)) {
            return error.value;
        }
        if (significance === 0) {
            return 0;
        }

        significance = Math.abs(significance);
        if (number >= 0) {
            return Math.floor(number / significance) * significance;
        } else {
            if (mode === 0) {
                return -1 * Math.ceil(Math.abs(number) / significance) * significance;
            } else {
                return -1 * Math.floor(Math.abs(number) / significance) * significance;
            }
        }
    };

    exports.FLOOR.MATH = exports.FLOOR;

    exports.GCD = null;

    exports.INT = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return Math.floor(number);
    };

    exports.LCM = function() {
        // Credits: Jonas Raoni Soares Silva
        var o = utils.parseNumberArray(utils.flatten(arguments));
        if (o instanceof Error) {
            return o;
        }
        for (var i, j, n, d, r = 1;
            (n = o.pop()) !== undefined;) {
            while (n > 1) {
                if (n % 2) {
                    for (i = 3, j = Math.floor(Math.sqrt(n)); i <= j && n % i; i += 2) {
                      //empty
                    }
                    d = (i <= j) ? i : n;
                } else {
                    d = 2;
                }
                for (n /= d, r *= d, i = o.length; i;
                    (o[--i] % d) === 0 && (o[i] /= d) === 1 && o.splice(i, 1)) {
                    //empty
                }
            }
        }
        return r;
    };

    exports.LN = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return Math.log(number);
    };

    exports.LOG = function(number, base) {
        number = utils.parseNumber(number);
        base = (base === undefined) ? 10 : utils.parseNumber(base);

        if (utils.anyIsError(number, base)) {
            return error.value;
        }

        return Math.log(number) / Math.log(base);
    };

    exports.LOG10 = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return Math.log(number) / Math.log(10);
    };

    exports.MDETERM = null;

    exports.MINVERSE = null;

    exports.MMULT = null;

    exports.MOD = function(dividend, divisor) {
        dividend = utils.parseNumber(dividend);
        divisor = utils.parseNumber(divisor);
        if (utils.anyIsError(dividend, divisor)) {
            return error.value;
        }
        if (divisor === 0) {
            return error.div0;
        }
        var modulus = Math.abs(dividend % divisor);
        return (divisor > 0) ? modulus : -modulus;
    };

    exports.MROUND = function(number, multiple) {
        number = utils.parseNumber(number);
        multiple = utils.parseNumber(multiple);
        if (utils.anyIsError(number, multiple)) {
            return error.value;
        }
        if (number * multiple < 0) {
            return error.num;
        }

        return Math.round(number / multiple) * multiple;
    };

    exports.MULTINOMIAL = function() {
        var args = utils.parseNumberArray(utils.flatten(arguments));
        if (args instanceof Error) {
            return args;
        }
        var sum = 0;
        var divisor = 1;
        for (var i = 0; i < args.length; i++) {
            sum += args[i];
            divisor *= exports.FACT(args[i]);
        }
        return exports.FACT(sum) / divisor;
    };

    exports.MUNIT = null;

    exports.ODD = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        var temp = Math.ceil(Math.abs(number));
        temp = (temp & 1) ? temp : temp + 1;
        return (number > 0) ? temp : -temp;
    };

    exports.PI = function() {
        return Math.PI;
    };

    exports.POWER = function(number, power) {
        number = utils.parseNumber(number);
        power = utils.parseNumber(power);
        if (utils.anyIsError(number, power)) {
            return error.value;
        }
        var result = Math.pow(number, power);
        if (isNaN(result)) {
            return error.num;
        }

        return result;
    };

    exports.PRODUCT = function() {
        var args = utils.parseNumberArray(utils.flatten(arguments));
        if (args instanceof Error) {
            return args;
        }
        var result = 1;
        for (var i = 0; i < args.length; i++) {
            result *= args[i];
        }
        return result;
    };

    exports.QUOTIENT = function(numerator, denominator) {
        numerator = utils.parseNumber(numerator);
        denominator = utils.parseNumber(denominator);
        if (utils.anyIsError(numerator, denominator)) {
            return error.value;
        }
        return parseInt(numerator / denominator, 10);
    };

    exports.RADIANS = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return number * Math.PI / 180;
    };

    exports.RAND = function() {
        return Math.random();
    };

    exports.RANDBETWEEN = function(bottom, top) {
        bottom = utils.parseNumber(bottom);
        top = utils.parseNumber(top);
        if (utils.anyIsError(bottom, top)) {
            return error.value;
        }
        // Creative Commons Attribution 3.0 License
        // Copyright (c) 2012 eqcode
        return bottom + Math.ceil((top - bottom + 1) * Math.random()) - 1;
    };

    exports.ROMAN = null;

    exports.ROUND = function(number, digits) {
        number = utils.parseNumber(number);
        digits = utils.parseNumber(digits);
        if (utils.anyIsError(number, digits)) {
            return error.value;
        }
        return Math.round(number * Math.pow(10, digits)) / Math.pow(10, digits);
    };

    exports.ROUNDDOWN = function(number, digits) {
        number = utils.parseNumber(number);
        digits = utils.parseNumber(digits);
        if (utils.anyIsError(number, digits)) {
            return error.value;
        }
        var sign = (number > 0) ? 1 : -1;
        return sign * (Math.floor(Math.abs(number) * Math.pow(10, digits))) / Math.pow(10, digits);
    };

    exports.ROUNDUP = function(number, digits) {
        number = utils.parseNumber(number);
        digits = utils.parseNumber(digits);
        if (utils.anyIsError(number, digits)) {
            return error.value;
        }
        var sign = (number > 0) ? 1 : -1;
        return sign * (Math.ceil(Math.abs(number) * Math.pow(10, digits))) / Math.pow(10, digits);
    };

    exports.SEC = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return 1 / Math.cos(number);
    };

    exports.SECH = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return 2 / (Math.exp(number) + Math.exp(-number));
    };

    exports.SERIESSUM = function(x, n, m, coefficients) {
        x = utils.parseNumber(x);
        n = utils.parseNumber(n);
        m = utils.parseNumber(m);
        coefficients = utils.parseNumberArray(coefficients);
        if (utils.anyIsError(x, n, m, coefficients)) {
            return error.value;
        }
        var result = coefficients[0] * Math.pow(x, n);
        for (var i = 1; i < coefficients.length; i++) {
            result += coefficients[i] * Math.pow(x, n + i * m);
        }
        return result;
    };

    exports.SIGN = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        if (number < 0) {
            return -1;
        } else if (number === 0) {
            return 0;
        } else {
            return 1;
        }
    };

    exports.SIN = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return Math.sin(number);
    };

    exports.SINH = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return (Math.exp(number) - Math.exp(-number)) / 2;
    };

    exports.SQRT = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        if (number < 0) {
            return error.num;
        }
        return Math.sqrt(number);
    };

    exports.SQRTPI = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return Math.sqrt(number * Math.PI);
    };

    exports.SUBTOTAL = null;

    exports.ADD = function (num1, num2) {
        if (arguments.length !== 2) {
            return error.na;
        }

        num1 = utils.parseNumber(num1);
        num2 = utils.parseNumber(num2);
        if (utils.anyIsError(num1, num2)) {
            return error.value;
        }

        return num1 + num2;
    };

    exports.MINUS = function (num1, num2) {
        if (arguments.length !== 2) {
            return error.na;
        }

        num1 = utils.parseNumber(num1);
        num2 = utils.parseNumber(num2);
        if (utils.anyIsError(num1, num2)) {
            return error.value;
        }

        return num1 - num2;
    };

    exports.DIVIDE = function (dividend, divisor) {
        if (arguments.length !== 2) {
            return error.na;
        }

        dividend = utils.parseNumber(dividend);
        divisor = utils.parseNumber(divisor);
        if (utils.anyIsError(dividend, divisor)) {
            return error.value;
        }

        if (divisor === 0) {
            return error.div0;
        }

        return dividend / divisor;
    };

    exports.MULTIPLY = function (factor1, factor2) {
        if (arguments.length !== 2) {
            return error.na;
        }

        factor1 = utils.parseNumber(factor1);
        factor2 = utils.parseNumber(factor2);
        if (utils.anyIsError(factor1, factor2)) {
            return error.value;
        }

        return factor1 * factor2;
    };

    exports.GTE = function (num1, num2) {
        if (arguments.length !== 2) {
            return error.na;
        }

        num1 = utils.parseNumber(num1);
        num2 = utils.parseNumber(num2);
        if (utils.anyIsError(num1, num2)) {
            return error.error;
        }

        return num1 >= num2;
    };

    exports.LT = function (num1, num2) {
        if (arguments.length !== 2) {
            return error.na;
        }

        num1 = utils.parseNumber(num1);
        num2 = utils.parseNumber(num2);
        if (utils.anyIsError(num1, num2)) {
            return error.error;
        }

        return num1 < num2;
    };

    exports.LTE = function (num1, num2) {
        if (arguments.length !== 2) {
            return error.na;
        }

        num1 = utils.parseNumber(num1);
        num2 = utils.parseNumber(num2);
        if (utils.anyIsError(num1, num2)) {
            return error.error;
        }

        return num1 <= num2;
    };

    exports.EQ = function (value1, value2) {
        if (arguments.length !== 2) {
            return error.na;
        }

        return value1 === value2;
    };

    exports.NE = function (value1, value2) {
        if (arguments.length !== 2) {
            return error.na;
        }

        return value1 !== value2;
    };

    exports.POW = function (base, exponent) {
        if (arguments.length !== 2) {
            return error.na;
        }

        base = utils.parseNumber(base);
        exponent = utils.parseNumber(exponent);
        if (utils.anyIsError(base, exponent)) {
            return error.error;
        }

        return exports.POWER(base, exponent);
    };

    exports.SUM = function() {
        var result = 0;
        var argsKeys = Object.keys(arguments);
        for (var i = 0; i < argsKeys.length; ++i) {
            var elt = arguments[argsKeys[i]];
            if (typeof elt === 'number') {
                result += elt;
            } else if (typeof elt === 'string') {
                var parsed = parseFloat(elt);
                !isNaN(parsed) && (result += parsed);
            } else if (Array.isArray(elt)) {
                result += exports.SUM.apply(null, elt);
            }
        }
        return result;
    };

    exports.SUMIF = function(range, criteria) {
        range = utils.parseNumberArray(utils.flatten(range));
        if (range instanceof Error) {
            return range;
        }
        var result = 0;
        for (var i = 0; i < range.length; i++) {
            result += (eval(range[i] + criteria)) ? range[i] : 0; // jshint ignore:line
        }
        return result;
    };

    exports.SUMIFS = function() {
        var args = utils.argsToArray(arguments);
        var range = utils.parseNumberArray(utils.flatten(args.shift()));
        if (range instanceof Error) {
            return range;
        }
        var criteria = args;

        var n_range_elements = range.length;
        var n_criterias = criteria.length;

        var result = 0;
        for (var i = 0; i < n_range_elements; i++) {
            var el = range[i];
            var condition = '';
            for (var c = 0; c < n_criterias; c++) {
                condition += el + criteria[c];
                if (c !== n_criterias - 1) {
                    condition += '&&';
                }
            }
            if (eval(condition)) { // jshint ignore:line
                result += el;
            }
        }
        return result;
    };

    exports.SUMPRODUCT = null;

    exports.SUMSQ = function() {
        var numbers = utils.parseNumberArray(utils.flatten(arguments));
        if (numbers instanceof Error) {
            return numbers;
        }
        var result = 0;
        var length = numbers.length;
        for (var i = 0; i < length; i++) {
            result += (ISNUMBER(numbers[i])) ? numbers[i] * numbers[i] : 0;
        }
        return result;
    };

    exports.SUMX2MY2 = function(array_x, array_y) {
        array_x = utils.parseNumberArray(utils.flatten(array_x));
        array_y = utils.parseNumberArray(utils.flatten(array_y));
        if (utils.anyIsError(array_x, array_y)) {
            return error.value;
        }
        var result = 0;
        for (var i = 0; i < array_x.length; i++) {
            result += array_x[i] * array_x[i] - array_y[i] * array_y[i];
        }
        return result;
    };

    exports.SUMX2PY2 = function(array_x, array_y) {
        array_x = utils.parseNumberArray(utils.flatten(array_x));
        array_y = utils.parseNumberArray(utils.flatten(array_y));
        if (utils.anyIsError(array_x, array_y)) {
            return error.value;
        }
        var result = 0;
        array_x = utils.parseNumberArray(utils.flatten(array_x));
        array_y = utils.parseNumberArray(utils.flatten(array_y));
        for (var i = 0; i < array_x.length; i++) {
            result += array_x[i] * array_x[i] + array_y[i] * array_y[i];
        }
        return result;
    };

    exports.SUMXMY2 = function(array_x, array_y) {
        array_x = utils.parseNumberArray(utils.flatten(array_x));
        array_y = utils.parseNumberArray(utils.flatten(array_y));
        if (utils.anyIsError(array_x, array_y)) {
            return error.value;
        }
        var result = 0;
        array_x = utils.flatten(array_x);
        array_y = utils.flatten(array_y);
        for (var i = 0; i < array_x.length; i++) {
            result += Math.pow(array_x[i] - array_y[i], 2);
        }
        return result;
    };

    exports.TAN = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return Math.tan(number);
    };

    exports.TANH = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        var e2 = Math.exp(2 * number);
        return (e2 - 1) / (e2 + 1);
    };

    exports.TRUNC = function(number, digits) {
        digits = (digits === undefined) ? 0 : digits;
        number = utils.parseNumber(number);
        digits = utils.parseNumber(digits);
        if (utils.anyIsError(number, digits)) {
            return error.value;
        }
        var sign = (number > 0) ? 1 : -1;
        return sign * (Math.floor(Math.abs(number) * Math.pow(10, digits))) / Math.pow(10, digits);
    };

    return exports;
})();

jexcel.methods.misc = (function() {
    var exports = {};

    exports.UNIQUE = function () {
        var result = [];
        for (var i = 0; i < arguments.length; ++i) {
            var hasElement = false;
            var element = arguments[i];

            // Check if we've already seen this element.
            for (var j = 0; j < result.length; ++j) {
                hasElement = result[j] === element;
                if (hasElement) { break; }
            }

            // If we did not find it, add it to the result.
            if (!hasElement) {
                result.push(element);
            }
        }
        return result;
    };

    exports.FLATTEN = utils.flatten;

    exports.ARGS2ARRAY = function () {
        return Array.prototype.slice.call(arguments, 0);
    };

    exports.REFERENCE = function (context, reference) {
        try {
            var path = reference.split('.');
            var result = context;
            for (var i = 0; i < path.length; ++i) {
                var step = path[i];
                if (step[step.length - 1] === ']') {
                    var opening = step.indexOf('[');
                    var index = step.substring(opening + 1, step.length - 1);
                    result = result[step.substring(0, opening)][index];
                } else {
                    result = result[step];
                }
            }
            return result;
        } catch (error) {}
    };

    exports.JOIN = function (array, separator) {
        return array.join(separator);
    };

    exports.NUMBERS = function () {
        var possibleNumbers = utils.flatten(arguments);
        return possibleNumbers.filter(function (el) {
            return typeof el === 'number';
        });
    };

    exports.NUMERAL = null;

    return exports;
})();

jexcel.methods.text = (function() {
    var exports = {};

    exports.ASC = null;

    exports.BAHTTEXT = null;

    exports.CHAR = function(number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return String.fromCharCode(number);
    };

    exports.CLEAN = function(text) {
        text = text || '';
        var re = /[\0-\x1F]/g;
        return text.replace(re, "");
    };

    exports.CODE = function(text) {
        text = text || '';
        return text.charCodeAt(0);
    };

    exports.CONCATENATE = function() {
        var args = utils.flatten(arguments);

        var trueFound = 0;
        while ((trueFound = args.indexOf(true)) > -1) {
            args[trueFound] = 'TRUE';
        }

        var falseFound = 0;
        while ((falseFound = args.indexOf(false)) > -1) {
            args[falseFound] = 'FALSE';
        }

        return args.join('');
    };

    exports.DBCS = null;

    exports.DOLLAR = null;

    exports.EXACT = function(text1, text2) {
        return text1 === text2;
    };

    exports.FIND = function(find_text, within_text, position) {
        position = (position === undefined) ? 0 : position;
        return within_text ? within_text.indexOf(find_text, position - 1) + 1 : null;
    };

    exports.FIXED = null;

    exports.HTML2TEXT = function (value) {
        var result = '';

        if (value) {
            if (value instanceof Array) {
                value.forEach(function (line) {
                    if (result !== '') {
                      result += '\n';
                    }
                    result += (line.replace(/<(?:.|\n)*?>/gm, ''));
                });
            } else {
                result = value.replace(/<(?:.|\n)*?>/gm, '');
            }
        }

        return result;
    };

    exports.LEFT = function(text, number) {
        number = (number === undefined) ? 1 : number;
        number = utils.parseNumber(number);
        if (number instanceof Error || typeof text !== 'string') {
            return error.value;
        }
        return text ? text.substring(0, number) : null;
    };

    exports.LEN = function(text) {
        if (arguments.length === 0) {
            return error.error;
        }

        if (typeof text === 'string') {
            return text ? text.length : 0;
        }

        if (text.length) {
            return text.length;
        }

        return error.value;
    };

    exports.LOWER = function(text) {
        if (typeof text !== 'string') {
            return error.value;
        }
        return text ? text.toLowerCase() : text;
    };

    exports.MID = function(text, start, number) {
        start = utils.parseNumber(start);
        number = utils.parseNumber(number);
        if (utils.anyIsError(start, number) || typeof text !== 'string') {
            return number;
        }

        var begin = start - 1;
        var end = begin + number;

        return text.substring(begin, end);
    };

    exports.NUMBERVALUE = null;

    exports.PRONETIC = null;

    exports.PROPER = function(text) {
        if (text === undefined || text.length === 0) {
            return error.value;
        }
        if (text === true) {
            text = 'TRUE';
        }
        if (text === false) {
            text = 'FALSE';
        }
        if (isNaN(text) && typeof text === 'number') {
            return error.value;
        }
        if (typeof text === 'number') {
            text = '' + text;
        }

        return text.replace(/\w\S*/g, function(txt) {
            return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
        });
    };

    exports.REGEXEXTRACT = function (text, regular_expression) {
        var match = text.match(new RegExp(regular_expression));
        return match ? (match[match.length > 1 ? match.length - 1 : 0]) : null;
    };

    exports.REGEXMATCH = function (text, regular_expression, full) {
        var match = text.match(new RegExp(regular_expression));
        return full ? match : !!match;
    };

    exports.REGEXREPLACE = function (text, regular_expression, replacement) {
        return text.replace(new RegExp(regular_expression), replacement);
    };

    exports.REPLACE = function(text, position, length, new_text) {
        position = utils.parseNumber(position);
        length = utils.parseNumber(length);
        if (utils.anyIsError(position, length) ||
            typeof text !== 'string' ||
            typeof new_text !== 'string') {
            return error.value;
        }
        return text.substr(0, position - 1) + new_text + text.substr(position - 1 + length);
    };

    exports.REPT = function(text, number) {
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return new Array(number + 1).join(text);
    };

    exports.RIGHT = function(text, number) {
        number = (number === undefined) ? 1 : number;
        number = utils.parseNumber(number);
        if (number instanceof Error) {
            return number;
        }
        return text ? text.substring(text.length - number) : null;
    };

    exports.SEARCH = function(find_text, within_text, position) {
        var foundAt;
        if (typeof find_text !== 'string' || typeof within_text !== 'string') {
            return error.value;
        }
        position = (position === undefined) ? 0 : position;
        foundAt = within_text.toLowerCase().indexOf(find_text.toLowerCase(), position - 1)+1;
        return (foundAt === 0)?error.value:foundAt;
    };

    exports.SPLIT = function (text, separator) {
        return text.split(separator);
    };

    exports.SUBSTITUTE = function(text, old_text, new_text, occurrence) {
        if (!text || !old_text || !new_text) {
            return text;
        } else if (occurrence === undefined) {
            return text.replace(new RegExp(old_text, 'g'), new_text);
        } else {
            var index = 0;
            var i = 0;
            while (text.indexOf(old_text, index) > 0) {
                index = text.indexOf(old_text, index + 1);
                i++;
                if (i === occurrence) {
                    return text.substring(0, index) + new_text + text.substring(index + old_text.length);
                }
            }
        }
    };

    exports.T = function(value) {
        return (typeof value === "string") ? value : '';
    };

    exports.TEXT = null;

    exports.TRIM = function(text) {
        if (typeof text !== 'string') {
            return error.value;
        }
        return text.replace(/ +/g, ' ').trim();
    };

    exports.UNICHAR = exports.CHAR;

    exports.UNICODE = exports.CODE;

    exports.UPPER = function(text) {
        if (typeof text !== 'string') {
            return error.value;
        }
        return text.toUpperCase();
    };

    exports.VALUE = null;

    return exports;
})();

jexcel.methods.stats = (function() {
    var exports = {};

    var SQRT2PI = 2.5066282746310002;

    exports.AVEDEV = null;

    exports.AVERAGE = function() {
        var range = utils.numbers(utils.flatten(arguments));
        var n = range.length;
        var sum = 0;
        var count = 0;
        for (var i = 0; i < n; i++) {
            sum += range[i];
            count += 1;
        }
        return sum / count;
    };

    exports.AVERAGEA = function() {
        var range = utils.flatten(arguments);
        var n = range.length;
        var sum = 0;
        var count = 0;
        for (var i = 0; i < n; i++) {
            var el = range[i];
            if (typeof el === 'number') {
                sum += el;
            }
            if (el === true) {
                sum++;
            }
            if (el !== null) {
                count++;
            }
        }
        return sum / count;
    };

    exports.AVERAGEIF = function(range, criteria, average_range) {
        average_range = average_range || range;
        range = utils.flatten(range);
        average_range = utils.parseNumberArray(utils.flatten(average_range));
        if (average_range instanceof Error) {
            return average_range;
        }
        var average_count = 0;
        var result = 0;
        for (var i = 0; i < range.length; i++) {
            if (eval(range[i] + criteria)) { // jshint ignore:line
                result += average_range[i];
                average_count++;
            }
        }
        return result / average_count;
    };

    exports.AVERAGEIFS = null;

    exports.COUNT = function() {
        return utils.numbers(utils.flatten(arguments)).length;
    };

    exports.COUNTA = function() {
        var range = utils.flatten(arguments);
        return range.length - exports.COUNTBLANK(range);
    };

    exports.COUNTIN = function (range, value) {
        var result = 0;
        for (var i = 0; i < range.length; i++) {
            if (range[i] === value) {
                result++;
            }
        }
        return result;
    };

    exports.COUNTBLANK = function() {
        var range = utils.flatten(arguments);
        var blanks = 0;
        var element;
        for (var i = 0; i < range.length; i++) {
            element = range[i];
            if (element === null || element === '') {
                blanks++;
            }
        }
        return blanks;
    };

    exports.COUNTIF = function(range, criteria) {
        range = utils.flatten(range);
        if (!/[<>=!]/.test(criteria)) {
            criteria = '=="' + criteria + '"';
        }
        var matches = 0;
        for (var i = 0; i < range.length; i++) {
            if (typeof range[i] !== 'string') {
                if (eval(range[i] + criteria)) { // jshint ignore:line
                    matches++;
                }
            } else {
                if (eval('"' + range[i] + '"' + criteria)) { // jshint ignore:line
                    matches++;
                }
            }
        }
        return matches;
    };

    exports.COUNTIFS = function() {
        var args = utils.argsToArray(arguments);
        var results = new Array(utils.flatten(args[0]).length);
        for (var i = 0; i < results.length; i++) {
            results[i] = true;
        }
        for (i = 0; i < args.length; i += 2) {
            var range = utils.flatten(args[i]);
            var criteria = args[i + 1];
            if (!/[<>=!]/.test(criteria)) {
                criteria = '=="' + criteria + '"';
            }
            for (var j = 0; j < range.length; j++) {
                if (typeof range[j] !== 'string') {
                    results[j] = results[j] && eval(range[j] + criteria); // jshint ignore:line
                } else {
                    results[j] = results[j] && eval('"' + range[j] + '"' + criteria); // jshint ignore:line
                }
            }
        }
        var result = 0;
        for (i = 0; i < results.length; i++) {
            if (results[i]) {
                result++;
            }
        }
        return result;
    };

    exports.COUNTUNIQUE = function () {
        return UNIQUE.apply(null, utils.flatten(arguments)).length;
    };

    exports.FISHER = function(x) {
        x = utils.parseNumber(x);
        if (x instanceof Error) {
            return x;
        }
        return Math.log((1 + x) / (1 - x)) / 2;
    };

    exports.FISHERINV = function(y) {
        y = utils.parseNumber(y);
        if (y instanceof Error) {
            return y;
        }
        var e2y = Math.exp(2 * y);
        return (e2y - 1) / (e2y + 1);
    };

    exports.FREQUENCY = function(data, bins) {
        data = utils.parseNumberArray(utils.flatten(data));
        bins = utils.parseNumberArray(utils.flatten(bins));
        if (utils.anyIsError(data, bins)) {
            return error.value;
        }
        var n = data.length;
        var b = bins.length;
        var r = [];
        for (var i = 0; i <= b; i++) {
            r[i] = 0;
            for (var j = 0; j < n; j++) {
                if (i === 0) {
                    if (data[j] <= bins[0]) {
                        r[0] += 1;
                    }
                } else if (i < b) {
                    if (data[j] > bins[i - 1] && data[j] <= bins[i]) {
                        r[i] += 1;
                    }
                } else if (i === b) {
                    if (data[j] > bins[b - 1]) {
                        r[b] += 1;
                    }
                }
            }
        }
        return r;
    };

    exports.LARGE = function(range, k) {
        range = utils.parseNumberArray(utils.flatten(range));
        k = utils.parseNumber(k);
        if (utils.anyIsError(range, k)) {
            return range;
        }
        return range.sort(function(a, b) {
            return b - a;
        })[k - 1];
    };

    exports.MAX = function() {
        var range = utils.numbers(utils.flatten(arguments));
        return (range.length === 0) ? 0 : Math.max.apply(Math, range);
    };

    exports.MAXA = function() {
        var range = utils.arrayValuesToNumbers(utils.flatten(arguments));
        return (range.length === 0) ? 0 : Math.max.apply(Math, range);
    };

    exports.MIN = function() {
        var range = utils.numbers(utils.flatten(arguments));
        return (range.length === 0) ? 0 : Math.min.apply(Math, range);
    };

    exports.MINA = function() {
        var range = utils.arrayValuesToNumbers(utils.flatten(arguments));
        return (range.length === 0) ? 0 : Math.min.apply(Math, range);
    };

    exports.MODE = {};

    exports.MODE.MULT = function() {
        // Credits: Roönaän
        var range = utils.parseNumberArray(utils.flatten(arguments));
        if (range instanceof Error) {
            return range;
        }
        var n = range.length;
        var count = {};
        var maxItems = [];
        var max = 0;
        var currentItem;

        for (var i = 0; i < n; i++) {
            currentItem = range[i];
            count[currentItem] = count[currentItem] ? count[currentItem] + 1 : 1;
            if (count[currentItem] > max) {
                max = count[currentItem];
                maxItems = [];
            }
            if (count[currentItem] === max) {
                maxItems[maxItems.length] = currentItem;
            }
        }
        return maxItems;
    };

    exports.MODE.SNGL = function() {
        var range = utils.parseNumberArray(utils.flatten(arguments));
        if (range instanceof Error) {
            return range;
        }
        return exports.MODE.MULT(range).sort(function(a, b) {
            return a - b;
        })[0];
    };

    exports.PERCENTILE = {};

    exports.PERCENTILE.EXC = function(array, k) {
        array = utils.parseNumberArray(utils.flatten(array));
        k = utils.parseNumber(k);
        if (utils.anyIsError(array, k)) {
            return error.value;
        }
        array = array.sort(function(a, b) {
            {
                return a - b;
            }
        });
        var n = array.length;
        if (k < 1 / (n + 1) || k > 1 - 1 / (n + 1)) {
            return error.num;
        }
        var l = k * (n + 1) - 1;
        var fl = Math.floor(l);
        return utils.cleanFloat((l === fl) ? array[l] : array[fl] + (l - fl) * (array[fl + 1] - array[fl]));
    };

    exports.PERCENTILE.INC = function(array, k) {
        array = utils.parseNumberArray(utils.flatten(array));
        k = utils.parseNumber(k);
        if (utils.anyIsError(array, k)) {
            return error.value;
        }
        array = array.sort(function(a, b) {
            return a - b;
        });
        var n = array.length;
        var l = k * (n - 1);
        var fl = Math.floor(l);
        return utils.cleanFloat((l === fl) ? array[l] : array[fl] + (l - fl) * (array[fl + 1] - array[fl]));
    };

    exports.PERCENTRANK = {};

    exports.PERCENTRANK.EXC = function(array, x, significance) {
        significance = (significance === undefined) ? 3 : significance;
        array = utils.parseNumberArray(utils.flatten(array));
        x = utils.parseNumber(x);
        significance = utils.parseNumber(significance);
        if (utils.anyIsError(array, x, significance)) {
            return error.value;
        }
        array = array.sort(function(a, b) {
            return a - b;
        });
        var uniques = UNIQUE.apply(null, array);
        var n = array.length;
        var m = uniques.length;
        var power = Math.pow(10, significance);
        var result = 0;
        var match = false;
        var i = 0;
        while (!match && i < m) {
            if (x === uniques[i]) {
                result = (array.indexOf(uniques[i]) + 1) / (n + 1);
                match = true;
            } else if (x >= uniques[i] && (x < uniques[i + 1] || i === m - 1)) {
                result = (array.indexOf(uniques[i]) + 1 + (x - uniques[i]) / (uniques[i + 1] - uniques[i])) / (n + 1);
                match = true;
            }
            i++;
        }
        return Math.floor(result * power) / power;
    };

    exports.PERCENTRANK.INC = function(array, x, significance) {
        significance = (significance === undefined) ? 3 : significance;
        array = utils.parseNumberArray(utils.flatten(array));
        x = utils.parseNumber(x);
        significance = utils.parseNumber(significance);
        if (utils.anyIsError(array, x, significance)) {
            return error.value;
        }
        array = array.sort(function(a, b) {
            return a - b;
        });
        var uniques = UNIQUE.apply(null, array);
        var n = array.length;
        var m = uniques.length;
        var power = Math.pow(10, significance);
        var result = 0;
        var match = false;
        var i = 0;
        while (!match && i < m) {
            if (x === uniques[i]) {
                result = array.indexOf(uniques[i]) / (n - 1);
                match = true;
            } else if (x >= uniques[i] && (x < uniques[i + 1] || i === m - 1)) {
                result = (array.indexOf(uniques[i]) + (x - uniques[i]) / (uniques[i + 1] - uniques[i])) / (n - 1);
                match = true;
            }
            i++;
        }
        return Math.floor(result * power) / power;
    };

    exports.PERMUT = function(number, number_chosen) {
        number = utils.parseNumber(number);
        number_chosen = utils.parseNumber(number_chosen);
        if (utils.anyIsError(number, number_chosen)) {
            return error.value;
        }
        return FACT(number) / FACT(number - number_chosen);
    };

    exports.PERMUTATIONA = function(number, number_chosen) {
        number = utils.parseNumber(number);
        number_chosen = utils.parseNumber(number_chosen);
        if (utils.anyIsError(number, number_chosen)) {
            return error.value;
        }
        return Math.pow(number, number_chosen);
    };

    exports.PHI = function(x) {
        x = utils.parseNumber(x);
        if (x instanceof Error) {
            return error.value;
        }
        return Math.exp(-0.5 * x * x) / SQRT2PI;
    };

    exports.PROB = function(range, probability, lower, upper) {
        if (lower === undefined) {
            return 0;
        }
        upper = (upper === undefined) ? lower : upper;

        range = utils.parseNumberArray(utils.flatten(range));
        probability = utils.parseNumberArray(utils.flatten(probability));
        lower = utils.parseNumber(lower);
        upper = utils.parseNumber(upper);
        if (utils.anyIsError(range, probability, lower, upper)) {
            return error.value;
        }

        if (lower === upper) {
            return (range.indexOf(lower) >= 0) ? probability[range.indexOf(lower)] : 0;
        }

        var sorted = range.sort(function(a, b) {
            return a - b;
        });
        var n = sorted.length;
        var result = 0;
        for (var i = 0; i < n; i++) {
            if (sorted[i] >= lower && sorted[i] <= upper) {
                result += probability[range.indexOf(sorted[i])];
            }
        }
        return result;
    };

    exports.QUARTILE = {};

    exports.QUARTILE.EXC = function(range, quart) {
        range = utils.parseNumberArray(utils.flatten(range));
        quart = utils.parseNumber(quart);
        if (utils.anyIsError(range, quart)) {
            return error.value;
        }
        switch (quart) {
            case 1:
                return exports.PERCENTILE.EXC(range, 0.25);
            case 2:
                return exports.PERCENTILE.EXC(range, 0.5);
            case 3:
                return exports.PERCENTILE.EXC(range, 0.75);
            default:
                return error.num;
        }
    };

    exports.QUARTILE.INC = function(range, quart) {
        range = utils.parseNumberArray(utils.flatten(range));
        quart = utils.parseNumber(quart);
        if (utils.anyIsError(range, quart)) {
            return error.value;
        }
        switch (quart) {
            case 1:
                return exports.PERCENTILE.INC(range, 0.25);
            case 2:
                return exports.PERCENTILE.INC(range, 0.5);
            case 3:
                return exports.PERCENTILE.INC(range, 0.75);
            default:
                return error.num;
        }
    };

    exports.RANK = {};

    exports.RANK.AVG = function(number, range, order) {
        number = utils.parseNumber(number);
        range = utils.parseNumberArray(utils.flatten(range));
        if (utils.anyIsError(number, range)) {
            return error.value;
        }
        range = utils.flatten(range);
        order = order || false;
        var sort = (order) ? function(a, b) {
            return a - b;
        } : function(a, b) {
            return b - a;
        };
        range = range.sort(sort);

        var length = range.length;
        var count = 0;
        for (var i = 0; i < length; i++) {
            if (range[i] === number) {
                count++;
            }
        }

        return (count > 1) ? (2 * range.indexOf(number) + count + 1) / 2 : range.indexOf(number) + 1;
    };

    exports.RANK.EQ = function(number, range, order) {
        number = utils.parseNumber(number);
        range = utils.parseNumberArray(utils.flatten(range));
        if (utils.anyIsError(number, range)) {
            return error.value;
        }
        order = order || false;
        var sort = (order) ? function(a, b) {
            return a - b;
        } : function(a, b) {
            return b - a;
        };
        range = range.sort(sort);
        return range.indexOf(number) + 1;
    };

    exports.RSQ = function(data_x, data_y) { // no need to flatten here, PEARSON will take care of that
        data_x = utils.parseNumberArray(utils.flatten(data_x));
        data_y = utils.parseNumberArray(utils.flatten(data_y));
        if (utils.anyIsError(data_x, data_y)) {
            return error.value;
        }
        return Math.pow(exports.PEARSON(data_x, data_y), 2);
    };

    exports.SMALL = function(range, k) {
        range = utils.parseNumberArray(utils.flatten(range));
        k = utils.parseNumber(k);
        if (utils.anyIsError(range, k)) {
            return range;
        }
        return range.sort(function(a, b) {
            return a - b;
        })[k - 1];
    };

    exports.STANDARDIZE = function(x, mean, sd) {
        x = utils.parseNumber(x);
        mean = utils.parseNumber(mean);
        sd = utils.parseNumber(sd);
        if (utils.anyIsError(x, mean, sd)) {
            return error.value;
        }
        return (x - mean) / sd;
    };

    exports.STDEV = {};

    exports.STDEV.P = function() {
        var v = exports.VAR.P.apply(this, arguments);
        return Math.sqrt(v);
    };

    exports.STDEV.S = function() {
        var v = exports.VAR.S.apply(this, arguments);
        return Math.sqrt(v);
    };

    exports.STDEVA = function() {
        var v = exports.VARA.apply(this, arguments);
        return Math.sqrt(v);
    };

    exports.STDEVPA = function() {
        var v = exports.VARPA.apply(this, arguments);
        return Math.sqrt(v);
    };

    exports.VAR = {};

    exports.VAR.P = function() {
        var range = utils.numbers(utils.flatten(arguments));
        var n = range.length;
        var sigma = 0;
        var mean = exports.AVERAGE(range);
        for (var i = 0; i < n; i++) {
            sigma += Math.pow(range[i] - mean, 2);
        }
        return sigma / n;
    };

    exports.VAR.S = function() {
        var range = utils.numbers(utils.flatten(arguments));
        var n = range.length;
        var sigma = 0;
        var mean = exports.AVERAGE(range);
        for (var i = 0; i < n; i++) {
            sigma += Math.pow(range[i] - mean, 2);
        }
        return sigma / (n - 1);
    };

    exports.VARA = function() {
        var range = utils.flatten(arguments);
        var n = range.length;
        var sigma = 0;
        var count = 0;
        var mean = exports.AVERAGEA(range);
        for (var i = 0; i < n; i++) {
            var el = range[i];
            if (typeof el === 'number') {
                sigma += Math.pow(el - mean, 2);
            } else if (el === true) {
                sigma += Math.pow(1 - mean, 2);
            } else {
                sigma += Math.pow(0 - mean, 2);
            }

            if (el !== null) {
                count++;
            }
        }
        return sigma / (count - 1);
    };

    exports.VARPA = function() {
        var range = utils.flatten(arguments);
        var n = range.length;
        var sigma = 0;
        var count = 0;
        var mean = exports.AVERAGEA(range);
        for (var i = 0; i < n; i++) {
            var el = range[i];
            if (typeof el === 'number') {
                sigma += Math.pow(el - mean, 2);
            } else if (el === true) {
                sigma += Math.pow(1 - mean, 2);
            } else {
                sigma += Math.pow(0 - mean, 2);
            }

            if (el !== null) {
                count++;
            }
        }
        return sigma / count;
    };

    exports.WEIBULL = {};

    exports.WEIBULL.DIST = function(x, alpha, beta, cumulative) {
        x = utils.parseNumber(x);
        alpha = utils.parseNumber(alpha);
        beta = utils.parseNumber(beta);
        if (utils.anyIsError(x, alpha, beta)) {
            return error.value;
        }
        return (cumulative) ? 1 - Math.exp(-Math.pow(x / beta, alpha)) : Math.pow(x, alpha - 1) * Math.exp(-Math.pow(x / beta, alpha)) * alpha / Math.pow(beta, alpha);
    };

    exports.Z = {};

    exports.Z.TEST = function(range, x, sd) {
        range = utils.parseNumberArray(utils.flatten(range));
        x = utils.parseNumber(x);
        if (utils.anyIsError(range, x)) {
            return error.value;
        }

        sd = sd || exports.STDEV.S(range);
        var n = range.length;
        return 1 - exports.NORM.S.DIST((exports.AVERAGE(range) - x) / (sd / Math.sqrt(n)), true);
    };

    return exports;
})();

for (var i = 0; i < Object.keys(jexcel.methods).length; i++) {
    var methods = jexcel.methods[Object.keys(jexcel.methods)[i]];
    for (var j = 0; j < Object.keys(methods).length; j++) {
        if (typeof(methods[Object.keys(methods)[j]]) == 'function') {
            window[Object.keys(methods)[j]] = methods[Object.keys(methods)[j]];
        } else {
            window[Object.keys(methods)[j]] = function() {
                return Object.keys(methods)[j] + 'Not implemented';
            }
        }
    }
}




    return jexcel;

})));

/***/ }),

/***/ "./node_modules/jsuites/dist/jsuites.css":
/*!***********************************************!*\
  !*** ./node_modules/jsuites/dist/jsuites.css ***!
  \***********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var api = __webpack_require__(/*! ../../style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
            var content = __webpack_require__(/*! !../../css-loader/dist/cjs.js!./jsuites.css */ "./node_modules/css-loader/dist/cjs.js!./node_modules/jsuites/dist/jsuites.css");

            content = content.__esModule ? content.default : content;

            if (typeof content === 'string') {
              content = [[module.i, content, '']];
            }

var options = {};

options.insert = "head";
options.singleton = false;

var update = api(content, options);



module.exports = content.locals || {};

/***/ }),

/***/ "./node_modules/jsuites/dist/jsuites.js":
/*!**********************************************!*\
  !*** ./node_modules/jsuites/dist/jsuites.js ***!
  \**********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {


/**
 * (c) jSuites Javascript Web Components (v2.8)
 *
 * Author: Paul Hodel <paul.hodel@gmail.com>
 * Website: https://bossanova.uk/jsuites/
 * Description: Create amazing web based applications.
 *
 * MIT License
 *
 */
;(function (global, factory) {
     true ? module.exports = factory() :
    undefined;
}(this, (function () {

    'use strict';

var jSuites = function(options) {
    var obj = {}

    obj.init = function() {
        // Find root element
        var app = document.querySelector('.japp');

        // Root element
        if (app) {
            obj.el = app;
        } else {
            obj.el = document.body;
        }

        // Loading modules
        var modules = document.querySelectorAll('[data-autoload]');
        for (var i = 0; i < modules.length; i++) {
            var m = modules[i].getAttribute('data-autoload');
            if (typeof(window[m]) == 'function') {
                window[m](modules[i]);
            }
        }
    }

    obj.guid = function() {
        var guid = '';
        for (var i = 0; i < 32; i++) {
            guid += Math.floor(Math.random()*0xF).toString(0xF);
        }
        return guid;
    }

    obj.getWindowWidth = function() {
        var w = window,
        d = document,
        e = d.documentElement,
        g = d.getElementsByTagName('body')[0],
        x = w.innerWidth || e.clientWidth || g.clientWidth;
        return x;
    }

    obj.getWindowHeight = function() {
        var w = window,
        d = document,
        e = d.documentElement,
        g = d.getElementsByTagName('body')[0],
        y = w.innerHeight|| e.clientHeight|| g.clientHeight;
        return  y;
    }

    obj.getPosition = function(e) {
        if (e.changedTouches && e.changedTouches[0]) {
            var x = e.changedTouches[0].pageX;
            var y = e.changedTouches[0].pageY;
        } else {
            var x = (window.Event) ? e.pageX : e.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);
            var y = (window.Event) ? e.pageY : e.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
        }

        return [ x, y ];
    }

    obj.click = function(el) {
        if (el.click) {
            el.click();
        } else {
            var evt = new MouseEvent('click', {
                bubbles: true,
                cancelable: true,
                view: window
            });
            el.dispatchEvent(evt);
        }
    }

    obj.getElement = function(element, className) {
        var foundElement = false;

        function path (element) {
            if (element.className) {
                if (element.classList.contains(className)) {
                    foundElement = element;
                }
            }

            if (element.parentNode) {
                path(element.parentNode);
            }
        }

        path(element);

        return foundElement;
    }

    obj.getLinkElement = function(element) {
        var targetElement = false;

        function path (element) {
            if ((element.tagName == 'A' || element.tagName == 'DIV') && element.getAttribute('data-href')) {
                targetElement = element;
            }

            if (element.parentNode) {
                path(element.parentNode);
            }
        }

        path(element);

        return targetElement;
    }

    obj.getFormElements = function(formObject) {
        var ret = {};

        if (formObject) {
            var elements = formObject.querySelectorAll("input, select, textarea");
        } else {
            var elements = document.querySelectorAll("input, select, textarea");
        }

        for (var i = 0; i < elements.length; i++) {
            var element = elements[i];
            var name = element.name;
            var value = element.value;

            if (name) {
                ret[name] = value;
            }
        }

        return ret;
    }

    obj.exists = function(url, __callback) {
        var http = new XMLHttpRequest();
        http.open('HEAD', url, false);
        http.send();
        if (http.status) {
            __callback(http.status);
        }
    }

    obj.getFiles = function(element) {
        if (! element) {
            console.error('No element defined in the arguments of your method');
        }

        // Get attachments
        var files = element.querySelectorAll('.jfile');

        if (files.length > 0) {
            var data = [];
            for (var i = 0; i < files.length; i++) {
                var file = {};

                var src = files[i].getAttribute('src');

                if (files[i].classList.contains('jremove')) {
                    file.remove = 1;
                } else {
                    if (src.substr(0,4) == 'data') {
                        file.content = src.substr(src.indexOf(',') + 1);
                        file.extension = files[i].getAttribute('data-extension');
                    } else {
                        file.file = src;
                        file.extension = files[i].getAttribute('data-extension');
                        if (! file.extension) {
                            file.extension =  src.substr(src.lastIndexOf('.') + 1);
                        }
                        if (jSuites.files[file.file]) {
                            file.content = jSuites.files[file.file];
                        }
                    }

                    // Optional file information
                    if (files[i].getAttribute('data-name')) {
                        file.name = files[i].getAttribute('data-name');
                    }

                    if (files[i].getAttribute('data-file')) {
                        file.file = files[i].getAttribute('data-file');
                    }

                    if (files[i].getAttribute('data-size')) {
                        file.size = files[i].getAttribute('data-size');
                    }

                    if (files[i].getAttribute('data-date')) {
                        file.date = files[i].getAttribute('data-date');
                    }

                    if (files[i].getAttribute('data-cover')) {
                        file.cover = files[i].getAttribute('data-cover');
                    }
                }

                // TODO SMALL thumbs?

                data[i] = file;
            }

            return data;
        }
    }

    obj.ajax = function(options) {
        if (! options.data) {
            options.data = {};
        }

        if (options.type) {
            options.method = options.type;
        }

        if (options.data) {
            var data = [];
            var keys = Object.keys(options.data);

            if (keys.length) {
                for (var i = 0; i < keys.length; i++) {
                    if (typeof(options.data[keys[i]]) == 'object') {
                        var o = options.data[keys[i]];
                        for (var j = 0; j < o.length; j++) {
                            if (typeof(o[j]) == 'string') {
                                data.push(keys[i] + '[' + j + ']=' + encodeURIComponent(o[j]));
                            } else {
                                var prop = Object.keys(o[j]);
                                for (var z = 0; z < prop.length; z++) {
                                    data.push(keys[i] + '[' + j + '][' + prop[z] + ']=' + encodeURIComponent(o[j][prop[z]]));
                                }
                            }
                        }
                    } else {
                        data.push(keys[i] + '=' + encodeURIComponent(options.data[keys[i]]));
                    }
                }
            }

            if (options.method == 'GET' && data.length > 0) {
                if (options.url.indexOf('?') < 0) {
                    options.url += '?';
                }
                options.url += data.join('&');
            }
        }

        var httpRequest = new XMLHttpRequest();
        httpRequest.open(options.method, options.url, true);
        httpRequest.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

        if (options.method == 'POST') {
            httpRequest.setRequestHeader('Accept', 'application/json');
            httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        } else {
            if (options.dataType == 'json') {
                httpRequest.setRequestHeader('Content-Type', 'text/json');
            }
        }

        // No cache
        if (options.cache != true) {
            httpRequest.setRequestHeader('pragma', 'no-cache');
            httpRequest.setRequestHeader('cache-control', 'no-cache');
        }

        // Authentication
        if (options.withCredentials == true) {
            httpRequest.withCredentials = true
        }

        // Before send
        if (typeof(options.beforeSend) == 'function') {
            options.beforeSend(httpRequest);
        }

        httpRequest.onload = function() {
            if (httpRequest.status === 200) {
                if (options.dataType == 'json') {
                    try {
                        var result = JSON.parse(httpRequest.responseText);

                        if (options.success && typeof(options.success) == 'function') {
                            options.success(result);
                        }
                    } catch(err) {
                        if (options.error && typeof(options.error) == 'function') {
                            options.error(result);
                        }
                    }
                } else {
                    var result = httpRequest.responseText;

                    if (options.success && typeof(options.success) == 'function') {
                        options.success(result);
                    }
                }
            } else {
                if (options.error && typeof(options.error) == 'function') {
                    options.error(httpRequest.responseText);
                }
            }

            // Global complete method
            if (obj.ajax.requests && obj.ajax.requests.length) {
                // Get index of this request in the container
                var index = obj.ajax.requests.indexOf(httpRequest)
                // Remove from the ajax requests container
                obj.ajax.requests.splice(index, 1);
                // Last one?
                if (! obj.ajax.requests.length) {
                    if (options.complete && typeof(options.complete) == 'function') {
                        options.complete(result);
                    }
                }
            }
        }

        if (data) {
            httpRequest.send(data.join('&'));
        } else {
            httpRequest.send();
        }

        obj.ajax.requests.push(httpRequest);

        return httpRequest;
    }

    obj.ajax.requests = [];

    obj.slideLeft = function(element, direction, done) {
        if (direction == true) {
            element.classList.add('slide-left-in');
            setTimeout(function() {
                element.classList.remove('slide-left-in');
                if (typeof(done) == 'function') {
                    done();
                }
            }, 400);
        } else {
            element.classList.add('slide-left-out');
            setTimeout(function() {
                element.classList.remove('slide-left-out');
                if (typeof(done) == 'function') {
                    done();
                }
            }, 400);
        }
    }

    obj.slideRight = function(element, direction, done) {
        if (direction == true) {
            element.classList.add('slide-right-in');
            setTimeout(function() {
                element.classList.remove('slide-right-in');
                if (typeof(done) == 'function') {
                    done();
                }
            }, 400);
        } else {
            element.classList.add('slide-right-out');
            setTimeout(function() {
                element.classList.remove('slide-right-out');
                if (typeof(done) == 'function') {
                    done();
                }
            }, 400);
        }
    }

    obj.slideTop = function(element, direction, done) {
        if (direction == true) {
            element.classList.add('slide-top-in');
            setTimeout(function() {
                element.classList.remove('slide-top-in');
                if (typeof(done) == 'function') {
                    done();
                }
            }, 400);
        } else {
            element.classList.add('slide-top-out');
            setTimeout(function() {
                element.classList.remove('slide-top-out');
                if (typeof(done) == 'function') {
                    done();
                }
            }, 400);
        }
    }

    obj.slideBottom = function(element, direction, done) {
        if (direction == true) {
            element.classList.add('slide-bottom-in');
            setTimeout(function() {
                element.classList.remove('slide-bottom-in');
                if (typeof(done) == 'function') {
                    done();
                }
            }, 400);
        } else {
            element.classList.add('slide-bottom-out');
            setTimeout(function() {
                element.classList.remove('slide-bottom-out');
                if (typeof(done) == 'function') {
                    done();
                }
            }, 100);
        }
    }

    obj.fadeIn = function(element, done) {
        element.classList.add('fade-in');
        setTimeout(function() {
            element.classList.remove('fade-in');
            if (typeof(done) == 'function') {
                done();
            }
        }, 2000);
    }

    obj.fadeOut = function(element, done) {
        element.classList.add('fade-out');
        setTimeout(function() {
            element.classList.remove('fade-out');
            if (typeof(done) == 'function') {
                done();
            }
        }, 1000);
    }

    obj.keyDownControls = function(e) {
        if (e.which == 27) {
            var nodes = document.querySelectorAll('.jslider');
            if (nodes.length > 0) {
                for (var i = 0; i < nodes.length; i++) {
                    nodes[i].slider.close();
                }
            }

            if (document.querySelector('.jdialog')) {
                jSuites.dialog.close();
            }
        } else if (e.which == 13) {
            if (document.querySelector('.jdialog')) {
                if (typeof(jSuites.dialog.options.onconfirm) == 'function') {
                    jSuites.dialog.options.onconfirm();
                }
                jSuites.dialog.close();
            }
        }

        // Verify mask
        if (jSuites.mask) {
            jSuites.mask.apply(e);
        }
    }

    var actionUpControl = function(e) {
        var element = null;
        if (element = jSuites.getLinkElement(e.target)) {
            var link = element.getAttribute('data-href');
            if (link == '#back') {
                window.history.back();
            } else if (link == '#panel') {
                jSuites.panel();
            } else {
                jSuites.pages(link);
            }
        }
    }

    var controlSwipeLeft = function(e) {
        var element = jSuites.getElement(e.target, 'option');

        if (element && element.querySelector('.option-actions')) {
            element.scrollTo({
                left: 100,
                behavior: 'smooth'
            });
        } else {
            var element = jSuites.getElement(e.target, 'jcalendar');
            if (element && jSuites.calendar.current) {
                jSuites.calendar.current.prev();
            } else {
                if (jSuites.panel) {
                    var element = jSuites.panel.get();
                    if (element) {
                        if (element.style.display != 'none') {
                            jSuites.panel.close();
                        }
                    }
                }
            }
        }
    }

    var controlSwipeRight = function(e) {
        var element = jSuites.getElement(e.target, 'option');
        if (element && element.querySelector('.option-actions')) {
            element.scrollTo({
                left: 0,
                behavior: 'smooth'
            });
        } else {
            var element = jSuites.getElement(e.target, 'jcalendar');
            if (element && jSuites.calendar.current) {
                jSuites.calendar.current.next();
            } else {
                if (jSuites.panel) {
                    var element = jSuites.panel.get();
                    if (element) {
                        if (element.style.display == 'none') {
                            jSuites.panel();
                        }
                    }
                }
            }
        }
    }

    var actionOverControl = function(e) {
        // Tooltip
        if (jSuites.tooltip) {
            jSuites.tooltip(e);
        }
    }

    var actionOutControl = function(e) {
        // Tooltip
        if (jSuites.tooltip) {
            jSuites.tooltip.hide();
        }
    }

    // Create page container
    document.addEventListener('swipeleft', controlSwipeLeft);
    document.addEventListener('swiperight', controlSwipeRight);
    document.addEventListener('keydown', obj.keyDownControls);

    if ('ontouchend' in document.documentElement === true) {
        document.addEventListener('touchend', actionUpControl);
    } else {
        document.addEventListener('mouseup', actionUpControl);
    }

    // Onmouseover
    document.addEventListener('mouseover', actionOverControl);
    document.addEventListener('mouseout', actionOutControl);
    document.addEventListener('DOMContentLoaded', function() {
        obj.init();
    });

    // Pop state control
    window.onpopstate = function(e) {
        if (e.state && e.state.route) {
            if (jSuites.pages.get(e.state.route)) {
                jSuites.pages(e.state.route, { ignoreHistory:true });
            }
        }
    }

    return obj;
}();

jSuites.files = [];

jSuites.calendar = (function(el, options) {
    var obj = {};
    obj.options = {};

    // Global container
    if (! jSuites.calendar.current) {
        jSuites.calendar.current = null;
    }

    // Default configuration
    var defaults = {
        // Data
        data: null,
        // Inline or not
        type: null,
        // Restrictions
        validRange: null,
        // Starting weekday - 0 for sunday, 6 for saturday
        startingDay: null, 
        // Date format
        format: 'DD/MM/YYYY',
        // Allow keyboard date entry
        readonly: true,
        // Today is default
        today: false,
        // Show timepicker
        time: false,
        // Show the reset button
        resetButton: true,
        // Placeholder
        placeholder: '',
        // Translations can be done here
        months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
        weekdays: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
        weekdays_short: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
        // Value
        value: null,
        // Events
        onclose: null,
        onchange: null,
        // Fullscreen (this is automatic set for screensize < 800)
        fullscreen: false,
        // Internal mode controller
        mode: null,
        position: null,
        // Create the calendar closed as default
        opened: false,
    };

    // Loop through our object
    for (var property in defaults) {
        if (options && options.hasOwnProperty(property)) {
            obj.options[property] = options[property];
        } else {
            obj.options[property] = defaults[property];
        }
    }

    // Value
    if (! obj.options.value) {
        if (el.tagName == 'INPUT' && el.value) {
            obj.options.value = el.value;
        }
    }

    // Make sure use upper case in the format
    obj.options.format = obj.options.format.toUpperCase();

    if (obj.options.value) {
        var date = obj.options.value.split(' ');
        var time = date[1];
        var date = date[0].split('-');
        var y = parseInt(date[0]);
        var m = parseInt(date[1]);
        var d = parseInt(date[2]);

        if (time) {
            var time = time.split(':');
            var h = parseInt(time[0]);
            var i = parseInt(time[1]);
        } else {
            var h = 0;
            var i = 0;
        }
    } else {
        var date = new Date();
        var y = date.getFullYear();
        var m = date.getMonth() + 1;
        var d = date.getDate();
        var h = date.getHours();
        var i = date.getMinutes();
    }

    // Current value
    obj.date = [ y, m, d, h, i, 0 ];

    // Two digits
    var two = function(value) {
        value = '' + value;
        if (value.length == 1) {
            value = '0' + value;
        }
        return value;
    }

    // Calendar elements
    var calendarReset = document.createElement('div');
    calendarReset.className = 'jcalendar-reset';
    calendarReset.innerHTML = 'Reset';

    var calendarConfirm = document.createElement('div');
    calendarConfirm.className = 'jcalendar-confirm';
    calendarConfirm.innerHTML = 'Done';

    var calendarControls = document.createElement('div');
    calendarControls.className = 'jcalendar-controls'
    if (obj.options.resetButton) {
        calendarControls.appendChild(calendarReset);
    }
    calendarControls.appendChild(calendarConfirm);

    var calendarContainer = document.createElement('div');
    calendarContainer.className = 'jcalendar-container';

    var calendarContent = document.createElement('div');
    calendarContent.className = 'jcalendar-content';
    calendarContent.appendChild(calendarControls);
    calendarContainer.appendChild(calendarContent);

    // Table container
    var calendarTableContainer = document.createElement('div');
    calendarTableContainer.className = 'jcalendar-table';
    calendarContent.appendChild(calendarTableContainer);

    // Main element
    if (el.tagName == 'INPUT') {
        var calendar = document.createElement('div');
    } else {
        var calendar = el;
    }
    calendar.className = 'jcalendar';
    calendar.appendChild(calendarContainer);

    // Previous button
    var calendarHeaderPrev = document.createElement('td');
    calendarHeaderPrev.setAttribute('colspan', '2');
    calendarHeaderPrev.className = 'jcalendar-prev';

    // Header with year and month
    var calendarLabelYear = document.createElement('span');
    calendarLabelYear.className = 'jcalendar-year';

    var calendarLabelMonth = document.createElement('span');
    calendarLabelMonth.className = 'jcalendar-month';

    var calendarHeaderTitle = document.createElement('td');
    calendarHeaderTitle.className = 'jcalendar-header';
    calendarHeaderTitle.setAttribute('colspan', '3');
    calendarHeaderTitle.appendChild(calendarLabelMonth);
    calendarHeaderTitle.appendChild(calendarLabelYear);

    var calendarHeaderNext = document.createElement('td');
    calendarHeaderNext.setAttribute('colspan', '2');
    calendarHeaderNext.className = 'jcalendar-next';

    var calendarHeaderRow = document.createElement('tr');
    calendarHeaderRow.appendChild(calendarHeaderPrev);
    calendarHeaderRow.appendChild(calendarHeaderTitle);
    calendarHeaderRow.appendChild(calendarHeaderNext);

    var calendarHeader = document.createElement('thead');
    calendarHeader.appendChild(calendarHeaderRow);

    var calendarBody = document.createElement('tbody');
    var calendarFooter = document.createElement('tfoot');

    // Calendar table
    var calendarTable = document.createElement('table');
    calendarTable.setAttribute('cellpadding', '0');
    calendarTable.setAttribute('cellspacing', '0');
    calendarTable.appendChild(calendarHeader);
    calendarTable.appendChild(calendarBody);
    calendarTable.appendChild(calendarFooter);
    calendarTableContainer.appendChild(calendarTable);

    var calendarSelectHour = document.createElement('select');
    calendarSelectHour.className = 'jcalendar-select';
    calendarSelectHour.onchange = function() {
        obj.date[3] = this.value; 
    }

    for (var i = 0; i < 24; i++) {
        var element = document.createElement('option');
        element.value = i;
        element.innerHTML = two(i);
        calendarSelectHour.appendChild(element);
    }

    var calendarSelectMin = document.createElement('select');
    calendarSelectMin.className = 'jcalendar-select';
    calendarSelectMin.onchange = function() {
        obj.date[4] = this.value; 
    }

    for (var i = 0; i < 60; i++) {
        var element = document.createElement('option');
        element.value = i;
        element.innerHTML = two(i);
        calendarSelectMin.appendChild(element);
    }

    // Footer controls
    var calendarControlsFooter = document.createElement('div');
    calendarControlsFooter.className = 'jcalendar-controls';

    var calendarControlsTime = document.createElement('div');
    calendarControlsTime.className = 'jcalendar-time';
    calendarControlsTime.style.maxWidth = '140px';
    calendarControlsTime.appendChild(calendarSelectHour);
    calendarControlsTime.appendChild(calendarSelectMin);

    var calendarControlsUpdateButton = document.createElement('input');
    calendarControlsUpdateButton.setAttribute('type', 'button');
    calendarControlsUpdateButton.className = 'jcalendar-update';
    calendarControlsUpdateButton.value = 'Update';

    var calendarControlsUpdate = document.createElement('div');
    calendarControlsUpdate.style.flexGrow = '10';
    calendarControlsUpdate.appendChild(calendarControlsUpdateButton);
    calendarControlsFooter.appendChild(calendarControlsTime);
    calendarControlsFooter.appendChild(calendarControlsUpdate);
    calendarContent.appendChild(calendarControlsFooter);

    var calendarBackdrop = document.createElement('div');
    calendarBackdrop.className = 'jcalendar-backdrop';
    calendar.appendChild(calendarBackdrop);

    // Update actions button
    var updateActions = function() {
        var currentDay = calendar.querySelector('.jcalendar-selected');

        if (currentDay && currentDay.classList.contains('jcalendar-disabled')) {
            calendarControlsUpdateButton.setAttribute('disabled', 'disabled');
            calendarSelectHour.setAttribute('disabled', 'disabled');
            calendarSelectMin.setAttribute('disabled', 'disabled');
        } else {
            calendarControlsUpdateButton.removeAttribute('disabled');
            calendarSelectHour.removeAttribute('disabled');
            calendarSelectMin.removeAttribute('disabled');
        }
    }

    // Methods
    obj.open = function (value) {
        if (! calendar.classList.contains('jcalendar-focus')) {
            if (jSuites.calendar.current) {
                jSuites.calendar.current.close();
            }
            // Current
            jSuites.calendar.current = obj;
            // Show calendar
            calendar.classList.add('jcalendar-focus');
            // Get days
            obj.getDays();
            // Hour
            if (obj.options.time) {
                calendarSelectHour.value = obj.date[3];
                calendarSelectMin.value = obj.date[4];
            }

            // Get the position of the corner helper
            if (jSuites.getWindowWidth() < 800 || obj.options.fullscreen) {
                // Full
                calendar.classList.add('jcalendar-fullsize');
                // Animation
                jSuites.slideBottom(calendarContent, 1);
            } else {
                const rect = el.getBoundingClientRect();
                const rectContent = calendarContent.getBoundingClientRect();

                if (obj.options.position) {
                    calendarContainer.style.position = 'fixed';
                    if (window.innerHeight < rect.bottom + rectContent.height) {
                        calendarContainer.style.top = (rect.top - (rectContent.height + 2)) + 'px';
                    } else {
                        calendarContainer.style.top = (rect.top + rect.height + 2) + 'px';
                    }
                    calendarContainer.style.left = rect.left + 'px';
                } else {
                    if (window.innerHeight < rect.bottom + rectContent.height) {
                        calendarContainer.style.bottom = (1 * rect.height + rectContent.height + 2) + 'px';
                    } else {
                        calendarContainer.style.top = 2 + 'px'; 
                    }
                }
            }
        }
    }

    obj.close = function (ignoreEvents, update) {
        if (jSuites.calendar.current) {
            // Current
            jSuites.calendar.current = null;

            if (update !== false) {
                var element = calendar.querySelector('.jcalendar-selected');

                if (typeof(update) == 'string') {
                    var value = update;
                } else if (element && element.classList.contains('jcalendar-disabled')) {
                    var value = obj.options.value
                } else {
                    var value = obj.getValue();
                }

                obj.setValue(value);
            }

            // Events
            if (! ignoreEvents && typeof(obj.options.onclose) == 'function') {
                obj.options.onclose(el);
            }

            // Hide
            calendar.classList.remove('jcalendar-focus');
        }

        return obj.options.value;
    }

    obj.prev = function() {
        // Check if the visualization is the days picker or years picker
        if (obj.options.mode == 'years') {
            obj.date[0] = obj.date[0] - 12;

            // Update picker table of days
            obj.getYears();
        } else {
            // Go to the previous month
            if (obj.date[1] < 2) {
                obj.date[0] = obj.date[0] - 1;
                obj.date[1] = 12;
            } else {
                obj.date[1] = obj.date[1] - 1;
            }

            // Update picker table of days
            obj.getDays();
        }
    }

    obj.next = function() {
        // Check if the visualization is the days picker or years picker
        if (obj.options.mode == 'years') {
            obj.date[0] = parseInt(obj.date[0]) + 12;

            // Update picker table of days
            obj.getYears();
        } else {
            // Go to the previous month
            if (obj.date[1] > 11) {
                obj.date[0] = parseInt(obj.date[0]) + 1;
                obj.date[1] = 1;
            } else {
                obj.date[1] = parseInt(obj.date[1]) + 1;
            }

            // Update picker table of days
            obj.getDays();
        }
    }

    obj.setValue = function(val) {
        if (! val) {
            val = '' + val;
        }
        // Values
        var newValue = val;
        var oldValue = obj.options.value;
        // Set label
        var value = obj.setLabel(newValue, obj.options.format);
        var date = newValue.split(' ');
        if (! date[1]) {
            date[1] = '00:00:00';
        }
        var time = date[1].split(':')
        var date = date[0].split('-');
        var y = parseInt(date[0]);
        var m = parseInt(date[1]);
        var d = parseInt(date[2]);
        var h = parseInt(time[0]);
        var i = parseInt(time[1]);
        obj.date = [ y, m, d, h, i, 0 ];
        var val = obj.setLabel(newValue, obj.options.format);

        if (oldValue != newValue) {
            // Input value
            if (el.tagName == 'INPUT') {
                el.value = val;
            }
            // New value
            obj.options.value = newValue;
            // On change
            if (typeof(obj.options.onchange) ==  'function') {
                obj.options.onchange(el, newValue, oldValue);
            }
        }

        obj.getDays();
    }

    obj.getValue = function() {
        if (obj.date) {
            if (obj.options.time) {
                return two(obj.date[0]) + '-' + two(obj.date[1]) + '-' + two(obj.date[2]) + ' ' + two(obj.date[3]) + ':' + two(obj.date[4]) + ':' + two(0);
            } else {
                return two(obj.date[0]) + '-' + two(obj.date[1]) + '-' + two(obj.date[2]) + ' ' + two(0) + ':' + two(0) + ':' + two(0);
            }
        } else {
            return "";
        }
    }

    /**
     *  Calendar
     */
    obj.update = function(element) {
        if (element.classList.contains('jcalendar-disabled')) {
            // Do nothing
        } else {
            obj.date[2] = element.innerText;

            if (! obj.options.time) {
                obj.close();
            } else {
                obj.date[3] = calendarSelectHour.value;
                obj.date[4] = calendarSelectMin.value;
            }

            var elements = calendar.querySelector('.jcalendar-selected');
            if (elements) {
                elements.classList.remove('jcalendar-selected');
            }
            element.classList.add('jcalendar-selected');
        }

        // Update
        updateActions();
    }

    /**
     * Set to blank
     */
    obj.reset = function() {
        // Close calendar
        obj.close(false, '');
    }

    /**
     * Get calendar days
     */
    obj.getDays = function() {
        // Mode
        obj.options.mode = 'days';

        // Setting current values in case of NULLs
        var date = new Date();

        // Current selection
        var year = obj.date && obj.date[0] ? obj.date[0] : parseInt(date.getFullYear());
        var month = obj.date && obj.date[1] ? obj.date[1] : parseInt(date.getMonth()) + 1;
        var day = obj.date && obj.date[2] ? obj.date[2] : parseInt(date.getDay());
        var hour = obj.date && obj.date[3] ? obj.date[3] : parseInt(date.getHours());
        var min = obj.date && obj.date[4] ? obj.date[4] : parseInt(date.getMinutes());

        // Selection container
        obj.date = [year, month, day, hour, min, 0 ];

        // Update title
        calendarLabelYear.innerHTML = year;
        calendarLabelMonth.innerHTML = obj.options.months[month - 1];

        // Current month and Year
        var isCurrentMonthAndYear = (date.getMonth() == month - 1) && (date.getFullYear() == year) ? true : false;
        var currentDay = date.getDate();

        // Number of days in the month
        var date = new Date(year, month, 0, 0, 0);
        var numberOfDays = date.getDate();

        // First day
        var date = new Date(year, month-1, 0, 0, 0);
        var firstDay = date.getDay() + 1;

        // Index value
        var index = obj.options.startingDay || 0;

        // First of day relative to the starting calendar weekday
        firstDay = firstDay - index;

        // Reset table
        calendarBody.innerHTML = '';

        // Weekdays Row
        var row = document.createElement('tr');
        row.setAttribute('align', 'center');
        calendarBody.appendChild(row);

        // Create weekdays row
        for (var i = 0; i < 7; i++) {
            var cell = document.createElement('td');
            cell.classList.add('jcalendar-weekday')
            cell.innerHTML = obj.options.weekdays_short[index];
            row.appendChild(cell);
            // Next week day
            index++;
            // Restart index
            if (index > 6) {
                index = 0;
            }
        }

        // Index of days
        var index = 0;
        var d = 0;
 
        // Calendar table
        for (var j = 0; j < 5; j++) {
            // Reset cells container
            var row = document.createElement('tr');
            row.setAttribute('align', 'center');
            // Data control
            var emptyRow = true;
            // Create cells
            for (var i = 0; i < 7; i++) {
                // Create cell
                var cell = document.createElement('td');
                cell.classList.add('jcalendar-set-day');

                if (index >= firstDay && index < (firstDay + numberOfDays)) {
                    // Day cell
                    d++;
                    cell.innerHTML = d;

                    // Selected
                    if (d == day) {
                        cell.classList.add('jcalendar-selected');
                    }

                    // Current selection day is today
                    if (isCurrentMonthAndYear && currentDay == d) {
                        cell.style.fontWeight = 'bold';
                    }

                    // Current selection day
                    var current = jSuites.calendar.now(new Date(year, month-1, d), true);

                    // Available ranges
                    if (obj.options.validRange) {
                        if (! obj.options.validRange[0] || current >= obj.options.validRange[0]) {
                            var test1 = true;
                        } else {
                            var test1 = false;
                        }

                        if (! obj.options.validRange[1] || current <= obj.options.validRange[1]) {
                            var test2 = true;
                        } else {
                            var test2 = false;
                        }

                        if (! (test1 && test2)) {
                            cell.classList.add('jcalendar-disabled');
                        }
                    }

                    // Control
                    emptyRow = false;
                }
                // Day cell
                row.appendChild(cell);
                // Index
                index++;
            }

            // Add cell to the calendar body
            if (emptyRow == false) {
                calendarBody.appendChild(row);
            }
        }

        // Show time controls
        if (obj.options.time) {
            calendarControlsTime.style.display = '';
        } else {
            calendarControlsTime.style.display = 'none';
        }

        // Update
        updateActions();
    }

    obj.getMonths = function() {
        // Mode
        obj.options.mode = 'months';

        // Loading month labels
        var months = obj.options.months;

        // Update title
        calendarLabelYear.innerHTML = obj.date[0];
        calendarLabelMonth.innerHTML = '';

        // Create months table
        var html = '<td colspan="7"><table width="100%"><tr align="center">';

        for (i = 0; i < 12; i++) {
            if ((i > 0) && (!(i % 4))) {
                html += '</tr><tr align="center">';
            }

            var month = parseInt(i) + 1;
            html += '<td class="jcalendar-set-month" data-value="' + month + '">' + months[i] +'</td>';
        }

        html += '</tr></table></td>';

        calendarBody.innerHTML = html;
    }

    obj.getYears = function() { 
        // Mode
        obj.options.mode = 'years';

        // Array of years
        var y = [];
        for (i = 0; i < 25; i++) {
            y[i] = parseInt(obj.date[0]) + (i - 12);
        }

        // Assembling the year tables
        var html = '<td colspan="7"><table width="100%"><tr align="center">';

        for (i = 0; i < 25; i++) {
            if ((i > 0) && (!(i % 5))) {
                html += '</tr><tr align="center">';
            }
            html += '<td class="jcalendar-set-year">'+ y[i] +'</td>';
        }

        html += '</tr></table></td>';

        calendarBody.innerHTML = html;
    }

    obj.setLabel = function(value, format) {
        return jSuites.calendar.getDateString(value, format);
    }

    obj.fromFormatted = function (value, format) {
        return jSuites.calendar.extractDateFromString(value, format);
    }

    var mouseUpControls = function(e) {
        var action = e.target.className;

        // Object id
        if (action == 'jcalendar-prev') {
            obj.prev();
            e.stopPropagation();
            e.preventDefault();
        } else if (action == 'jcalendar-next') {
            obj.next();
            e.stopPropagation();
            e.preventDefault();
        } else if (action == 'jcalendar-month') {
            obj.getMonths();
            e.stopPropagation();
            e.preventDefault();
        } else if (action == 'jcalendar-year') {
            obj.getYears();
            e.stopPropagation();
            e.preventDefault();
        } else if (action == 'jcalendar-set-year') {
            obj.date[0] = e.target.innerText;
            obj.getDays();
            e.stopPropagation();
            e.preventDefault();
        } else if (action == 'jcalendar-set-month') {
            obj.date[1] = parseInt(e.target.getAttribute('data-value'));
            obj.getDays();
            e.stopPropagation();
            e.preventDefault();
        } else if (action == 'jcalendar-confirm' || action == 'jcalendar-update') {
            obj.close();
            e.stopPropagation();
            e.preventDefault();
        } else if (action == 'jcalendar-close') {
            obj.close();
            e.stopPropagation();
            e.preventDefault();
        } else if (action == 'jcalendar-backdrop') {
            obj.close(false, false);
            e.stopPropagation();
            e.preventDefault();
        } else if (action == 'jcalendar-reset') {
            obj.reset();
            e.stopPropagation();
            e.preventDefault();
        } else if (e.target.classList.contains('jcalendar-set-day')) {
            if (e.target.innerText) {
                obj.update(e.target);
                e.stopPropagation();
                e.preventDefault();
            }
        }
    }

    var keyUpControls = function(e) {
        if (e.target.value && e.target.value.length > 3) {
            var test = jSuites.calendar.extractDateFromString(e.target.value, obj.options.format);
            if (test) {
                if (e.target.getAttribute('data-completed') == 'true') {
                    obj.setValue(test);
                }
            }
        }
    }

    // Handle events
    el.addEventListener("keyup", keyUpControls);

    // Add global events
    calendar.addEventListener("swipeleft", function(e) {
        jSuites.slideLeft(calendarTable, 0, function() {
            obj.next();
            jSuites.slideRight(calendarTable, 1);
        });
        e.preventDefault();
        e.stopPropagation();
    });

    calendar.addEventListener("swiperight", function(e) {
        jSuites.slideRight(calendarTable, 0, function() {
            obj.prev();
            jSuites.slideLeft(calendarTable, 1);
        });
        e.preventDefault();
        e.stopPropagation();
    });

    if ('ontouchend' in document.documentElement === true) {
        calendar.addEventListener("touchend", mouseUpControls);

        el.addEventListener("touchend", function(e) {
            obj.open();
        });
    } else {
        calendar.addEventListener("mouseup", mouseUpControls);

        el.addEventListener("mouseup", function(e) {
            obj.open();
        });
    }

    // Append element to the DOM
    if (el.tagName == 'INPUT') {
        el.parentNode.insertBefore(calendar, el.nextSibling);
        // Add properties
        el.setAttribute('autocomplete', 'off');
        el.setAttribute('data-mask', obj.options.format.toLowerCase());

        if (obj.options.readonly) {
            el.setAttribute('readonly', 'readonly');
        }
        if (obj.options.placeholder) {
            el.setAttribute('placeholder', obj.options.placeholder);
        }
        // Element
        el.classList.add('jcalendar-input');
        // Value
        el.value = obj.setLabel(obj.getValue(), obj.options.format);
    }

    // Keep object available from the node
    el.calendar = obj;

    if (obj.options.opened == true) {
        obj.open();
    }

    return obj;
});

jSuites.calendar.prettify = function(d, texts) {
    if (! texts) {
        var texts = {
            justNow: 'Just now',
            xMinutesAgo: '{0}m ago',
            xHoursAgo: '{0}h ago',
            xDaysAgo: '{0}d ago',
            xWeeksAgo: '{0}w ago',
            xMonthsAgo: '{0} mon ago',
            xYearsAgo: '{0}y ago',
        }
    }

    var d1 = new Date();
    var d2 = new Date(d);
    var total = parseInt((d1 - d2) / 1000 / 60);

    String.prototype.format = function(o) {
        return this.replace('{0}', o);
    }

    if (total == 0) {
        var text = texts.justNow;
    } else if (total < 90) {
        var text = texts.xMinutesAgo.format(total);
    } else if (total < 1440) { // One day
        var text = texts.xHoursAgo.format(Math.round(total/60));
    } else if (total < 20160) { // 14 days
        var text = texts.xDaysAgo.format(Math.round(total / 1440));
    } else if (total < 43200) { // 30 days
        var text = texts.xWeeksAgo.format(Math.round(total / 10080));
    } else if (total < 1036800) { // 24 months
        var text = texts.xMonthsAgo.format(Math.round(total / 43200));
    } else { // 24 months+
        var text = texts.xYearsAgo.format(Math.round(total / 525600));
    }

    return text;
}

jSuites.calendar.prettifyAll = function() {
    var elements = document.querySelectorAll('.prettydate');
    for (var i = 0; i < elements.length; i++) {
        if (elements[i].getAttribute('data-date')) {
            elements[i].innerHTML = jSuites.calendar.prettify(elements[i].getAttribute('data-date'));
        } else {
            elements[i].setAttribute('data-date', elements[i].innerHTML);
            elements[i].innerHTML = jSuites.calendar.prettify(elements[i].innerHTML);
        }
    }
}

jSuites.calendar.now = function(date, dateOnly) {
    if (! date) {
        var date = new Date();
    }
    var y = date.getFullYear();
    var m = date.getMonth() + 1;
    var d = date.getDate();
    var h = date.getHours();
    var i = date.getMinutes();
    var s = date.getSeconds();

    // Two digits
    var two = function(value) {
        value = '' + value;
        if (value.length == 1) {
            value = '0' + value;
        }
        return value;
    }

    if (dateOnly == true) {
        return two(y) + '-' + two(m) + '-' + two(d);
    } else {
        return two(y) + '-' + two(m) + '-' + two(d) + ' ' + two(h) + ':' + two(i) + ':' + two(s);
    }
}

// Helper to extract date from a string
jSuites.calendar.extractDateFromString = function(date, format) {
    var v1 = '' + date;
    var v2 = format.replace(/[0-9]/g,'');

    var test = 1;

    // Get year
    var y = v2.search("YYYY");
    y = v1.substr(y,4);
    if (parseInt(y) != y) {
        test = 0;
    }

    // Get month
    var m = v2.search("MM");
    m = v1.substr(m,2);
    if (parseInt(m) != m || d > 12) {
        test = 0;
    }

    // Get day
    var d = v2.search("DD");
    d = v1.substr(d,2);
    if (parseInt(d) != d  || d > 31) {
        test = 0;
    }

    // Get hour
    var h = v2.search("HH");
    if (h >= 0) {
        h = v1.substr(h,2);
        if (! parseInt(h) || h > 23) {
            h = '00';
        }
    } else {
        h = '00';
    }
    
    // Get minutes
    var i = v2.search("MI");
    if (i >= 0) {
        i = v1.substr(i,2);
        if (! parseInt(i) || i > 59) {
            i = '00';
        }
    } else {
        i = '00';
    }

    // Get seconds
    var s = v2.search("SS");
    if (s >= 0) {
        s = v1.substr(s,2);
        if (! parseInt(s) || s > 59) {
            s = '00';
        }
    } else {
        s = '00';
    }

    if (test == 1 && date.length == v2.length) {
        // Update source
        var data = y + '-' + m + '-' + d + ' ' + h + ':' +  i + ':' + s;

        return data;
    }

    return '';
}

// Helper to convert date into string
jSuites.calendar.getDateString = function(value, format) {
    // Default calendar
    if (! format) {
        var format = 'DD/MM/YYYY';
    }

    if (value) {
        var d = ''+value;
        d = d.split(' ');

        var h = '';
        var m = '';
        var s = '';

        if (d[1]) {
            h = d[1].split(':');
            m = h[1] ? h[1] : '00';
            s = h[2] ? h[2] : '00';
            h = h[0] ? h[0] : '00';
        } else {
            h = '00';
            m = '00';
            s = '00';
        }

        d = d[0].split('-');

        if (d[0] && d[1] && d[2] && d[0] > 0 && d[1] > 0 && d[1] < 13 && d[2] > 0 && d[2] < 32) {
            var calendar = new Date(d[0], d[1]-1, d[2]);
            var weekday = new Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday');
            var months = new Array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');

            d[1] = (d[1].length < 2 ? '0' : '') + d[1];
            d[2] = (d[2].length < 2 ? '0' : '') + d[2];
            h = (h.length < 2 ? '0' : '') + h;
            m = (m.length < 2 ? '0' : '') + m;
            s = (s.length < 2 ? '0' : '') + s;

            value = format;
            value = value.replace('WD', weekday[calendar.getDay()]);
            value = value.replace('DD', d[2]);
            value = value.replace('MM', d[1]);
            value = value.replace('YYYY', d[0]);
            value = value.replace('YY', d[0].substring(2,4));
            value = value.replace('MON', months[parseInt(d[1])-1].toUpperCase());

            if (h) {
                value = value.replace('HH24', h);
            }

            if (h > 12) {
                value = value.replace('HH12', h - 12);
                value = value.replace('HH', h);
            } else {
                value = value.replace('HH12', h);
                value = value.replace('HH', h);
            }

            value = value.replace('MI', m);
            value = value.replace('MM', m);
            value = value.replace('SS', s);
        } else {
            value = '';
        }
    }

    return value;
}

jSuites.calendar.isOpen = function(e) {
    if (jSuites.calendar.current) {
        if (! e.target.className || e.target.className.indexOf('jcalendar') == -1) {
            jSuites.calendar.current.close(false, false);
        }
    }
}

if ('ontouchstart' in document.documentElement === true) {
    document.addEventListener("touchstart", jSuites.calendar.isOpen);
} else {
    document.addEventListener("mousedown", jSuites.calendar.isOpen);
}

jSuites.color = (function(el, options) {
    var obj = {};
    obj.options = {};
    obj.values = [];

    // Global container
    if (! jSuites.color.current) {
        jSuites.color.current = null;
    }

    /**
     * @typedef {Object} defaults
     * @property {(string|Array)} value - Initial value of the compontent
     * @property {string} placeholder - The default instruction text on the element
     * @property {requestCallback} onchange - Method to be execute after any changes on the element
     * @property {requestCallback} onclose - Method to be execute when the element is closed
     */
    var defaults = {
        placeholder: '',
        value: null,
        onclose: null,
        onchange: null,
        closeOnChange: true,
    };

    // Loop through our object
    for (var property in defaults) {
        if (options && options.hasOwnProperty(property)) {
            obj.options[property] = options[property];
        } else {
            obj.options[property] = defaults[property];
        }
    }

    var palette = {
          "red": {
            "50": "#ffebee",
            "100": "#ffcdd2",
            "200": "#ef9a9a",
            "300": "#e57373",
            "400": "#ef5350",
            "500": "#f44336",
            "600": "#e53935",
            "700": "#d32f2f",
            "800": "#c62828",
            "900": "#b71c1c",
          },
          "pink": {
            "50": "#fce4ec",
            "100": "#f8bbd0",
            "200": "#f48fb1",
            "300": "#f06292",
            "400": "#ec407a",
            "500": "#e91e63",
            "600": "#d81b60",
            "700": "#c2185b",
            "800": "#ad1457",
            "900": "#880e4f",
          },
          "purple": {
            "50": "#f3e5f5",
            "100": "#e1bee7",
            "200": "#ce93d8",
            "300": "#ba68c8",
            "400": "#ab47bc",
            "500": "#9c27b0",
            "600": "#8e24aa",
            "700": "#7b1fa2",
            "800": "#6a1b9a",
            "900": "#4a148c",
          },
          "indigo": {
            "50": "#e8eaf6",
            "100": "#c5cae9",
            "200": "#9fa8da",
            "300": "#7986cb",
            "400": "#5c6bc0",
            "500": "#3f51b5",
            "600": "#3949ab",
            "700": "#303f9f",
            "800": "#283593",
            "900": "#1a237e",
          },
          "blue": {
            "50": "#e3f2fd",
            "100": "#bbdefb",
            "200": "#90caf9",
            "300": "#64b5f6",
            "400": "#42a5f5",
            "500": "#2196f3",
            "600": "#1e88e5",
            "700": "#1976d2",
            "800": "#1565c0",
            "900": "#0d47a1",
          },
          "cyan": {
            "50": "#e0f7fa",
            "100": "#b2ebf2",
            "200": "#80deea",
            "300": "#4dd0e1",
            "400": "#26c6da",
            "500": "#00bcd4",
            "600": "#00acc1",
            "700": "#0097a7",
            "800": "#00838f",
            "900": "#006064",
          },
          "teal": {
            "50": "#e0f2f1",
            "100": "#b2dfdb",
            "200": "#80cbc4",
            "300": "#4db6ac",
            "400": "#26a69a",
            "500": "#009688",
            "600": "#00897b",
            "700": "#00796b",
            "800": "#00695c",
            "900": "#004d40",
          },
          "green": {
            "50": "#e8f5e9",
            "100": "#c8e6c9",
            "200": "#a5d6a7",
            "300": "#81c784",
            "400": "#66bb6a",
            "500": "#4caf50",
            "600": "#43a047",
            "700": "#388e3c",
            "800": "#2e7d32",
            "900": "#1b5e20",
          },
          "lightgreen": {
            "50": "#f1f8e9",
            "100": "#dcedc8",
            "200": "#c5e1a5",
            "300": "#aed581",
            "400": "#9ccc65",
            "500": "#8bc34a",
            "600": "#7cb342",
            "700": "#689f38",
            "800": "#558b2f",
            "900": "#33691e",
          },
          "lime": {
            "50": "#f9fbe7",
            "100": "#f0f4c3",
            "200": "#e6ee9c",
            "300": "#dce775",
            "400": "#d4e157",
            "500": "#cddc39",
            "600": "#c0ca33",
            "700": "#afb42b",
            "800": "#9e9d24",
            "900": "#827717",
          },
          "yellow": {
            "50": "#fffde7",
            "100": "#fff9c4",
            "200": "#fff59d",
            "300": "#fff176",
            "400": "#ffee58",
            "500": "#ffeb3b",
            "600": "#fdd835",
            "700": "#fbc02d",
            "800": "#f9a825",
            "900": "#f57f17",
          },
          "amber": {
            "50": "#fff8e1",
            "100": "#ffecb3",
            "200": "#ffe082",
            "300": "#ffd54f",
            "400": "#ffca28",
            "500": "#ffc107",
            "600": "#ffb300",
            "700": "#ffa000",
            "800": "#ff8f00",
            "900": "#ff6f00",
          },
          "orange": {
            "50": "#fff3e0",
            "100": "#ffe0b2",
            "200": "#ffcc80",
            "300": "#ffb74d",
            "400": "#ffa726",
            "500": "#ff9800",
            "600": "#fb8c00",
            "700": "#f57c00",
            "800": "#ef6c00",
            "900": "#e65100",
          },
          "deeporange": {
            "50": "#fbe9e7",
            "100": "#ffccbc",
            "200": "#ffab91",
            "300": "#ff8a65",
            "400": "#ff7043",
            "500": "#ff5722",
            "600": "#f4511e",
            "700": "#e64a19",
            "800": "#d84315",
            "900": "#bf360c",
          },
          "brown": {
            "50": "#efebe9",
            "100": "#d7ccc8",
            "200": "#bcaaa4",
            "300": "#a1887f",
            "400": "#8d6e63",
            "500": "#795548",
            "600": "#6d4c41",
            "700": "#5d4037",
            "800": "#4e342e",
            "900": "#3e2723"
          },
          "grey": {
            "50": "#fafafa",
            "100": "#f5f5f5",
            "200": "#eeeeee",
            "300": "#e0e0e0",
            "400": "#bdbdbd",
            "500": "#9e9e9e",
            "600": "#757575",
            "700": "#616161",
            "800": "#424242",
            "900": "#212121"
          },
          "bluegrey": {
            "50": "#eceff1",
            "100": "#cfd8dc",
            "200": "#b0bec5",
            "300": "#90a4ae",
            "400": "#78909c",
            "500": "#607d8b",
            "600": "#546e7a",
            "700": "#455a64",
            "800": "#37474f",
            "900": "#263238"
          }
    };

    var x = 0;
    var y = 0;
    var colors = [];

    var col = Object.keys(palette);
    var shade = Object.keys(palette[col[0]]);

    for (var i = 0; i < col.length; i++) {
        for (var j = 0; j < shade.length; j++) {
            if (! colors[j]) {
                colors[j] = [];
            }
            colors[j][i] = palette[col[i]][shade[j]];
        }
    };

    // Value
    if (obj.options.value) {
        el.value = obj.options.value;
    }

    // Table container
    var container = document.createElement('div');
    container.className = 'jcolor';

    // Table container
    var backdrop = document.createElement('div');
    backdrop.className = 'jcolor-backdrop';
    container.appendChild(backdrop);

    // Content
    var content = document.createElement('div');
    content.className = 'jcolor-content';

    // Close button
    var closeButton  = document.createElement('div');
    closeButton.className = 'jcolor-close';
    closeButton.innerHTML = 'Done';
    closeButton.onclick = function() {
        obj.close();
    }
    content.appendChild(closeButton);

    // Table pallete
    var table = document.createElement('table');
    table.setAttribute('cellpadding', '7');
    table.setAttribute('cellspacing', '0');

    for (var i = 0; i < colors.length; i++) {
        var tr = document.createElement('tr');
        for (var j = 0; j < colors[i].length; j++) {
            var td = document.createElement('td');
            td.style.backgroundColor = colors[i][j];
            td.setAttribute('data-value', colors[i][j]);
            td.innerHTML = '';
            tr.appendChild(td);

            // Selected color
            if (obj.options.value == colors[i][j]) {
                td.classList.add('jcolor-selected');
            }

            // Possible values
            obj.values[colors[i][j]] = td;
        }
        table.appendChild(tr);
    }

    /**
     * Open color pallete
     */
    obj.open = function() {
        if (jSuites.color.current) {
            if (jSuites.color.current != obj) {
                jSuites.color.current.close();
            }
        }

        if (! jSuites.color.current) {
            // Persist element
            jSuites.color.current = obj;
            // Show colorpicker
            container.classList.add('jcolor-focus');

            const rectContent = content.getBoundingClientRect();

            if (jSuites.getWindowWidth() < 800) {
                content.style.top = '';
                content.classList.add('jcolor-fullscreen');
                jSuites.slideBottom(content, 1);
                backdrop.style.display = 'block';
            } else {
                if (content.classList.contains('jcolor-fullscreen')) {
                    content.classList.remove('jcolor-fullscreen');
                    backdrop.style.display = '';
                }

                const rect = el.getBoundingClientRect();

                if (window.innerHeight < rect.bottom + rectContent.height) {
                    content.style.top = -1 * (rectContent.height + rect.height + 2) + 'px';
                } else {
                    content.style.top = '2px';
                }
            }

            container.focus();
        }
    }

    /**
     * Close color pallete
     */
    obj.close = function(ignoreEvents) {
        if (jSuites.color.current) {
            jSuites.color.current = null;
            if (! ignoreEvents && typeof(obj.options.onclose) == 'function') {
                obj.options.onclose(el);
            }
            container.classList.remove('jcolor-focus');
        }

        // Make sure backdrop is hidden
        backdrop.style.display = '';

        return obj.options.value;
    }

    /**
     * Set value
     */
    obj.setValue = function(color) {
        if (color) {
            el.value = color;
            obj.options.value = color;
        }

        // Remove current selecded mark
        var selected = container.querySelector('.jcolor-selected');
        if (selected) {
            selected.classList.remove('jcolor-selected');
        }

        // Mark cell as selected
        obj.values[color].classList.add('jcolor-selected');

        // Onchange
        if (typeof(obj.options.onchange) == 'function') {
            obj.options.onchange(el, color);
        }

        if (obj.options.closeOnChange == true) {
            obj.close();
        }
    }

    /**
     * Get value
     */
    obj.getValue = function() {
        return obj.options.value;
    }

    /**
     * If element is focus open the picker
     */
    el.addEventListener("focus", function(e) {
        obj.open();
    });

    el.addEventListener("mousedown", function(e) {
        if (! jSuites.color.current) {
            setTimeout(function() {
        obj.open();
                e.preventDefault();
            }, 200);
        }
    });

    // Select color
    container.addEventListener("mouseup", function(e) {
        if (e.target.tagName == 'TD') {
            jSuites.color.current.setValue(e.target.getAttribute('data-value'));

            if (jSuites.color.current) {
                jSuites.color.current.close();
            }
        }
    });

    // Close controller
    document.addEventListener("mousedown", function(e) {
        if (jSuites.color.current) {
            var element = jSuites.getElement(e.target, 'jcolor');
            if (! element) {
                jSuites.color.current.close();
            }
        }
    });

    // Possible to focus the container
    container.setAttribute('tabindex', '900');

    // Placeholder
    if (obj.options.placeholder) {
        el.setAttribute('placeholder', obj.options.placeholder);
    }

    // Append to the table
    content.appendChild(table);
    container.appendChild(content);

    // Insert picker after the element
    if (el.tagName == 'INPUT') {
        el.parentNode.insertBefore(container, el.nextSibling);
    } else {
        el.appendChild(container);
    }

    // Keep object available from the node
    el.color = obj;

    return obj;
});


jSuites.contextmenu = (function(el, options) {
    var obj = {};
    obj.options = {};

    // Default configuration
    var defaults = {
        items: null,
        onclick: null,
    };

    // Loop through our object
    for (var property in defaults) {
        if (options && options.hasOwnProperty(property)) {
            obj.options[property] = options[property];
        } else {
            obj.options[property] = defaults[property];
        }
    }

    // Class definition
    el.classList.add('jcontextmenu');
    // Focusable
    el.setAttribute('tabindex', '900');

    /**
     * Open contextmenu
     */
    obj.open = function(e, items) {
        if (items) {
            // Update content
            obj.options.items = items;
            // Create items
            obj.create(items);
        }
        // Coordinates
        if (e.target) {
            var x = e.clientX;
            var y = e.clientY;
        } else {
            var x = e.x;
            var y = e.y;
        }

        el.classList.add('jcontextmenu-focus');
        el.focus();

        const rect = el.getBoundingClientRect();

        if (window.innerHeight < y + rect.height) {
            el.style.top = (y - rect.height) + 'px';
        } else {
            el.style.top = y + 'px';
        }

        if (window.innerWidth < x + rect.width) {
            if (x - rect.width > 0) {
                el.style.left = (x - rect.width) + 'px';
            } else {
                el.style.left = '10px';
            }
        } else {
            el.style.left = x + 'px';
        }
    }

    /**
     * Close menu
     */
    obj.close = function() {
        if (el.classList.contains('jcontextmenu-focus')) {
            el.classList.remove('jcontextmenu-focus');
        }
    }

    /**
     * Create items based on the declared objectd
     * @param {object} items - List of object
     */
    obj.create = function(items) {
        // Update content
        el.innerHTML = '';

        // Append items
        for (var i = 0; i < items.length; i++) {
            if (items[i].type && items[i].type == 'line') {
                var itemContainer = document.createElement('hr');
            } else {
                var itemContainer = document.createElement('div');
                var itemText = document.createElement('a');
                itemText.innerHTML = items[i].title;

                if (items[i].disabled) {
                    itemContainer.className = 'jcontextmenu-disabled';
                } else if (items[i].onclick) {
                    itemContainer.method = items[i].onclick;
                    itemContainer.addEventListener("mouseup", function() {
                        // Execute method
                        this.method(this);
                    });
                }
                itemContainer.appendChild(itemText);

                if (items[i].shortcut) {
                    var itemShortCut = document.createElement('span');
                    itemShortCut.innerHTML = items[i].shortcut;
                    itemContainer.appendChild(itemShortCut);
                }
            }

            el.appendChild(itemContainer);
        }
    }

    if (typeof(obj.options.onclick) == 'function') {
        el.addEventListener('click', function(e) {
            obj.options.onclick(obj);
        });
    }

    el.addEventListener('blur', function(e) {
        setTimeout(function() {
            obj.close();
        }, 120);
    });

    window.addEventListener("mousewheel", function() {
        obj.close();
    });

    // Create items
    if (obj.options.items) {
        obj.create(obj.options.items);
    }

    el.contextmenu = obj;

    return obj;
});

jSuites.contextmenu.getElement = function(element) {
    var foundId = 0;

    function path (element) {
        if (element.parentNode && element.getAttribute('aria-contextmenu-id')) {
            foundId = element.getAttribute('aria-contextmenu-id')
        } else {
            if (element.parentNode) {
                path(element.parentNode);
            }
        }
    }

    path(element);

    return foundId;
}

document.addEventListener("contextmenu", function(e) {
    var id = jSuites.contextmenu.getElement(e.target);
    if (id) {
        var element = document.querySelector('#' + id);
        if (! element) {
            console.error('JSUITES: Contextmenu id not found');
        } else {
            element.contextmenu.open(e);
            e.preventDefault();
        }
    }
});

/**
 * Dialog v1.0.1
 * Author: paul.hodel@gmail.com
 * https://github.com/paulhodel/jtools
 */
 
jSuites.dialog = (function() {
    var obj = {};
    obj.options = {};

    var dialog = document.createElement('div');
    dialog.setAttribute('tabindex', '901');
    dialog.className = 'jdialog';
    dialog.id = 'dialog';

    var dialogHeader = document.createElement('div');
    dialogHeader.className = 'jdialog-header';

    var dialogTitle = document.createElement('div');
    dialogTitle.className = 'jdialog-title';
    dialogHeader.appendChild(dialogTitle);

    var dialogMessage = document.createElement('div');
    dialogMessage.className = 'jdialog-message';
    dialogHeader.appendChild(dialogMessage);

    var dialogFooter = document.createElement('div');
    dialogFooter.className = 'jdialog-footer';

    var dialogContainer = document.createElement('div');
    dialogContainer.className = 'jdialog-container';
    dialogContainer.appendChild(dialogHeader);
    dialogContainer.appendChild(dialogFooter);

    // Confirm
    var dialogConfirm = document.createElement('div');
    var dialogConfirmButton = document.createElement('input');
    dialogConfirmButton.value = obj.options.confirmLabel;
    dialogConfirmButton.type = 'button';
    dialogConfirmButton.onclick = function() {
        if (typeof(obj.options.onconfirm) == 'function') {
            obj.options.onconfirm();
        }
        obj.close();
    };
    dialogConfirm.appendChild(dialogConfirmButton);
    dialogFooter.appendChild(dialogConfirm);

    // Cancel
    var dialogCancel = document.createElement('div');
    var dialogCancelButton = document.createElement('input');
    dialogCancelButton.value = obj.options.cancelLabel;
    dialogCancelButton.type = 'button';
    dialogCancelButton.onclick = function() {
        if (typeof(obj.options.oncancel) == 'function') {
            obj.options.oncancel();
        }
        obj.close();
    }
    dialogCancel.appendChild(dialogCancelButton);
    dialogFooter.appendChild(dialogCancel);

    // Dialog
    dialog.appendChild(dialogContainer);

    obj.open = function(options) {
        obj.options = options;

        if (obj.options.title) {
            dialogTitle.innerHTML = obj.options.title;
        }

        if (obj.options.message) {
            dialogMessage.innerHTML = obj.options.message;
        }

        if (! obj.options.confirmLabel) {
            obj.options.confirmLabel = 'OK';
        }
        dialogConfirmButton.value = obj.options.confirmLabel;

        if (! obj.options.cancelLabel) {
            obj.options.cancelLabel = 'Cancel';
        }
        dialogCancelButton.value = obj.options.cancelLabel;

        if (obj.options.type == 'confirm') {
            dialogCancelButton.parentNode.style.display = '';
        } else {
            dialogCancelButton.parentNode.style.display = 'none';
        }

        // Append element to the app
        dialog.style.opacity = 100;

        // Append to the page
        if (jSuites.el) {
            jSuites.el.appendChild(dialog);
        } else {
            document.body.appendChild(dialog);
        }

        // Focus
        dialog.focus();

        // Show
        setTimeout(function() {
            dialogContainer.style.opacity = 100;
        }, 0);
    };

    obj.close = function() {
        dialog.style.opacity = 0;
        dialogContainer.style.opacity = 0;
        setTimeout(function() {
            dialog.remove();
        }, 100);
    };

    return obj;
})();

jSuites.confirm = (function(message, onconfirm) {
    if (jSuites.getWindowWidth() < 800) {
        jSuites.dialog.open({
            type: 'confirm',
            message: message,
            title: 'Confirmation',
            onconfirm: onconfirm,
        });
    } else {
        if (confirm(message)) {
            onconfirm();
        }
    }
});

jSuites.alert = function(message) {
    if (jSuites.getWindowWidth() < 800) {
        jSuites.dialog.open({
            title:'Alert',
            message:message,
        });
    } else {
        alert(message);
    }
}


jSuites.dropdown = (function(el, options) {
    var obj = {};
    obj.options = {};

    // If the element is a SELECT tag, create a configuration object
    if (el.tagName == 'SELECT') {
        var ret = jSuites.dropdown.extractFromDom(el, options);
        el = ret.el;
        options = ret.options;
    }

    // Default configuration
    var defaults = {
        url: null,
        data: [],
        multiple: false,
        autocomplete: false,
        type: null,
        width: null,
        opened: false,
        value: null,
        placeholder: '',
        position: false,
        onchange: null,
        onload: null,
        onopen: null,
        onclose: null,
        onblur: null,
    };

    // Loop through our object
    for (var property in defaults) {
        if (options && options.hasOwnProperty(property)) {
            obj.options[property] = options[property];
        } else {
            obj.options[property] = defaults[property];
        }
    }

    // Global container
    if (! jSuites.dropdown.current) {
        jSuites.dropdown.current = null;
    }

    // Containers
    obj.items = [];
    obj.groups = [];
    obj.selected = [];

    // Create dropdown
    el.classList.add('jdropdown');
 
    if (obj.options.type == 'searchbar') {
        el.classList.add('jdropdown-searchbar');
    } else if (obj.options.type == 'list') {
        el.classList.add('jdropdown-list');
    } else if (obj.options.type == 'picker') {
        el.classList.add('jdropdown-picker');
    } else {
        if (jSuites.getWindowWidth() < 800) {
            el.classList.add('jdropdown-picker');
            obj.options.type = 'picker';
        } else {
            if (obj.options.width) {
                el.style.width = obj.options.width;
                el.style.minWidth = obj.options.width;
            }
            el.classList.add('jdropdown-default');
            obj.options.type = 'default';
        }
    }

    // Header container
    var containerHeader = document.createElement('div');
    containerHeader.className = 'jdropdown-container-header';

    // Header
    var header = document.createElement('input');
    header.className = 'jdropdown-header';
    if (typeof(obj.options.onblur) == 'function') {
        header.onblur = function() {
            obj.options.onblur(el);
        }
    }

    // Container
    var container = document.createElement('div');
    container.className = 'jdropdown-container';

    // Dropdown content
    var content = document.createElement('div');
    content.className = 'jdropdown-content';

    // Close button
    var closeButton  = document.createElement('div');
    closeButton.className = 'jdropdown-close';
    closeButton.innerHTML = 'Done';

    // Create backdrop
    var backdrop  = document.createElement('div');
    backdrop.className = 'jdropdown-backdrop';

    // Autocomplete
    if (obj.options.autocomplete == true) {
        el.setAttribute('data-autocomplete', true);

        // Handler
        var keyTimer = null;
        header.addEventListener('keyup', function(e) {
            if (keyTimer) {
                clearTimeout(keyTimer);
            }
            keyTimer = setTimeout(function() {
                obj.find(header.value);
                keyTimer = null;
            }, 500);

            if (! el.classList.contains('jdropdown-focus')) {
                if (e.which > 65) {
                    obj.open();
                }
            }
        });
    } else {
        header.setAttribute('readonly', 'readonly');
    }

    // Place holder
    if (! obj.options.placeholder && el.getAttribute('placeholder')) {
        obj.options.placeholder = el.getAttribute('placeholder');
    }

    if (obj.options.placeholder) {
        header.setAttribute('placeholder', obj.options.placeholder);
    }

    // Append elements
    containerHeader.appendChild(header);
    if (obj.options.type == 'searchbar') {
        containerHeader.appendChild(closeButton);
    } else {
        container.appendChild(closeButton);
    }
    container.appendChild(content);
    el.appendChild(containerHeader);
    el.appendChild(container);
    el.appendChild(backdrop);

    /**
     * Init dropdown
     */
    obj.init = function() {
        if (obj.options.url) {
            jSuites.ajax({
                url: obj.options.url,
                method: 'GET',
                dataType: 'json',
                success: function(data) {
                    if (data) {
                        // Set data
                        obj.setData(data);
                        // Set value
                        if (obj.options.value != null) {
                            obj.setValue(obj.options.value);
                        }
                        // Onload method
                        if (typeof(obj.options.onload) == 'function') {
                            obj.options.onload(el, obj, data);
                        }
                    }
                }
            });
        } else {
            // Set data
            obj.setData();
            // Set value
            if (obj.options.value != null) {
                obj.setValue(obj.options.value);
            }
            // Onload
            if (typeof(obj.options.onload) == 'function') {
                obj.options.onload(el, obj, data);
            }
        }

        // Open dropdown
        if (obj.options.opened == true) {
            obj.open();
        }
    }

    obj.getUrl = function() {
        return obj.options.url;
    }

    obj.setUrl = function(url) {
        obj.options.url = url;

        jSuites.ajax({
            url: obj.options.url,
            method: 'GET',
            dataType: 'json',
            success: function(data) {
                obj.setData(data);
            }
        });
    }

    /**
     * Create a new item
     */
    obj.createItem = function(data) {
        // Create item
        var item = {};
        item.element = document.createElement('div');
        item.element.className = 'jdropdown-item';
        item.value = data.id;
        item.text = data.name;
        item.textLowerCase = '' + data.name.toLowerCase();

        // Image
        if (data.image) {
            var image = document.createElement('img');
            image.className = 'jdropdown-image';
            image.src = data.image;
            if (! data.title) {
               image.classList.add('jdropdown-image-small');
            }
            item.element.appendChild(image);
        }

        // Set content
        var node = document.createElement('div');
        node.className = 'jdropdown-description';
        node.innerHTML = data.name;

        // Title
        if (data.title) {
            var title = document.createElement('div');
            title.className = 'jdropdown-title';
            title.innerHTML = data.title;
            node.appendChild(title);
        }

        // Add node to item
        item.element.appendChild(node);

        return item;
    }

    obj.setData = function(data) {
        // Update data
        if (data) {
            obj.options.data = data;
        }

        // Data
        var data = obj.options.data;

        // Remove content from the DOM
        container.removeChild(content);

        // Make sure the content container is blank
        content.innerHTML = '';

        // Reset
        obj.reset();

        // Reset items
        obj.items = [];

        // Helpers
        var items = [];
        var groups = [];

        // Create elements
        if (data.length) {
            // Prepare data
            for (var i = 0; i < data.length; i++) {
                // Compatibility
                if (typeof(data[i]) != 'object') {
                    // Correct format
                    obj.options.data[i] = data[i] = { id: data[i], name: data[i] };
                }

                // Process groups
                if (data[i].group) {
                    if (! groups[data[i].group]) {
                        groups[data[i].group] = [];
                    }
                    groups[data[i].group].push(i);
                } else {
                    items.push(i);
                }
            }

            // Groups
            var groupNames = Object.keys(groups);

            // Append groups in case exists
            if (groupNames.length > 0) {
                for (var i = 0; i < groupNames.length; i++) {
                    // Group container
                    var group = document.createElement('div');
                    group.className = 'jdropdown-group';
                    // Group name
                    var groupName = document.createElement('div');
                    groupName.className = 'jdropdown-group-name';
                    groupName.innerHTML = groupNames[i];
                    // Group arrow
                    var groupArrow = document.createElement('i');
                    groupArrow.className = 'jdropdown-group-arrow jdropdown-group-arrow-down';
                    groupName.appendChild(groupArrow);
                    // Group items
                    var groupContent = document.createElement('div');
                    groupContent.className = 'jdropdown-group-items';
                    for (var j = 0; j < groups[groupNames[i]].length; j++) {
                        var item = obj.createItem(data[groups[groupNames[i]][j]]);
                        groupContent.appendChild(item.element);
                        // Items
                        obj.items.push(item);
                    }
                    // Group itens
                    group.appendChild(groupName);
                    group.appendChild(groupArrow);
                    group.appendChild(groupContent);
                    content.appendChild(group);
                }
            }

            if (items.length) {
                for (var i = 0; i < items.length; i++) {
                    var item = obj.createItem(data[items[i]]);
                    obj.items.push(item);
                    content.appendChild(item.element);
                }
            }

            // Create the Indexes
            for (var i = 0; i < obj.items.length; i++) {
                obj.items[i].element.setAttribute('data-index', i);
            }
        }

        // Re-insert the content to the container
        container.appendChild(content);
    }

    obj.getText = function(asArray) {
        // Result
        var result = [];
        // Append options
        for (var i = 0; i < obj.selected.length; i++) {
            if (obj.items[obj.selected[i]]) {
                result.push(obj.items[obj.selected[i]].text);
            }
        }

        if (asArray) {
            return result;
        } else {
            return result.join('; ');
        }
    }

    obj.getValue = function(asArray) {
        // Result
        var result = [];
        // Append options
        for (var i = 0; i < obj.selected.length; i++) {
            if (obj.items[obj.selected[i]]) {
                result.push(obj.items[obj.selected[i]].value);
            }
        }

        if (asArray) {
            return result;
        } else {
            return result.join(';');
        }
    }

    obj.setValue = function(value) {
        // Remove values
        for (var i = 0; i < obj.selected.length; i++) {
            obj.items[obj.selected[i]].element.classList.remove('jdropdown-selected')
        } 

        // Reset selected
        obj.selected = [];

        // Set values
        if (value != null) {
            if (Array.isArray(value)) {
                for (var i = 0; i < obj.items.length; i++) {
                    for (var j = 0; j < value.length; j++) {
                        if (obj.items[i].value == value[j]) {
                            // Keep index of the selected item
                            obj.selected.push(i);
                            // Visual selection
                            obj.items[i].element.classList.add('jdropdown-selected');
                        }
                    }
                }
            } else {
                for (var i = 0; i < obj.items.length; i++) {
                    if (obj.items[i].value == value) {
                        // Keep index of the selected item
                        obj.selected.push(i);
                        // Visual selection
                        obj.items[i].element.classList.add('jdropdown-selected');
                    }
                }
            }
        }

        // Update labels
        obj.updateLabel();
    }

    obj.selectIndex = function(index) {
        // Only select those existing elements
        if (obj.items && obj.items[index]) {
            var index = index = parseInt(index);
            // Current selection
            var oldValue = obj.getValue();
            var oldLabel = obj.getText();

            // Remove cursor style
            if (obj.currentIndex != null) {
                obj.items[obj.currentIndex].element.classList.remove('jdropdown-cursor');
            }
            // Set cursor style
            obj.items[index].element.classList.add('jdropdown-cursor');

            // Update cursor position
            obj.currentIndex = index;

            // Focus behaviour
            if (! obj.options.multiple) {
                // Unselect option
                if (obj.items[index].element.classList.contains('jdropdown-selected')) {
                    // Reset selected
                    obj.resetSelected();
                } else {
                    // Reset selected
                    obj.resetSelected();
                    // Update selected item
                    obj.items[index].element.classList.add('jdropdown-selected');
                    // Add to the selected list
                    obj.selected.push(index);
                    // Close
                    obj.close();
                }
            } else {
                // Toggle option
                if (obj.items[index].element.classList.contains('jdropdown-selected')) {
                    obj.items[index].element.classList.remove('jdropdown-selected');
                    // Remove from selected list
                    var indexToRemove = obj.selected.indexOf(index);
                    // Remove select
                    obj.selected.splice(indexToRemove, 1);
                } else {
                    // Select element
                    obj.items[index].element.classList.add('jdropdown-selected');
                    // Add to the selected list
                    obj.selected.push(index);
                }

                // Update labels for multiple dropdown
                if (! obj.options.autocomplete) {
                    obj.updateLabel();
                }
            }

            // Current selection
            var newValue = obj.getValue();
            var newLabel = obj.getText();

            // Events
            if (typeof(obj.options.onchange) == 'function') {
                obj.options.onchange(el, index, oldValue, newValue, oldLabel, newLabel);
            }
        }
    }

    obj.selectItem = function(item) {
        if (jSuites.dropdown.current) {
            var index = item.getAttribute('data-index');
            if (index != null) {
                obj.selectIndex(index);
            }
        }
    }

    obj.find = function(str) {
        // Force lowercase
        var str = str ? str.toLowerCase() : null;

        // Append options
        for (var i = 0; i < obj.items.length; i++) {
            if (str == null || obj.items[i].textLowerCase.indexOf(str) != -1) {
                obj.items[i].element.style.display = '';
            } else {
                if (obj.selected.indexOf(i) == -1) {
                    obj.items[i].element.style.display = 'none';
                } else {
                    obj.items[i].element.style.display = '';
                }
            }
        }

        var numVisibleItems = function(items) {
            var visible = 0;
            for (var j = 0; j < items.length; j++) {
                if (items[j].style.display != 'none') {
                    visible++;
                }
            }
            return visible;
        }

        // Hide groups
        /*for (var i = 0; i < obj.groups.length; i++) {
            if (numVisibleItems(obj.groups[i].querySelectorAll('.jdropdown-item'))) {
                obj.groups[i].children[0].style.display = '';
            } else {
                obj.groups[i].children[0].style.display = 'none';
            }
        }*/
    }

    obj.updateLabel = function() {
        // Update label
        header.value = obj.getText();
    }

    obj.open = function() {
        if (jSuites.dropdown.current != el) {
            if (jSuites.dropdown.current) {
                jSuites.dropdown.current.dropdown.close();
            }
            jSuites.dropdown.current = el;
        }

        // Focus
        if (! el.classList.contains('jdropdown-focus')) {
            // Add focus
            el.classList.add('jdropdown-focus');

            // Animation
            if (jSuites.getWindowWidth() < 800) {
                if (obj.options.type == null || obj.options.type == 'picker') {
                    jSuites.slideBottom(container, 1);
                }
            }

            // Filter
            if (obj.options.autocomplete == true) {
                // Redo search
                obj.find();
                // Clear search field
                header.value = '';
                header.focus();
            }

            // Set cursor for the first or first selected element
            var cursor = (obj.selected && obj.selected[0]) ? obj.selected[0] : 0;
            obj.updateCursor(cursor);

            // Container Size
            if (! obj.options.type || obj.options.type == 'default') {
                const rect = el.getBoundingClientRect();
                const rectContainer = container.getBoundingClientRect();

                if (obj.options.position) {
                    container.style.position = 'fixed';
                    if (window.innerHeight < rect.bottom + rectContainer.height) {
                        container.style.top = '';
                        container.style.bottom = (window.innerHeight - rect.top ) + 1 + 'px';
                    } else {
                        container.style.top = rect.bottom + 'px';
                        container.style.bottom = '';
                    }
                    container.style.left = rect.left + 'px';
                } else {
                    if (window.innerHeight < rect.bottom + rectContainer.height) {
                        container.style.top = '';
                        container.style.bottom = rect.height + 1 + 'px';
                    } else {
                        container.style.top = '';
                        container.style.bottom = '';
                    }
                }

                container.style.minWidth = rect.width + 'px';
            }
        }

        // Events
        if (typeof(obj.options.onopen) == 'function') {
            obj.options.onopen(el);
        }
    }

    obj.close = function(ignoreEvents) {
        if (jSuites.dropdown.current) {
            // Remove controller
            jSuites.dropdown.current = null
            // Remove cursor
            obj.resetCursor();
            // Update labels
            obj.updateLabel();
            // Events
            if (! ignoreEvents && typeof(obj.options.onclose) == 'function') {
                obj.options.onclose(el);
            }
            // Blur
            if (header.blur) {
                header.blur();
            }
            // Remove focus
            el.classList.remove('jdropdown-focus');
        }

        return obj.getValue();
    }

    /**
     * Update position cursor
     */
    obj.updateCursor = function(index) {
        // Set new cursor
        if (obj.items && obj.items[index] && obj.items[index].element) {
            // Reset cursor
            obj.resetCursor();

            // Set new cursor
            obj.items[index].element.classList.add('jdropdown-cursor');

            // Update position
            obj.currentIndex = parseInt(index);
    
            // Update scroll to the cursor element
            var container = content.scrollTop;
            var element = obj.items[obj.currentIndex].element;
            content.scrollTop = element.offsetTop - element.scrollTop + element.clientTop - 95;
        }
    }

    /**
     * Reset cursor
     */
    obj.resetCursor = function() {
        // Remove current cursor
        if (obj.currentIndex != null) {
            // Remove visual cursor
            if (obj.items && obj.items[obj.currentIndex]) {
                obj.items[obj.currentIndex].element.classList.remove('jdropdown-cursor');
            }
            // Reset cursor
            obj.currentIndex = null;
        }
    }

    /**
     * Reset cursor
     */
    obj.resetSelected = function() {
        // Unselected all
        if (obj.selected) {
            // Remove visual selection
            for (var i = 0; i < obj.selected.length; i++) {
                if (obj.items[obj.selected[i]]) {
                    obj.items[obj.selected[i]].element.classList.remove('jdropdown-selected');
                }
            }
            // Reset current selected items
            obj.selected = [];
        }
    }

    /**
     * Reset cursor and selected items
     */
    obj.reset = function() {
        // Reset cursor
        obj.resetCursor();

        // Reset selected
        obj.resetSelected();

        // Update labels
        obj.updateLabel();
    }

    /**
     * First visible item
     */
    obj.firstVisible = function() {
        var newIndex = null;
        for (var i = 0; i < obj.items.length; i++) {
            if (obj.items[i].element.style.display != 'none') {
                newIndex = i;
                break;
            }
        }

        if (newIndex == null) {
            return false;
        }

        obj.updateCursor(newIndex);
    }

    /**
     * Navigation
     */
    obj.first = function() {
        var newIndex = null;
        for (var i = obj.currentIndex - 1; i >= 0; i--) {
            if (obj.items[i].element.style.display != 'none') {
                newIndex = i;
            }
        }

        if (newIndex == null) {
            return false;
        }

        obj.updateCursor(newIndex);
    }

    obj.last = function() {
        var newIndex = null;
        for (var i = obj.currentIndex + 1; i < obj.items.length; i++) {
            if (obj.items[i].element.style.display != 'none') {
                newIndex = i;
            }
        }

        if (newIndex == null) {
            return false;
        }

        obj.updateCursor(newIndex);
    }

    obj.next = function() {
        var newIndex = null;
        for (var i = obj.currentIndex + 1; i < obj.items.length; i++) {
            if (obj.items[i].element.style.display != 'none') {
                newIndex = i;
                break;
            }
        }

        if (newIndex == null) {
            return false;
        }

        obj.updateCursor(newIndex);
    }

    obj.prev = function() {
        var newIndex = null;
        for (var i = obj.currentIndex - 1; i >= 0; i--) {
            if (obj.items[i].element.style.display != 'none') {
                newIndex = i;
                break;
            }
        }

        if (newIndex == null) {
            return false;
        }

        obj.updateCursor(newIndex);
    }

    if (! jSuites.dropdown.hasEvents) {
        if ('ontouchsend' in document.documentElement === true) {
            document.addEventListener('touchsend', jSuites.dropdown.mouseup);
        } else {
            document.addEventListener('mouseup', jSuites.dropdown.mouseup);
        }
        document.addEventListener('keydown', jSuites.dropdown.onkeydown);

        jSuites.dropdown.hasEvents = true;
    }

    // Start dropdown
    obj.init();

    // Keep object available from the node
    el.dropdown = obj;

    return obj;
});

jSuites.dropdown.hasEvents = false;

jSuites.dropdown.mouseup = function(e) {
    var element = jSuites.getElement(e.target, 'jdropdown');
    if (element) {
        var dropdown = element.dropdown;
        if (e.target.classList.contains('jdropdown-header')) {
            if (element.classList.contains('jdropdown-focus') && element.classList.contains('jdropdown-default')) {
                dropdown.close();
            } else {
                dropdown.open();
            }
        } else if (e.target.classList.contains('jdropdown-group-name')) {
            var items = e.target.nextSibling.children;
            if (e.target.nextSibling.style.display != 'none') {
                for (var i = 0; i < items.length; i++) {
                    if (items[i].style.display != 'none') {
                        dropdown.selectItem(items[i]);
                    }
                }
            }
        } else if (e.target.classList.contains('jdropdown-group-arrow')) {
            if (e.target.classList.contains('jdropdown-group-arrow-down')) {
                e.target.classList.remove('jdropdown-group-arrow-down');
                e.target.classList.add('jdropdown-group-arrow-up');
                e.target.parentNode.nextSibling.style.display = 'none';
            } else {
                e.target.classList.remove('jdropdown-group-arrow-up');
                e.target.classList.add('jdropdown-group-arrow-down');
                e.target.parentNode.nextSibling.style.display = '';
            }
        } else if (e.target.classList.contains('jdropdown-item')) {
            dropdown.selectItem(e.target);
        } else if (e.target.classList.contains('jdropdown-image')) {
            dropdown.selectIndex(e.target.parentNode.getAttribute('data-index'));
        } else if (e.target.classList.contains('jdropdown-description')) {
            dropdown.selectIndex(e.target.parentNode.getAttribute('data-index'));
        } else if (e.target.classList.contains('jdropdown-title')) {
            dropdown.selectIndex(e.target.parentNode.parentNode.getAttribute('data-index'));
        } else if (e.target.classList.contains('jdropdown-close') || e.target.classList.contains('jdropdown-backdrop')) {
            // Close
            dropdown.close();
        }

        e.stopPropagation();
        e.preventDefault();
    } else {
        if (jSuites.dropdown.current) {
            jSuites.dropdown.current.dropdown.close();
        }
    }
}


// Keydown controls
jSuites.dropdown.onkeydown = function(e) {
    if (jSuites.dropdown.current) {
        // Element
        var element = jSuites.dropdown.current.dropdown;
        // Index
        var index = element.currentIndex;

        if (e.shiftKey) {

        } else {
            if (e.which == 13 || e.which == 27 || e.which == 35 || e.which == 36 || e.which == 38 || e.which == 40) {
                // Move cursor
                if (e.which == 13) {
                    element.selectIndex(index)
                } else if (e.which == 38) {
                    if (index == null) {
                        element.firstVisible();
                    } else if (index > 0) {
                        element.prev();
                    }
                } else if (e.which == 40) {
                    if (index == null) {
                        element.firstVisible();
                    } else if (index + 1 < element.options.data.length) {
                        element.next();
                    }
                } else if (e.which == 36) {
                    element.first();
                } else if (e.which == 35) {
                    element.last();
                } else if (e.which == 27) {
                    element.close();
                }

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

jSuites.dropdown.extractFromDom = function(el, options) {
    // Keep reference
    var select = el;
    if (! options) {
        options = {};
    }
    // Prepare configuration
    if (el.getAttribute('multiple') && (! options || options.multiple == undefined)) {
        options.multiple = true;
    }
    if (el.getAttribute('placeholder') && (! options || options.placeholder == undefined)) {
        options.placeholder = el.getAttribute('placeholder');
    }
    if (el.getAttribute('data-autocomplete') && (! options || options.autocomplete == undefined)) {
        options.autocomplete = true;
    }
    if (! options || options.width == undefined) {
        options.width = el.offsetWidth;
    }
    if (el.value && (! options || options.value == undefined)) {
        options.value = el.value;
    }
    if (! options || options.data == undefined) {
        options.data = [];
        for (var j = 0; j < el.children.length; j++) {
            if (el.children[j].tagName == 'OPTGROUP') {
                for (var i = 0; i < el.children[j].children.length; i++) {
                    options.data.push({
                        id: el.children[j].children[i].value,
                        name: el.children[j].children[i].innerHTML,
                        group: el.children[j].getAttribute('label'),
                    });
                }
            } else {
                options.data.push({
                    id: el.children[j].value,
                    name: el.children[j].innerHTML,
                });
            }
        }
    }
    if (! options || options.onchange == undefined) {
        options.onchange = function(a,b,c,d) {
            if (options.multiple == true) {
                if (obj.items[b].classList.contains('jdropdown-selected')) {
                    select.options[b].setAttribute('selected', 'selected');
                } else {
                    select.options[b].removeAttribute('selected');
                }
            } else {
                select.value = d;
            }
        }
    }
    // Create DIV
    var div = document.createElement('div');
    el.parentNode.insertBefore(div, el);
    el.style.display = 'none';
    el = div;

    return { el:el, options:options };
}

/**
 * (c) jTools Text Editor
 * https://github.com/paulhodel/jtools
 *
 * @author: Paul Hodel <paul.hodel@gmail.com>
 * @description: Inline richtext editor
 */

jSuites.editor = (function(el, options) {
    var obj = {};
    obj.options = {};

    // Default configuration
    var defaults = {
        // Initial HTML content
        value: null,
        // Initial snippet
        snippet: null,
        // Add toolbar
        toolbar: null,
        // Max height
        maxHeight: null,
        // Website parser is to read websites and images from cross domain
        remoteParser: null,
        // Key from youtube to read properties from URL
        youtubeKey: null,
        // User list
        userSearch: null,
        // Parse URL
        parseURL: false,
        // Accept drop files
        dropZone: true,
        dropAsAttachment: false,
        acceptImages: true,
        acceptFiles: false,
        maxFileSize: 5000000, 
        // Border
        border: true,
        padding: true,
        focus: false,
        // Events
        onclick: null,
        onfocus: null,
        onblur: null,
        onload: null,
        onenter: null,
        onkeyup: null,
        onkeydown: null,
    };

    // Loop through our object
    for (var property in defaults) {
        if (options && options.hasOwnProperty(property)) {
            obj.options[property] = options[property];
        } else {
            obj.options[property] = defaults[property];
        }
    }

    // Private controllers
    var imageResize = 0;
    var editorTimer = null;
    var editorAction = null;

    // Make sure element is empty
    el.innerHTML = '';

    // Prepare container
    el.classList.add('jeditor-container');

    // Padding
    if (obj.options.padding == true) {
        el.classList.add('jeditor-padding');
    }

    // Border
    if (obj.options.border == false) {
        el.style.border = '0px';
    }

    // Snippet
    var snippet = document.createElement('div');
    snippet.className = 'snippet';
    snippet.setAttribute('contenteditable', false);

    // Toolbar
    var toolbar = document.createElement('div');
    toolbar.className = 'jeditor-toolbar';

    // Create editor
    var editor = document.createElement('div');
    editor.setAttribute('contenteditable', true);
    editor.setAttribute('spellcheck', false);
    editor.className = 'jeditor';

    // Max height
    if (obj.options.maxHeight) {
        editor.style.overflowY = 'auto';
        editor.style.maxHeight = obj.options.maxHeight;
    }

    // Set editor initial value
    if (obj.options.value) {
        var value = obj.options.value;
    } else {
        var value = el.innerHTML ? el.innerHTML : ''; 
    }

    if (! value) {
        var value = '<br>';
    }

    /**
     * Extract images from a HTML string
     */
    var extractImageFromHtml = function(html) {
        // Create temp element
        var div = document.createElement('div');
        div.innerHTML = html;

        // Extract images
        var img = div.querySelectorAll('img');

        if (img.length) {
            for (var i = 0; i < img.length; i++) {
                obj.addImage(img[i].src);
            }
        }
    }

    /**
     * Insert node at caret
     */
    var insertNodeAtCaret = function(newNode) {
        var sel, range;

        if (window.getSelection) {
            sel = window.getSelection();
            if (sel.rangeCount) {
                range = sel.getRangeAt(0);
                var selectedText = range.toString();
                range.deleteContents();
                range.insertNode(newNode); 
                // move the cursor after element
                range.setStartAfter(newNode);
                range.setEndAfter(newNode); 
                sel.removeAllRanges();
                sel.addRange(range);
            }
        }
    }

    /**
     * Append snippet or thumbs in the editor
     * @Param object data
     */
    var appendElement = function(data) {
        // Reset snippet
        snippet.innerHTML = '';

        if (data.image) {
            var div = document.createElement('div');
            div.className = 'snippet-image';
            div.setAttribute('data-k', 'image');
            snippet.appendChild(div);

            var image = document.createElement('img');
            image.src = data.image;
            div.appendChild(image);
        }

        var div = document.createElement('div');
        div.className = 'snippet-title';
        div.setAttribute('data-k', 'title');
        div.innerHTML = data.title;
        snippet.appendChild(div);

        var div = document.createElement('div');
        div.className = 'snippet-description';
        div.setAttribute('data-k', 'description');
        div.innerHTML = data.description;
        snippet.appendChild(div);

        var div = document.createElement('div');
        div.className = 'snippet-host';
        div.setAttribute('data-k', 'host');
        div.innerHTML = data.host;
        snippet.appendChild(div);

        var div = document.createElement('div');
        div.className = 'snippet-url';
        div.setAttribute('data-k', 'url');
        div.innerHTML = data.url;
        snippet.appendChild(div);

        editor.appendChild(snippet);
    }

    var verifyEditor = function() {
        clearTimeout(editorTimer);
        editorTimer = setTimeout(function() {
            var snippet = editor.querySelector('.snippet');
            var thumbsContainer = el.querySelector('.jeditor-thumbs-container');

            if (! snippet && ! thumbsContainer) {
                var html = editor.innerHTML.replace(/\n/g, ' ');
                var container = document.createElement('div');
                container.innerHTML = html;
                var thumbsContainer = container.querySelector('.jeditor-thumbs-container');
                if (thumbsContainer) {
                    thumbsContainer.remove();
                }
                var text = container.innerText; 
                var url = jSuites.editor.detectUrl(text);

                if (url) {
                    if (url[0].substr(-3) == 'jpg' || url[0].substr(-3) == 'png' || url[0].substr(-3) == 'gif') {
                        if (jSuites.editor.getDomain(url[0]) == window.location.hostname) {
                            obj.importImage(url[0], '');
                        } else {
                            obj.importImage(obj.options.remoteParser + url[0], '');
                        }
                    } else {
                        var id = jSuites.editor.youtubeParser(url[0]);

                        if (id) {
                            obj.getYoutube(id);
                        } else {
                            obj.getWebsite(url[0]);
                        }
                    }
                }
            }
        }, 1000);
    }

    obj.parseContent = function() {
        verifyEditor();
    }

    /**
     * Get metadata from a youtube video
     */
    obj.getYoutube = function(id) {
        if (! obj.options.youtubeKey) {
            console.error('The youtubeKey is not defined');
        } else {
            jSuites.ajax({
                url: 'https://www.googleapis.com/youtube/v3/videos?part=snippet,statistics&key=' + obj.options.youtubeKey + '&id=' + id,
                method: 'GET',
                dataType: 'json',
                success: function(result) {
                    // Only valid elements to be appended
                    if (result.items && result.items[0]) {
                        var p = {
                            title: '',
                            description: '',
                            image: '',
                            host: 'www.youtube.com',
                            url: 'https://www.youtube.com?watch=' + id,
                        }
                        if (result.items[0].snippet.title) {
                            p.title = result.items[0].snippet.title;
                        }
                        if (result.items[0].snippet.description) {
                            p.description = result.items[0].snippet.description;

                            if (p.description.length > 150) {
                                p.description = p.description.substr(0, 150) + '...';
                            }
                        }
                        if (result.items[0].snippet.thumbnails.medium.url) {
                            p.image = result.items[0].snippet.thumbnails.medium.url;
                        }

                        appendElement(p);
                    }
                }
            });
        }
    }

    /**
     * Get meta information from a website
     */
    obj.getWebsite = function(url) {
        if (! obj.options.remoteParser) {
            console.log('The remoteParser is not defined');
        } else {
            jSuites.ajax({
                url: obj.options.remoteParser + encodeURI(url.trim()),
                method: 'GET',
                dataType: 'json',
                success: function(result) {
                    var p = {
                        title: '',
                        description: '',
                        image: '',
                        host: url,
                        url: url,
                    }

                    if (result.title) {
                        p.title = result.title;
                    }
                    if (result.description) {
                        p.description = result.description;
                    }
                    if (result.image) {
                        p.image = result.image;
                    } else if (result['og:image']) {
                        p.image = result['og:image'];
                    }
                    if (result.host) {
                        p.host = result.host;
                    }
                    if (result.url) {
                        p.url = result.url;
                    }

                    appendElement(p);
                }
            });
        }
    }

    /**
     * Set editor value
     */
    obj.setData = function(html) {
        editor.innerHTML = html;
        cursor();
    }

    /**
     * Get editor data
     */
    obj.getData = function(json) {
        if (! json) {
            var data = editor.innerHTML;
        } else {
            var data = {
                content : '',
            }

            // Get tag users
            var tagged = editor.querySelectorAll('.post-tag');
            if (tagged.length) {
                data.users = [];
                for (var i = 0; i < tagged.length; i++) {
                    var userId = tagged[i].getAttribute('data-user');
                    if (userId) {
                        data.users.push(userId);
                    }
                }
                data.users = data.users.join(',');
            }

            if (snippet.innerHTML) {
                var index = 0;
                data.snippet = {};
                for (var i = 0; i < snippet.children.length; i++) {
                    // Get key from element
                    var key = snippet.children[i].getAttribute('data-k');
                    if (key) {
                        if (key == 'image') {
                            data.snippet.image = snippet.children[i].children[0].getAttribute('src');
                        } else {
                            data.snippet[key] = snippet.children[i].innerHTML;
                        }
                    }
                }

                snippet.innerHTML = '';
                snippet.remove();
            }

            var text = editor.innerHTML;
            text = text.replace(/<br>/g, "\n");
            text = text.replace(/<\/div>/g, "<\/div>\n");
            text = text.replace(/<(?:.|\n)*?>/gm, "");
            data.content = text.trim();
            data = JSON.stringify(data);
        }

        return data;
    }

    // Reset
    obj.reset = function() {
        editor.innerHTML = '';
    }

    obj.addPdf = function(data) {
        if (data.result.substr(0,4) != 'data') {
            console.error('Invalid source');
        } else {
            var canvas = document.createElement('canvas');
            canvas.width = 60;
            canvas.height = 60;

            var img = new Image();
            var ctx = canvas.getContext('2d');
            ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

            canvas.toBlob(function(blob) {
                var newImage = document.createElement('img');
                newImage.src = window.URL.createObjectURL(blob);
                newImage.setAttribute('data-extension', 'pdf');
                if (data.name) {
                    newImage.setAttribute('data-name', data.name);
                }
                if (data.size) {
                    newImage.setAttribute('data-size', data.size);
                }
                if (data.date) {
                    newImage.setAttribute('data-date', data.date);
                }
                newImage.className = 'jfile pdf';

                insertNodeAtCaret(newImage);
                jSuites.files[newImage.src] = data.result.substr(data.result.indexOf(',') + 1);
            });
        }
    }

    obj.addImage = function(src, name, size, date) {
        if (src.substr(0,4) != 'data' && ! obj.options.remoteParser) {
            console.error('remoteParser not defined in your initialization');
        } else {
            // This is to process cross domain images
            if (src.substr(0,4) == 'data') {
                var extension = src.split(';')
                extension = extension[0].split('/');
                extension = extension[1];
            } else {
                var extension = src.substr(src.lastIndexOf('.') + 1);
                // Work for cross browsers
                src = obj.options.remoteParser + src;
            }

            var img = new Image();

            img.onload = function onload() {
                var canvas = document.createElement('canvas');
                canvas.width = img.width;
                canvas.height = img.height;

                var ctx = canvas.getContext('2d');
                ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

                canvas.toBlob(function(blob) {
                    var newImage = document.createElement('img');
                    newImage.src = window.URL.createObjectURL(blob);
                    newImage.setAttribute('tabindex', '900');
                    newImage.setAttribute('data-extension', extension);
                    if (name) {
                        newImage.setAttribute('data-name', name);
                    }
                    if (size) {
                        newImage.setAttribute('data-size', size);
                    }
                    if (date) {
                        newImage.setAttribute('data-date', date);
                    }
                    newImage.className = 'jfile';
                    var content = canvas.toDataURL();
                    insertNodeAtCaret(newImage);

                    jSuites.files[newImage.src] = content.substr(content.indexOf(',') + 1);
                });
            };

            img.src = src;
        }
    }

    obj.addFile = function(files) {
        var reader = [];

        for (var i = 0; i < files.length; i++) {
            if (files[i].size > obj.options.maxFileSize) {
                alert('The file is too big');
            } else {
                // Only PDF or Images
                var type = files[i].type.split('/');

                if (type[0] == 'image') {
                    type = 1;
                } else if (type[1] == 'pdf') {
                    type = 2;
                } else {
                    type = 0;
                }

                if (type) {
                    // Create file
                    reader[i] = new FileReader();
                    reader[i].index = i;
                    reader[i].type = type;
                    reader[i].name = files[i].name;
                    reader[i].date = files[i].lastModified;
                    reader[i].size = files[i].size;
                    reader[i].addEventListener("load", function (data) {
                        // Get result
                        if (data.target.type == 2) {
                            if (obj.options.acceptFiles == true) {
                                obj.addPdf(data.target);
                            }
                        } else {
                            obj.addImage(data.target.result, data.target.name, data.total, data.target.lastModified);
                        }
                    }, false);

                    reader[i].readAsDataURL(files[i])
                } else {
                    alert('The extension is not allowed');
                }
            }
        }
    }

    // Destroy
    obj.destroy = function() {
        editor.removeEventListener('mouseup', editorMouseUp);
        editor.removeEventListener('mousedown', editorMouseDown);
        editor.removeEventListener('mousemove', editorMouseMove);
        editor.removeEventListener('keyup', editorKeyUp);
        editor.removeEventListener('keydown', editorKeyDown);
        editor.removeEventListener('dragstart', editorDragStart);
        editor.removeEventListener('dragenter', editorDragEnter);
        editor.removeEventListener('dragover', editorDragOver);
        editor.removeEventListener('drop', editorDrop);
        editor.removeEventListener('paste', editorPaste);

        if (typeof(obj.options.onblur) == 'function') {
            editor.removeEventListener('blur', editorBlur);
        }
        if (typeof(obj.options.onfocus) == 'function') {
            editor.removeEventListener('focus', editorFocus);
        }

        el.editor = null;
        el.classList.remove('jeditor-container');

        toolbar.remove();
        snippet.remove();
        editor.remove();
    }

    var isLetter = function (str) {
        var regex = /([\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\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\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\u2183\u2184\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\u3006\u3031-\u3035\u303B\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-\uA6E5\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]+)/g;
        return str.match(regex) ? 1 : 0;
    }

    // Event handlers
    var editorMouseUp = function(e) {
        editorAction = false;
    }

    var editorMouseDown = function(e) {
        var close = function(snippet) {
            var rect = snippet.getBoundingClientRect();
            if (rect.width - (e.clientX - rect.left) < 40 && e.clientY - rect.top < 40) {
                snippet.innerHTML = '';
                snippet.remove();
            }
        }

        if (e.target.tagName == 'IMG') {
            if (e.target.style.cursor) {
                var rect = e.target.getBoundingClientRect();
                editorAction = {
                    e: e.target,
                    x: e.clientX,
                    y: e.clientY,
                    w: rect.width,
                    h: rect.height,
                    d: e.target.style.cursor,
                }

                if (! e.target.style.width) {
                    e.target.style.width = rect.width + 'px';
                }

                if (! e.target.style.height) {
                    e.target.style.height = rect.height + 'px';
                }

                var s = window.getSelection();
                if (s.rangeCount) {
                    for (var i = 0; i < s.rangeCount; i++) {
                        s.removeRange(s.getRangeAt(i));
                    }
                }
            } else {
                editorAction = true;
            }
        } else { 
            if (e.target.classList.contains('snippet')) {
                close(e.target);
            } else if (e.target.parentNode.classList.contains('snippet')) {
                close(e.target.parentNode);
            }

            editorAction = true;
        }
    }

    var editorMouseMove = function(e) {
        if (e.target.tagName == 'IMG') {
            if (e.target.getAttribute('tabindex')) {
                var rect = e.target.getBoundingClientRect();
                if (e.clientY - rect.top < 5) {
                    if (rect.width - (e.clientX - rect.left) < 5) {
                        e.target.style.cursor = 'ne-resize';
                    } else if (e.clientX - rect.left < 5) {
                        e.target.style.cursor = 'nw-resize';
                    } else {
                        e.target.style.cursor = 'n-resize';
                    }
                } else if (rect.height - (e.clientY - rect.top) < 5) {
                    if (rect.width - (e.clientX - rect.left) < 5) {
                        e.target.style.cursor = 'se-resize';
                    } else if (e.clientX - rect.left < 5) {
                        e.target.style.cursor = 'sw-resize';
                    } else {
                        e.target.style.cursor = 's-resize';
                    }
                } else if (rect.width - (e.clientX - rect.left) < 5) {
                    e.target.style.cursor = 'e-resize';
                } else if (e.clientX - rect.left < 5) {
                    e.target.style.cursor = 'w-resize';
                } else {
                    e.target.style.cursor = '';
                }
            }
        }

        // Move
        if (e.which == 1 && editorAction && editorAction.d) {
            if (editorAction.d == 'e-resize' || editorAction.d == 'ne-resize' ||  editorAction.d == 'se-resize') {
                editorAction.e.style.width = (editorAction.w + (e.clientX - editorAction.x)) + 'px';

                if (e.shiftKey) {
                    var newHeight = (e.clientX - editorAction.x) * (editorAction.h / editorAction.w);
                    editorAction.e.style.height = editorAction.h + newHeight + 'px';
                } else {
                    var newHeight =  null;
                }
            }

            if (! newHeight) {
                if (editorAction.d == 's-resize' || editorAction.d == 'se-resize' || editorAction.d == 'sw-resize') {
                    if (! e.shiftKey) {
                        editorAction.e.style.height = editorAction.h + (e.clientY - editorAction.y);
                    }
                }
            }
        }
    }

    var editorKeyUp = function(e) {
        if (! editor.innerHTML) {
            editor.innerHTML = '<div><br></div>';
        }

        if (typeof(obj.options.onkeyup) == 'function') { 
            obj.options.onkeyup(e, el);
        }
    }


    var editorKeyDown = function(e) {
        // Check for URL
        if (obj.options.parseURL == true) {
            verifyEditor();
        }

        // Closable
        if (typeof(obj.options.onenter) == 'function' && e.which == 13) {
            var data = obj.getData();
            obj.options.onenter(obj, el, data, e);
        }

        if (typeof(obj.options.onkeydown) == 'function') { 
            obj.options.onkeydown(e, el);
        }
    }

    var editorPaste = function(e) {
        if (e.clipboardData || e.originalEvent.clipboardData) {
            var html = (e.originalEvent || e).clipboardData.getData('text/html');
            var text = (e.originalEvent || e).clipboardData.getData('text/plain');
            var file = (e.originalEvent || e).clipboardData.files
        } else if (window.clipboardData) {
            var html = window.clipboardData.getData('Html');
            var text = window.clipboardData.getData('Text');
            var file = window.clipboardData.files
        }

        if (file.length) {
            // Paste a image from the clipboard
            obj.addFile(file);
        } else {
            // Paste text
            text = text.split('\r\n');
            var str = '';
            if (e.target.nodeName == 'DIV' && ! e.target.classList.contains('jeditor')) {
                for (var i = 0; i < text.length; i++) {
                    if (text[i]) {
                        str += text[i] + "<br>\r\n";
                    }
                }
            } else {
                for (var i = 0; i < text.length; i++) {
                    if (text[i]) {
                        str += '<div>' + text[i] + '</div>';
                    } else {
                        str += '<div><br></div>';
                    }
                }
            }
            // Insert text
            document.execCommand('insertHtml', false, str);

            // Extra images from the paste
            if (obj.options.acceptImages == true) {
                extractImageFromHtml(html);
            }
        }

        e.preventDefault();
    }

    var editorDragStart = function(e) {
        if (editorAction && editorAction.e) {
            e.preventDefault();
        }
    }

    var editorDragEnter = function(e) {
        if (editorAction || obj.options.dropZone == false) {
            // Do nothing
        } else {
            el.classList.add('jeditor-dragging');
        }
    }

    var editorDragOver = function(e) {
        if (editorAction || obj.options.dropZone == false) {
            // Do nothing
        } else {
            if (editorTimer) {
                clearTimeout(editorTimer);
            }

            editorTimer = setTimeout(function() {
                el.classList.remove('jeditor-dragging');
            }, 100);
        }
    }

    var editorDrop = function(e) {
        if (editorAction || obj.options.dropZone == false) {
            // Do nothing
        } else {
            // Position caret on the drop
            var range = null;
            if (document.caretRangeFromPoint) {
                range=document.caretRangeFromPoint(e.clientX, e.clientY);
            } else if (e.rangeParent) {
                range=document.createRange();
                range.setStart(e.rangeParent,e.rangeOffset);
            }
            var sel = window.getSelection();
            sel.removeAllRanges();
            sel.addRange(range);
            sel.anchorNode.parentNode.focus();

            var html = (e.originalEvent || e).dataTransfer.getData('text/html');
            var text = (e.originalEvent || e).dataTransfer.getData('text/plain');
            var file = (e.originalEvent || e).dataTransfer.files;
    
            if (file.length) {
                obj.addFile(file);
            } else if (text) {
                extractImageFromHtml(html);
            }

            el.classList.remove('jeditor-dragging');
            e.preventDefault();
        }
    }

    var editorBlur = function() {
        obj.options.onblur(obj, el, obj.getData());
    }

    var editorFocus = function() {
        obj.options.onfocus(obj, el, obj.getData());
    }

    editor.addEventListener('mouseup', editorMouseUp);
    editor.addEventListener('mousedown', editorMouseDown);
    editor.addEventListener('mousemove', editorMouseMove);
    editor.addEventListener('keyup', editorKeyUp);
    editor.addEventListener('keydown', editorKeyDown);
    editor.addEventListener('dragstart', editorDragStart);
    editor.addEventListener('dragenter', editorDragEnter);
    editor.addEventListener('dragover', editorDragOver);
    editor.addEventListener('drop', editorDrop);
    editor.addEventListener('paste', editorPaste);

    // Blur
    if (typeof(obj.options.onblur) == 'function') {
        editor.addEventListener('blur', editorBlur);
    }

    // Focus
    if (typeof(obj.options.onfocus) == 'function') {
        editor.addEventListener('focus', editorFocus);
    }

    // Onload
    if (typeof(obj.options.onload) == 'function') {
        obj.options.onload(el, editor);
    }

    // Set value to the editor
    editor.innerHTML = value;

    // Append editor to the containre
    el.appendChild(editor);

    // Snippet
    if (obj.options.snippet) {
        appendElement(obj.options.snippet);
    }

    // Default toolbar
    if (obj.options.toolbar == null) {
        obj.options.toolbar = jSuites.editor.getDefaultToolbar();
    }

    // Add toolbar
    if (obj.options.toolbar) {
        for (var i = 0; i < obj.options.toolbar.length; i++) {
            if (obj.options.toolbar[i].icon) {
                var item = document.createElement('div');
                item.style.userSelect = 'none';
                var itemIcon = document.createElement('i');
                itemIcon.className = 'material-icons';
                itemIcon.innerHTML = obj.options.toolbar[i].icon;
                itemIcon.onclick = (function (a) {
                    var b = a;
                    return function () {
                        obj.options.toolbar[b].onclick(el, obj, this)
                    };
                })(i);
                item.appendChild(itemIcon);
                toolbar.appendChild(item);
            } else {
                if (obj.options.toolbar[i].type == 'divisor') {
                    var item = document.createElement('div');
                    item.className = 'jeditor-toolbar-divisor';
                    toolbar.appendChild(item);
                } else if (obj.options.toolbar[i].type == 'button') {
                    var item = document.createElement('div');
                    item.classList.add('jeditor-toolbar-button');
                    item.innerHTML = obj.options.toolbar[i].value;
                    toolbar.appendChild(item);
                }
            }
        }

        el.appendChild(toolbar);
    }

    // Focus to the editor
    if (obj.options.focus) {
        jSuites.editor.setCursor(editor, obj.options.focus == 'initial' ? true : false);
    }

    el.editor = obj;

    return obj;
});

jSuites.editor.setCursor = function(element, first) {
    element.focus();
    document.execCommand('selectAll');
    var sel = window.getSelection();
    var range = sel.getRangeAt(0);
    if (first == true) {
        var node = range.startContainer;
        var size = 0;
    } else {
        var node = range.endContainer;
        var size = node.length;
    }
    range.setStart(node, size);
    range.setEnd(node, size);
    sel.removeAllRanges();
    sel.addRange(range);
}

jSuites.editor.getDomain = function(url) {
    return url.replace('http://','').replace('https://','').replace('www.','').split(/[/?#]/)[0].split(/:/g)[0];
}

jSuites.editor.detectUrl = function(text) {
    var expression = /(((https?:\/\/)|(www\.))[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|]+)/ig;
    var links = text.match(expression);

    if (links) {
        if (links[0].substr(0,3) == 'www') {
            links[0] = 'http://' + links[0];
        }
    }

    return links;
}

jSuites.editor.youtubeParser = function(url) {
    var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?]*).*/;
    var match = url.match(regExp);

    return (match && match[7].length == 11) ? match[7] : false;
}

jSuites.editor.getDefaultToolbar = function() { 
    return [
        {
            icon:'undo',
            onclick: function() {
                document.execCommand('undo');
            }
        },
        {
            icon:'redo',
            onclick: function() {
                document.execCommand('redo');
            }
        },
        {
            type:'divisor'
        },
        {
            icon:'format_bold',
            onclick: function(a,b,c) {
                document.execCommand('bold');

                if (document.queryCommandState("bold")) {
                    c.classList.add('selected');
                } else {
                    c.classList.remove('selected');
                }
            }
        },
        {
            icon:'format_italic',
            onclick: function(a,b,c) {
                document.execCommand('italic');

                if (document.queryCommandState("italic")) {
                    c.classList.add('selected');
                } else {
                    c.classList.remove('selected');
                }
            }
        },
        {
            icon:'format_underline',
            onclick: function(a,b,c) {
                document.execCommand('underline');

                if (document.queryCommandState("underline")) {
                    c.classList.add('selected');
                } else {
                    c.classList.remove('selected');
                }
            }
        },
        {
            type:'divisor'
        },
        {
            icon:'format_list_bulleted',
            onclick: function(a,b,c) {
                document.execCommand('insertUnorderedList');

                if (document.queryCommandState("insertUnorderedList")) {
                    c.classList.add('selected');
                } else {
                    c.classList.remove('selected');
                }
            }
        },
        {
            icon:'format_list_numbered',
            onclick: function(a,b,c) {
                document.execCommand('insertOrderedList');

                if (document.queryCommandState("insertOrderedList")) {
                    c.classList.add('selected');
                } else {
                    c.classList.remove('selected');
                }
            }
        },
        {
            icon:'format_indent_increase',
            onclick: function(a,b,c) {
                document.execCommand('indent', true, null);

                if (document.queryCommandState("indent")) {
                    c.classList.add('selected');
                } else {
                    c.classList.remove('selected');
                }
            }
        },
        {
            icon:'format_indent_decrease',
            onclick: function(a,b,c) {
                document.execCommand('outdent');

                if (document.queryCommandState("outdent")) {
                    c.classList.add('selected');
                } else {
                    c.classList.remove('selected');
                }
            }
        }
        /*{
            type:'select',
            items: ['Verdana','Arial','Courier New'],
            onchange: function() {
            }
        },
        {
            type:'select',
            items: ['10px','12px','14px','16px','18px','20px','22px'],
            onchange: function() {
            }
        },
        {
            icon:'format_align_left',
            onclick: function() {
                document.execCommand('JustifyLeft');

                if (document.queryCommandState("JustifyLeft")) {
                    this.classList.add('selected');
                } else {
                    this.classList.remove('selected');
                }
            }
        },
        {
            icon:'format_align_center',
            onclick: function() {
                document.execCommand('justifyCenter');

                if (document.queryCommandState("justifyCenter")) {
                    this.classList.add('selected');
                } else {
                    this.classList.remove('selected');
                }
            }
        },
        {
            icon:'format_align_right',
            onclick: function() {
                document.execCommand('justifyRight');

                if (document.queryCommandState("justifyRight")) {
                    this.classList.add('selected');
                } else {
                    this.classList.remove('selected');
                }
            }
        },
        {
            icon:'format_align_justify',
            onclick: function() {
                document.execCommand('justifyFull');

                if (document.queryCommandState("justifyFull")) {
                    this.classList.add('selected');
                } else {
                    this.classList.remove('selected');
                }
            }
        },
        {
            icon:'format_list_bulleted',
            onclick: function() {
                document.execCommand('insertUnorderedList');

                if (document.queryCommandState("insertUnorderedList")) {
                    this.classList.add('selected');
                } else {
                    this.classList.remove('selected');
                }
            }
        }*/
    ];
}


jSuites.image = (function(el, options) {
    var obj = {};
    obj.options = {};

    // Default configuration
    var defaults = {
        minWidth: false,
        onchange: null,
        singleFile: true,
        remoteParser: null,
        text:{
            extensionNotAllowed:'The extension is not allowed',
            imageTooSmall:'The resolution is too low, try a image with a better resolution. width > 800px',
        }
    };

    // Loop through our object
    for (var property in defaults) {
        if (options && options.hasOwnProperty(property)) {
            obj.options[property] = options[property];
        } else {
            obj.options[property] = defaults[property];
        }
    }

    // Upload icon
    el.classList.add('jupload');

    // Add image
    obj.addImage = function(file) {
        if (! file.date) {
            file.date = '';
        }
        var img = document.createElement('img');
        img.setAttribute('data-date', file.lastmodified ? file.lastmodified : file.date);
        img.setAttribute('data-name', file.name);
        img.setAttribute('data-size', file.size);
        img.setAttribute('data-small', file.small ? file.small : '');
        img.setAttribute('data-cover', file.cover ? 1 : 0);
        img.setAttribute('data-extension', file.extension);
        img.setAttribute('src', file.file);
        img.className = 'jfile';
        img.style.width = '100%';

        return img;
    }

    // Add image
    obj.addImages = function(files) {
        if (obj.options.singleFile == true) {
            el.innerHTML = '';
        }

        for (var i = 0; i < files.length; i++) {
            el.appendChild(obj.addImage(files[i]));
        }
    }

    obj.addFromFile = function(file) {
        var type = file.type.split('/');
        if (type[0] == 'image') {
            if (obj.options.singleFile == true) {
                el.innerHTML = '';
            }

            var imageFile = new FileReader();
            imageFile.addEventListener("load", function (v) {

                var img = new Image();

                img.onload = function onload() {
                    var canvas = document.createElement('canvas');
                    canvas.width = img.width;
                    canvas.height = img.height;

                    var ctx = canvas.getContext('2d');
                    ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

                    var data = {
                        file: canvas.toDataURL(),
                        extension: file.name.substr(file.name.lastIndexOf('.') + 1),
                        name: file.name,
                        size: file.size,
                        lastmodified: file.lastModified,
                    }
                    var newImage = obj.addImage(data);
                    el.appendChild(newImage);

                    // Onchange
                    if (typeof(obj.options.onchange) == 'function') {
                        obj.options.onchange(newImage);
                    }
                };

                img.src = v.srcElement.result;
            });

            imageFile.readAsDataURL(file);
        } else {
            alert(text.extentionNotAllowed);
        }
    }

    obj.addFromUrl = function(src) {
        if (src.substr(0,4) != 'data' && ! obj.options.remoteParser) {
            console.error('remoteParser not defined in your initialization');
        } else {
            // This is to process cross domain images
            if (src.substr(0,4) == 'data') {
                var extension = src.split(';')
                extension = extension[0].split('/');
                extension = extension[1];
            } else {
                var extension = src.substr(src.lastIndexOf('.') + 1);
                // Work for cross browsers
                src = obj.options.remoteParser + src;
            }

            var img = new Image();

            img.onload = function onload() {
                var canvas = document.createElement('canvas');
                canvas.width = img.width;
                canvas.height = img.height;

                var ctx = canvas.getContext('2d');
                ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

                canvas.toBlob(function(blob) {
                    var data = {
                        file: window.URL.createObjectURL(blob),
                        extension: extension
                    }
                    var newImage = obj.addImage(data);
                    el.appendChild(newImage);

                    // Keep base64 ready to go
                    var content = canvas.toDataURL();
                    jSuites.files[data.file] = content.substr(content.indexOf(',') + 1);

                    // Onchange
                    if (typeof(obj.options.onchange) == 'function') {
                        obj.options.onchange(newImage);
                    }
                });
            };

            img.src = src;
        }
    }

    var attachmentInput = document.createElement('input');
    attachmentInput.type = 'file';
    attachmentInput.setAttribute('accept', 'image/*');
    attachmentInput.onchange = function() {
        for (var i = 0; i < this.files.length; i++) {
            obj.addFromFile(this.files[i]);
        }
    }

    el.addEventListener("dblclick", function(e) {
        jSuites.click(attachmentInput);
    });

    el.addEventListener('dragenter', function(e) {
        el.style.border = '1px dashed #000';
    });

    el.addEventListener('dragleave', function(e) {
        el.style.border = '1px solid #eee';
    });

    el.addEventListener('dragstop', function(e) {
        el.style.border = '1px solid #eee';
    });

    el.addEventListener('dragover', function(e) {
        e.preventDefault();
    });

    el.addEventListener('drop', function(e) {
        e.preventDefault();  
        e.stopPropagation();


        var html = (e.originalEvent || e).dataTransfer.getData('text/html');
        var file = (e.originalEvent || e).dataTransfer.files;

        if (file.length) {
            for (var i = 0; i < e.dataTransfer.files.length; i++) {
                obj.addFromFile(e.dataTransfer.files[i]);
            }
        } else if (html) {
            if (obj.options.singleFile == true) {
                el.innerHTML = '';
            }

            // Create temp element
            var div = document.createElement('div');
            div.innerHTML = html;

            // Extract images
            var img = div.querySelectorAll('img');

            if (img.length) {
                for (var i = 0; i < img.length; i++) {
                    obj.addFromUrl(img[i].src);
                }
            }
        }

        el.style.border = '1px solid #eee';

        return false;
    });

    el.image = obj;

    return obj;
});

/**
 * (c) jLoading
 * https://github.com/paulhodel/jtools
 *
 * @author: Paul Hodel <paul.hodel@gmail.com>
 * @description: Page loading spin
 */

jSuites.loading = (function() {
    var obj = {};

    var loading = document.createElement('div');
    loading.className = 'jloading';

    obj.show = function() {
        document.body.appendChild(loading);
    };

    obj.hide = function() {
        document.body.removeChild(loading);
    };

    return obj;
})();

/**
 * (c) jLogin
 * https://github.com/paulhodel/jtools
 *
 * @author: Paul Hodel <paul.hodel@gmail.com>
 * @description: Login helper
 */

jSuites.login = (function(el, options) {
    var obj = {};
    obj.options = {};

    // Default configuration
    var defaults = {
        url: window.location.href,
        prepareRequest: null,
        accessToken: null,
        deviceToken: null,
        facebookUrl: null,
        facebookAuthentication: null,
        maxHeight: null,
        onload: null,
        onsuccess: null,
        onerror: null,
        message: null,
        logo: null,
        newProfile: false,
        newProfileUrl: false,
        newProfileLogin: false,
        fullscreen: false,
        newPasswordValidation: null,
    };

    // Loop through our object
    for (var property in defaults) {
        if (options && options.hasOwnProperty(property)) {
            obj.options[property] = options[property];
        } else {
            obj.options[property] = defaults[property];
        }
    }

    // Message console container
    if (! obj.options.message) {
        var messageElement = document.querySelector('.message');
        if (messageElement) {
            obj.options.message = messageElement;
        }
    }

    // Action
    var action = null;

    // Container
    var container = document.createElement('form');
    el.appendChild(container);

    // Logo
    var divLogo = document.createElement('div');
    divLogo.className = 'jlogin-logo'
    container.appendChild(divLogo);

    if (obj.options.logo) {
        var logo = document.createElement('img');
        logo.src = obj.options.logo;
        divLogo.appendChild(logo);
    }

    // Code
    var labelCode = document.createElement('label');
    labelCode.innerHTML = 'Please enter here the code received';
    var inputCode = document.createElement('input');
    inputCode.type = 'number';
    inputCode.id = 'code';
    inputCode.setAttribute('maxlength', 6);
    var divCode = document.createElement('div');
    divCode.appendChild(labelCode);
    divCode.appendChild(inputCode);

    // Hash
    var inputHash = document.createElement('input');
    inputHash.type = 'hidden';
    inputHash.name = 'h';
    var divHash = document.createElement('div');
    divHash.appendChild(inputHash);

    // Recovery
    var inputRecovery = document.createElement('input');
    inputRecovery.type = 'hidden';
    inputRecovery.name = 'recovery';
    inputRecovery.value = '1';
    var divRecovery = document.createElement('div');
    divRecovery.appendChild(inputRecovery);

    // Login
    var labelLogin = document.createElement('label');
    labelLogin.innerHTML = 'Login';
    var inputLogin = document.createElement('input');
    inputLogin.type = 'text';
    inputLogin.name = 'login';
    inputLogin.setAttribute('autocomplete', 'off');
    inputLogin.onkeyup = function() {
        this.value = this.value.toLowerCase().replace(/[^a-zA-Z0-9_+]+/gi, '');
    } 
    var divLogin = document.createElement('div');
    divLogin.appendChild(labelLogin);
    divLogin.appendChild(inputLogin);

    // Name
    var labelName = document.createElement('label');
    labelName.innerHTML = 'Name';
    var inputName = document.createElement('input');
    inputName.type = 'text';
    inputName.name = 'name';
    var divName = document.createElement('div');
    divName.appendChild(labelName);
    divName.appendChild(inputName);

    // Email
    var labelUsername = document.createElement('label');
    labelUsername.innerHTML = 'E-mail';
    var inputUsername = document.createElement('input');
    inputUsername.type = 'text';
    inputUsername.name = 'username';
    inputUsername.setAttribute('autocomplete', 'new-username');
    var divUsername = document.createElement('div');
    divUsername.appendChild(labelUsername);
    divUsername.appendChild(inputUsername);

    // Password
    var labelPassword = document.createElement('label');
    labelPassword.innerHTML = 'New password';
    var inputPassword = document.createElement('input');
    inputPassword.type = 'password';
    inputPassword.name = 'password';
    inputPassword.setAttribute('autocomplete', 'new-password');
    var divPassword = document.createElement('div');
    divPassword.appendChild(labelPassword);
    divPassword.appendChild(inputPassword);
    divPassword.onkeydown = function(e) {
        if (e.keyCode == 13) {
            obj.execute();
        }
    }

    // Repeat password
    var labelRepeatPassword = document.createElement('label');
    labelRepeatPassword.innerHTML = 'Repeat the new password';
    var inputRepeatPassword = document.createElement('input');
    inputRepeatPassword.type = 'password';
    inputRepeatPassword.name = 'password';
    var divRepeatPassword = document.createElement('div');
    divRepeatPassword.appendChild(labelRepeatPassword);
    divRepeatPassword.appendChild(inputRepeatPassword);

    // Remember checkbox
    var labelRemember = document.createElement('label');
    labelRemember.innerHTML = 'Remember me on this device';
    var inputRemember = document.createElement('input');
    inputRemember.type = 'checkbox';
    inputRemember.name = 'remember';
    inputRemember.value = '1';
    labelRemember.appendChild(inputRemember);
    var divRememberButton = document.createElement('div');
    divRememberButton.className = 'rememberButton';
    divRememberButton.appendChild(labelRemember);

    // Login button
    var actionButton = document.createElement('input');
    actionButton.type = 'button';
    actionButton.value = 'Log In';
    actionButton.onclick = function() {
        obj.execute();
    }
    var divActionButton = document.createElement('div');
    divActionButton.appendChild(actionButton);

    // Cancel button
    var cancelButton = document.createElement('div');
    cancelButton.innerHTML = 'Cancel';
    cancelButton.className = 'cancelButton';
    cancelButton.onclick = function() {
        obj.requestAccess();
    }
    var divCancelButton = document.createElement('div');
    divCancelButton.appendChild(cancelButton);

    // Captcha
    var labelCaptcha = document.createElement('label');
    labelCaptcha.innerHTML = 'Please type here the code below';
    var inputCaptcha = document.createElement('input');
    inputCaptcha.type = 'text';
    inputCaptcha.name = 'captcha';
    var imageCaptcha = document.createElement('img');
    var divCaptcha = document.createElement('div');
    divCaptcha.className = 'jlogin-captcha';
    divCaptcha.appendChild(labelCaptcha);
    divCaptcha.appendChild(inputCaptcha);
    divCaptcha.appendChild(imageCaptcha);

    // Facebook
    var facebookButton = document.createElement('div');
    facebookButton.innerHTML = 'Login with Facebook';
    facebookButton.className = 'facebookButton';
    var divFacebookButton = document.createElement('div');
    divFacebookButton.appendChild(facebookButton);
    divFacebookButton.onclick = function() {
        obj.requestLoginViaFacebook();
    }
    // Forgot password
    var inputRequest = document.createElement('span');
    inputRequest.innerHTML = 'Request a new password';
    var divRequestButton = document.createElement('div');
    divRequestButton.className = 'requestButton';
    divRequestButton.appendChild(inputRequest);
    divRequestButton.onclick = function() {
        obj.requestNewPassword();
    }
    // Create a new Profile
    var inputNewProfile = document.createElement('span');
    inputNewProfile.innerHTML = 'Create a new profile';
    var divNewProfileButton = document.createElement('div');
    divNewProfileButton.className = 'newProfileButton';
    divNewProfileButton.appendChild(inputNewProfile);
    divNewProfileButton.onclick = function() {
        obj.newProfile();
    }

    el.className = 'jlogin';

    if (obj.options.fullscreen == true) {
        el.classList.add('jlogin-fullscreen');
    }

    /** 
     * Show message
     */
    obj.showMessage = function(data) {
        var message = (typeof(data) == 'object') ? data.message : data;

        if (typeof(obj.options.showMessage) == 'function') {
            obj.options.showMessage(data);
        } else {
            jSuites.alert(data);
        }
    }

    /**
     * New profile
     */
    obj.newProfile = function() {
        container.innerHTML = '';
        container.appendChild(divLogo);
        if (obj.options.newProfileLogin) {
            container.appendChild(divLogin);
        }
        container.appendChild(divName);
        container.appendChild(divUsername);
        container.appendChild(divActionButton);
        if (obj.options.facebookAuthentication == true) {
            container.appendChild(divFacebookButton);
        }
        container.appendChild(divCancelButton);

        // Reset inputs
        inputLogin.value = '';
        inputUsername.value = '';
        inputPassword.value = '';

        // Button
        actionButton.value = 'Create new profile';

        // Action
        action = 'newProfile';
    }

    /**
     * Request the email with the recovery instructions
     */
    obj.requestNewPassword = function() {
        if (Array.prototype.indexOf.call(container.children, divCaptcha) >= 0) {
            var captcha = true;
        }

        container.innerHTML = '';
        container.appendChild(divLogo);
        container.appendChild(divRecovery);
        container.appendChild(divUsername);
        if (captcha) {
            container.appendChild(divCaptcha);
        }
        container.appendChild(divActionButton);
        container.appendChild(divCancelButton);
        actionButton.value = 'Request a new password';
        inputRecovery.value = 1;

        // Action
        action = 'requestNewPassword';
    }

    /**
     * Confirm recovery code
     */
    obj.codeConfirmation = function() {
        container.innerHTML = '';
        container.appendChild(divLogo);
        container.appendChild(divHash);
        container.appendChild(divCode);
        container.appendChild(divActionButton);
        container.appendChild(divCancelButton);
        actionButton.value = 'Confirm code';
        inputRecovery.value = 2;

        // Action
        action = 'codeConfirmation';
    }

    /**
     * Update my password
     */
    obj.changeMyPassword = function(hash) {
        container.innerHTML = '';
        container.appendChild(divLogo);
        container.appendChild(divHash);
        container.appendChild(divPassword);
        container.appendChild(divRepeatPassword);
        container.appendChild(divActionButton);
        container.appendChild(divCancelButton);
        actionButton.value = 'Change my password';
        inputHash.value = hash;

        // Action
        action = 'changeMyPassword';
    }

    /**
     * Request access default method
     */
    obj.requestAccess = function() {
        container.innerHTML = '';
        container.appendChild(divLogo);
        container.appendChild(divUsername);
        container.appendChild(divPassword);
        container.appendChild(divActionButton);
        if (obj.options.facebookAuthentication == true) {
            container.appendChild(divFacebookButton);
        }
        container.appendChild(divRequestButton);
        container.appendChild(divRememberButton);
        container.appendChild(divRequestButton);
        if (obj.options.newProfile == true) {
            container.appendChild(divNewProfileButton);
        }

        // Button
        actionButton.value = 'Login';

        // Password
        inputPassword.value = '';

        // Email persistence
        if (window.localStorage.getItem('username')) {
            inputUsername.value = window.localStorage.getItem('username');
            inputPassword.focus();
        } else {
            inputUsername.focus();
        }

        // Action
        action = 'requestAccess';
    }

    /**
     * Request login via facebook
     */
    obj.requestLoginViaFacebook = function() {
        if (typeof(deviceNotificationToken) == 'undefined') {
            FB.getLoginStatus(function(response) {
                if (! response.status || response.status != 'connected') {
                    FB.login(function(response) {
                        if (response.authResponse) {
                            obj.execute({ f:response.authResponse.accessToken });
                        } else {
                            obj.showMessage('Not authorized by facebook');
                        }
                    }, {scope: 'public_profile,email'});
                } else {
                    obj.execute({ f:response.authResponse.accessToken });
                }
            }, true);
        } else {
            jDestroy = function() {
                fbLogin.removeEventListener('loadstart', jStart);
                fbLogin.removeEventListener('loaderror', jError);
                fbLogin.removeEventListener('exit', jExit);
                fbLogin.close();
                fbLogin = null;
            }

            jStart = function(event) {
                var url = event.url;
                if (url.indexOf("access_token") >= 0) {
                    setTimeout(function(){
                        var u = url.match(/=(.*?)&/);
                        if (u[1].length > 32) {
                            obj.execute({ f:u[1] });
                        }
                        jDestroy();
                   },500);
                }

                if (url.indexOf("error=access_denied") >= 0) {
                   setTimeout(jDestroy ,500);
                   // Not authorized by facebook
                   obj.showMessage('Not authorized by facebook');
                }
            }

            jError = function(event) {
                jDestroy();
            }
        
            jExit = function(event) {
                jDestroy();
            }

            fbLogin = window.open(obj.options.facebookUrl, "_blank", "location=no,closebuttoncaption=Exit,disallowoverscroll=yes,toolbar=no");
            fbLogin.addEventListener('loadstart', jStart);
            fbLogin.addEventListener('loaderror', jError);
            fbLogin.addEventListener('exit', jExit);
        }

        // Action
        action = 'requestLoginViaFacebook';
    }

    // Perform request
    obj.execute = function(data) {
        // New profile
        if (action == 'newProfile') {
            var pattern = new RegExp(/^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/);
            if (! inputUsername.value || ! pattern.test(inputUsername.value)) {
                var message = 'Invalid e-mail address'; 
            }

            var pattern = new RegExp(/^[a-zA-Z0-9\_\-\.\s+]+$/);
            if (! inputLogin.value || ! pattern.test(inputLogin.value)) {
                var message = 'Invalid username, please use only characters and numbers';
            }

            if (message) {
                obj.showMessage(message);
                return false;
            }
        } else if (action == 'changeMyPassword') {
            if (inputPassword.value.length < 3) {
                var message = 'Password is too short';
            } else  if (inputPassword.value != inputRepeatPassword.value) {
                var message = 'Password should match';
            } else {
                if (typeof(obj.options.newPasswordValidation) == 'function') {
                    var val = obj.options.newPasswordValidation(obj, inputPassword.value, inputPassword.value);
                    if (val != undefined) {
                        message = val;
                    }
                }
            }

            if (message) {
                obj.showMessage(message);
                return false;
            }
        }

        // Keep email
        if (inputUsername.value != '') {
            window.localStorage.setItem('username', inputUsername.value);
        }

        // Captcha
        if (Array.prototype.indexOf.call(container.children, divCaptcha) >= 0) {
            if (inputCaptcha.value == '') {
                obj.showMessage('Please enter the captch code below');
                return false;
            }
        }

        // Url
        var url = obj.options.url;

        // Device token
        if (obj.options.deviceToken) {
            url += '?token=' + obj.options.deviceToken;
        }

        // Callback
        var onsuccess = function(result) {
            if (result) {
                // Successfully response
                if (result.success == 1) {
                    // Recovery process
                    if (action == 'requestNewPassword') {
                        obj.codeConfirmation();
                    } else if (action == 'codeConfirmation') {
                        obj.requestAccess();
                    } else if (action == 'newProfile') {
                        obj.requestAccess();
                        // New profile
                        result.newProfile = true;
                    }

                    // Token
                    if (result.token) {
                        // Set token
                        obj.options.accessToken = result.token;
                        // Save token
                        window.localStorage.setItem('Access-Token', result.token);
                    }
                }

                // Show message
                if (result.message) {
                    // Show message
                    obj.showMessage(result.message)
                }

                // Request captcha code
                if (! result.data) {
                    if (Array.prototype.indexOf.call(container.children, divCaptcha) >= 0) {
                        divCaptcha.remove();
                    }
                } else {
                    container.insertBefore(divCaptcha, divActionButton);
                    imageCaptcha.setAttribute('src', 'data:image/png;base64,' + result.data);
                }

                // Give time to user see the message
                if (result.hash) {
                    // Change password
                    obj.changeMyPassword(result.hash);
                } else if (result.url) {
                    // App initialization
                    if (result.success == 1) {
                        if (typeof(obj.options.onsuccess) == 'function') {
                            obj.options.onsuccess(result);
                        } else {
                            if (result.message) {
                                setTimeout(function() { window.location.href = result.url; }, 2000);
                            } else {
                                window.location.href = result.url;
                            }
                        }
                    } else {
                        if (typeof(obj.options.onerror) == 'function') {
                            obj.options.onerror(result);
                        }
                    }
                }
            }
        }

        // Password
        if (! data) {
            var data = jSuites.getFormElements(el);
            // Encode passworfd
            if (data.password) {
                data.password = jSuites.login.sha512(data.password);
            }
            // Recovery code
            if (Array.prototype.indexOf.call(container.children, divCode) >= 0 && inputCode.value) {
                data.h = jSuites.login.sha512(inputCode.value);
            }
        }

        // Loading
        el.classList.add('jlogin-loading');

        // Url
        var url = (action == 'newProfile' && obj.options.newProfileUrl) ? obj.options.newProfileUrl : obj.options.url;

        // Remote call
        jSuites.ajax({
            url: url,
            method: 'POST',
            dataType: 'json',
            data: data,
            success: function(result) {
                // Remove loading
                el.classList.remove('jlogin-loading');
                // Callback
                onsuccess(result);
            },
            error: function(result) {
                // Error
                el.classList.remove('jlogin-loading');

                if (typeof(obj.options.onerror) == 'function') {
                    obj.options.onerror(result);
                }
            }
        });
    }

    var queryString = window.location.href.split('?');
    if (queryString[1] && queryString[1].length == 130 && queryString[1].substr(0,2) == 'h=') {
        obj.changeMyPassword(queryString[1].substr(2));
    } else {
        obj.requestAccess();
    }

    return obj;
});

jSuites.login.sha512 = (function(str) {
    function int64(msint_32, lsint_32) {
        this.highOrder = msint_32;
        this.lowOrder = lsint_32;
    }

    var H = [new int64(0x6a09e667, 0xf3bcc908), new int64(0xbb67ae85, 0x84caa73b),
        new int64(0x3c6ef372, 0xfe94f82b), new int64(0xa54ff53a, 0x5f1d36f1),
        new int64(0x510e527f, 0xade682d1), new int64(0x9b05688c, 0x2b3e6c1f),
        new int64(0x1f83d9ab, 0xfb41bd6b), new int64(0x5be0cd19, 0x137e2179)];

    var K = [new int64(0x428a2f98, 0xd728ae22), new int64(0x71374491, 0x23ef65cd),
        new int64(0xb5c0fbcf, 0xec4d3b2f), new int64(0xe9b5dba5, 0x8189dbbc),
        new int64(0x3956c25b, 0xf348b538), new int64(0x59f111f1, 0xb605d019),
        new int64(0x923f82a4, 0xaf194f9b), new int64(0xab1c5ed5, 0xda6d8118),
        new int64(0xd807aa98, 0xa3030242), new int64(0x12835b01, 0x45706fbe),
        new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, 0xd5ffb4e2),
        new int64(0x72be5d74, 0xf27b896f), new int64(0x80deb1fe, 0x3b1696b1),
        new int64(0x9bdc06a7, 0x25c71235), new int64(0xc19bf174, 0xcf692694),
        new int64(0xe49b69c1, 0x9ef14ad2), new int64(0xefbe4786, 0x384f25e3),
        new int64(0x0fc19dc6, 0x8b8cd5b5), new int64(0x240ca1cc, 0x77ac9c65),
        new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483),
        new int64(0x5cb0a9dc, 0xbd41fbd4), new int64(0x76f988da, 0x831153b5),
        new int64(0x983e5152, 0xee66dfab), new int64(0xa831c66d, 0x2db43210),
        new int64(0xb00327c8, 0x98fb213f), new int64(0xbf597fc7, 0xbeef0ee4),
        new int64(0xc6e00bf3, 0x3da88fc2), new int64(0xd5a79147, 0x930aa725),
        new int64(0x06ca6351, 0xe003826f), new int64(0x14292967, 0x0a0e6e70),
        new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926),
        new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, 0x9d95b3df),
        new int64(0x650a7354, 0x8baf63de), new int64(0x766a0abb, 0x3c77b2a8),
        new int64(0x81c2c92e, 0x47edaee6), new int64(0x92722c85, 0x1482353b),
        new int64(0xa2bfe8a1, 0x4cf10364), new int64(0xa81a664b, 0xbc423001),
        new int64(0xc24b8b70, 0xd0f89791), new int64(0xc76c51a3, 0x0654be30),
        new int64(0xd192e819, 0xd6ef5218), new int64(0xd6990624, 0x5565a910),
        new int64(0xf40e3585, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8),
        new int64(0x19a4c116, 0xb8d2d0c8), new int64(0x1e376c08, 0x5141ab53),
        new int64(0x2748774c, 0xdf8eeb99), new int64(0x34b0bcb5, 0xe19b48a8),
        new int64(0x391c0cb3, 0xc5c95a63), new int64(0x4ed8aa4a, 0xe3418acb),
        new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, 0xd6b2b8a3),
        new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60),
        new int64(0x84c87814, 0xa1f0ab72), new int64(0x8cc70208, 0x1a6439ec),
        new int64(0x90befffa, 0x23631e28), new int64(0xa4506ceb, 0xde82bde9),
        new int64(0xbef9a3f7, 0xb2c67915), new int64(0xc67178f2, 0xe372532b),
        new int64(0xca273ece, 0xea26619c), new int64(0xd186b8c7, 0x21c0c207),
        new int64(0xeada7dd6, 0xcde0eb1e), new int64(0xf57d4f7f, 0xee6ed178),
        new int64(0x06f067aa, 0x72176fba), new int64(0x0a637dc5, 0xa2c898a6),
        new int64(0x113f9804, 0xbef90dae), new int64(0x1b710b35, 0x131c471b),
        new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493),
        new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, 0x9c100d4c),
        new int64(0x4cc5d4be, 0xcb3e42b6), new int64(0x597f299c, 0xfc657e2a),
        new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817)];

    var W = new Array(64);
    var a, b, c, d, e, f, g, h, i, j;
    var T1, T2;
    var charsize = 8;

    function utf8_encode(str) {
        return unescape(encodeURIComponent(str));
    }

    function str2binb(str) {
        var bin = [];
        var mask = (1 << charsize) - 1;
        var len = str.length * charsize;
    
        for (var i = 0; i < len; i += charsize) {
            bin[i >> 5] |= (str.charCodeAt(i / charsize) & mask) << (32 - charsize - (i % 32));
        }
    
        return bin;
    }

    function binb2hex(binarray) {
        var hex_tab = "0123456789abcdef";
        var str = "";
        var length = binarray.length * 4;
        var srcByte;

        for (var i = 0; i < length; i += 1) {
            srcByte = binarray[i >> 2] >> ((3 - (i % 4)) * 8);
            str += hex_tab.charAt((srcByte >> 4) & 0xF) + hex_tab.charAt(srcByte & 0xF);
        }

        return str;
    }

    function safe_add_2(x, y) {
        var lsw, msw, lowOrder, highOrder;

        lsw = (x.lowOrder & 0xFFFF) + (y.lowOrder & 0xFFFF);
        msw = (x.lowOrder >>> 16) + (y.lowOrder >>> 16) + (lsw >>> 16);
        lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);

        lsw = (x.highOrder & 0xFFFF) + (y.highOrder & 0xFFFF) + (msw >>> 16);
        msw = (x.highOrder >>> 16) + (y.highOrder >>> 16) + (lsw >>> 16);
        highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);

        return new int64(highOrder, lowOrder);
    }

    function safe_add_4(a, b, c, d) {
        var lsw, msw, lowOrder, highOrder;

        lsw = (a.lowOrder & 0xFFFF) + (b.lowOrder & 0xFFFF) + (c.lowOrder & 0xFFFF) + (d.lowOrder & 0xFFFF);
        msw = (a.lowOrder >>> 16) + (b.lowOrder >>> 16) + (c.lowOrder >>> 16) + (d.lowOrder >>> 16) + (lsw >>> 16);
        lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);

        lsw = (a.highOrder & 0xFFFF) + (b.highOrder & 0xFFFF) + (c.highOrder & 0xFFFF) + (d.highOrder & 0xFFFF) + (msw >>> 16);
        msw = (a.highOrder >>> 16) + (b.highOrder >>> 16) + (c.highOrder >>> 16) + (d.highOrder >>> 16) + (lsw >>> 16);
        highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);

        return new int64(highOrder, lowOrder);
    }

    function safe_add_5(a, b, c, d, e) {
        var lsw, msw, lowOrder, highOrder;

        lsw = (a.lowOrder & 0xFFFF) + (b.lowOrder & 0xFFFF) + (c.lowOrder & 0xFFFF) + (d.lowOrder & 0xFFFF) + (e.lowOrder & 0xFFFF);
        msw = (a.lowOrder >>> 16) + (b.lowOrder >>> 16) + (c.lowOrder >>> 16) + (d.lowOrder >>> 16) + (e.lowOrder >>> 16) + (lsw >>> 16);
        lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);

        lsw = (a.highOrder & 0xFFFF) + (b.highOrder & 0xFFFF) + (c.highOrder & 0xFFFF) + (d.highOrder & 0xFFFF) + (e.highOrder & 0xFFFF) + (msw >>> 16);
        msw = (a.highOrder >>> 16) + (b.highOrder >>> 16) + (c.highOrder >>> 16) + (d.highOrder >>> 16) + (e.highOrder >>> 16) + (lsw >>> 16);
        highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);

        return new int64(highOrder, lowOrder);
    }

    function maj(x, y, z) {
        return new int64(
            (x.highOrder & y.highOrder) ^ (x.highOrder & z.highOrder) ^ (y.highOrder & z.highOrder),
            (x.lowOrder & y.lowOrder) ^ (x.lowOrder & z.lowOrder) ^ (y.lowOrder & z.lowOrder)
        );
    }

    function ch(x, y, z) {
        return new int64(
            (x.highOrder & y.highOrder) ^ (~x.highOrder & z.highOrder),
            (x.lowOrder & y.lowOrder) ^ (~x.lowOrder & z.lowOrder)
        );
    }

    function rotr(x, n) {
        if (n <= 32) {
            return new int64(
             (x.highOrder >>> n) | (x.lowOrder << (32 - n)),
             (x.lowOrder >>> n) | (x.highOrder << (32 - n))
            );
        } else {
            return new int64(
             (x.lowOrder >>> n) | (x.highOrder << (32 - n)),
             (x.highOrder >>> n) | (x.lowOrder << (32 - n))
            );
        }
    }

    function sigma0(x) {
        var rotr28 = rotr(x, 28);
        var rotr34 = rotr(x, 34);
        var rotr39 = rotr(x, 39);

        return new int64(
            rotr28.highOrder ^ rotr34.highOrder ^ rotr39.highOrder,
            rotr28.lowOrder ^ rotr34.lowOrder ^ rotr39.lowOrder
        );
    }

    function sigma1(x) {
        var rotr14 = rotr(x, 14);
        var rotr18 = rotr(x, 18);
        var rotr41 = rotr(x, 41);

        return new int64(
            rotr14.highOrder ^ rotr18.highOrder ^ rotr41.highOrder,
            rotr14.lowOrder ^ rotr18.lowOrder ^ rotr41.lowOrder
        );
    }

    function gamma0(x) {
        var rotr1 = rotr(x, 1), rotr8 = rotr(x, 8), shr7 = shr(x, 7);

        return new int64(
            rotr1.highOrder ^ rotr8.highOrder ^ shr7.highOrder,
            rotr1.lowOrder ^ rotr8.lowOrder ^ shr7.lowOrder
        );
    }

    function gamma1(x) {
        var rotr19 = rotr(x, 19);
        var rotr61 = rotr(x, 61);
        var shr6 = shr(x, 6);

        return new int64(
            rotr19.highOrder ^ rotr61.highOrder ^ shr6.highOrder,
            rotr19.lowOrder ^ rotr61.lowOrder ^ shr6.lowOrder
        );
    }

    function shr(x, n) {
        if (n <= 32) {
            return new int64(
                x.highOrder >>> n,
                x.lowOrder >>> n | (x.highOrder << (32 - n))
            );
        } else {
            return new int64(
                0,
                x.highOrder << (32 - n)
            );
        }
    }

    var str = utf8_encode(str);
    var strlen = str.length*charsize;
    str = str2binb(str);

    str[strlen >> 5] |= 0x80 << (24 - strlen % 32);
    str[(((strlen + 128) >> 10) << 5) + 31] = strlen;

    for (var i = 0; i < str.length; i += 32) {
        a = H[0];
        b = H[1];
        c = H[2];
        d = H[3];
        e = H[4];
        f = H[5];
        g = H[6];
        h = H[7];

        for (var j = 0; j < 80; j++) {
            if (j < 16) {
                W[j] = new int64(str[j*2 + i], str[j*2 + i + 1]);
            } else {
                W[j] = safe_add_4(gamma1(W[j - 2]), W[j - 7], gamma0(W[j - 15]), W[j - 16]);
            }

            T1 = safe_add_5(h, sigma1(e), ch(e, f, g), K[j], W[j]);
            T2 = safe_add_2(sigma0(a), maj(a, b, c));
            h = g;
            g = f;
            f = e;
            e = safe_add_2(d, T1);
            d = c;
            c = b;
            b = a;
            a = safe_add_2(T1, T2);
        }

        H[0] = safe_add_2(a, H[0]);
        H[1] = safe_add_2(b, H[1]);
        H[2] = safe_add_2(c, H[2]);
        H[3] = safe_add_2(d, H[3]);
        H[4] = safe_add_2(e, H[4]);
        H[5] = safe_add_2(f, H[5]);
        H[6] = safe_add_2(g, H[6]);
        H[7] = safe_add_2(h, H[7]);
    }

    var binarray = [];
    for (var i = 0; i < H.length; i++) {
        binarray.push(H[i].highOrder);
        binarray.push(H[i].lowOrder);
    }

    return binb2hex(binarray);
});

/**
 * (c) jTools Input Mask
 * https://github.com/paulhodel/jtools
 *
 * @author: Paul Hodel <paul.hodel@gmail.com>
 * @description: Input mask
 */

jSuites.mask = (function() {
    var obj = {};
    var index = 0;
    var values = []
    var pieces = [];

    obj.run = function(value, mask, decimal) {
        if (value && mask) {
            if (! decimal) {
                decimal = '.';
            }
            if (value == Number(value)) {
                var number = (''+value).split('.');
                var value = number[0];
                var valueDecimal = number[1];
            } else {
                value = '' + value;
            }
            index = 0;
            values = [];
            // Create mask token
            obj.prepare(mask);
            // Current value
            var currentValue = value;
            if (currentValue) {
                // Checking current value
                for (var i = 0; i < currentValue.length; i++) {
                    if (currentValue[i] != null) {
                        obj.process(currentValue[i]);
                    }
                }
            }
            if (valueDecimal) {
                obj.process(decimal);
                var currentValue = valueDecimal;
                if (currentValue) {
                    // Checking current value
                    for (var i = 0; i < currentValue.length; i++) {
                        if (currentValue[i] != null) {
                            obj.process(currentValue[i]);
                        }
                    }
                }
            }
            // Formatted value
            return values.join('');
        } else {
            return '';
        }
    }

    obj.apply = function(e) {
        if (e.target && ! e.target.getAttribute('readonly')) {
            var mask = e.target.getAttribute('data-mask');
            if (mask && e.keyCode > 46) {
                index = 0;
                values = [];
                // Create mask token
                obj.prepare(mask);
                // Current value
                if (e.target.selectionStart < e.target.selectionEnd) {
                    var currentValue = e.target.value.substring(0, e.target.selectionStart); 
                } else {
                    var currentValue = e.target.value;
                }
                if (currentValue) {
                    // Checking current value
                    for (var i = 0; i < currentValue.length; i++) {
                        if (currentValue[i] != null) {
                            obj.process(currentValue[i]);
                        }
                    }
                }
                // New input
                obj.process(obj.fromKeyCode(e));
                // Update value to the element
                e.target.value = values.join('');
                if (pieces.length == values.length && pieces[pieces.length-1].length == values[values.length-1].length) {
                    e.target.setAttribute('data-completed', 'true');
                } else {
                    e.target.setAttribute('data-completed', 'false');
                }
                // Prevent default
                e.preventDefault();
            }
        }
    }

    /**
     * Process inputs and save to values
     */
    obj.process = function(input) {
        do {
            if (pieces[index] == 'mm') {
                if (values[index] == null || values[index] == '') {
                    if (parseInt(input) > 1 && parseInt(input) < 10) {
                        values[index] = '0' + input;
                        index++;
                        return true;
                    } else if (parseInt(input) < 10) {
                        values[index] = input;
                        return true;
                    } else {
                        return false;
                    }
                } else {
                    if (values[index] == 1 && values[index] < 2 && parseInt(input) < 3) {
                        values[index] += input;
                        index++;
                        return true;
                    } else if (values[index] == 0 && values[index] < 10) {
                        values[index] += input;
                        index++;
                        return true;
                    } else {
                        return false
                    }
                }
            } else if (pieces[index] == 'dd') {
                if (values[index] == null || values[index] == '') {
                    if (parseInt(input) > 3 && parseInt(input) < 10) {
                        values[index] = '0' + input;
                        index++;
                        return true;
                    } else if (parseInt(input) < 10) {
                        values[index] = input;
                        return true;
                    } else {
                        return false;
                    }
                } else {
                    if (values[index] == 3 && parseInt(input) < 2) {
                        values[index] += input;
                        index++;
                        return true;
                    } else if (values[index] < 3 && parseInt(input) < 10) {
                        values[index] += input;
                        index++;
                        return true;
                    } else {
                        return false
                    }
                }
            } else if (pieces[index] == 'hh24') {
                if (values[index] == null || values[index] == '') {
                    if (parseInt(input) > 2 && parseInt(input) < 10) {
                        values[index] = '0' + input;
                        index++;
                        return true;
                    } else if (parseInt(input) < 10) {
                        values[index] = input;
                        return true;
                    } else {
                        return false;
                    }
                } else {
                    if (values[index] == 2 && parseInt(input) < 4) {
                        values[index] += input;
                        index++;
                        return true;
                    } else if (values[index] < 2 && parseInt(input) < 10) {
                        values[index] += input;
                        index++;
                        return true;
                    } else {
                        return false
                    }
                }
            } else if (pieces[index] == 'hh') {
                if (values[index] == null || values[index] == '') {
                    if (parseInt(input) > 1 && parseInt(input) < 10) {
                        values[index] = '0' + input;
                        index++;
                        return true;
                    } else if (parseInt(input) < 10) {
                        values[index] = input;
                        return true;
                    } else {
                        return false;
                    }
                } else {
                    if (values[index] == 1 && parseInt(input) < 3) {
                        values[index] += input;
                        index++;
                        return true;
                    } else if (values[index] < 1 && parseInt(input) < 10) {
                        values[index] += input;
                        index++;
                        return true;
                    } else {
                        return false
                    }
                }
            } else if (pieces[index] == 'mi' || pieces[index] == 'ss') {
                if (values[index] == null || values[index] == '') {
                    if (parseInt(input) > 5 && parseInt(input) < 10) {
                        values[index] = '0' + input;
                        index++;
                        return true;
                    } else if (parseInt(input) < 10) {
                        values[index] = input;
                        return true;
                    } else {
                        return false;
                    }
                } else {
                    if (parseInt(input) < 10) {
                        values[index] += input;
                        index++;
                        return true;
                     } else {
                        return false
                    }
                }
            } else if (pieces[index] == 'yy' || pieces[index] == 'yyyy') {
                if (parseInt(input) < 10) {
                    if (values[index] == null || values[index] == '') {
                        values[index] = input;
                    } else {
                        values[index] += input;
                    }
                    
                    if (values[index].length == pieces[index].length) {
                        index++;
                    }
                    return true;
                } else {
                    return false;
                }
            } else if (pieces[index] == '#' || pieces[index] == '#.##' || pieces[index] == '#,##' || pieces[index] == '# ##') {
                if (input.match(/[0-9]/g)) {
                    if (pieces[index] == '#.##') {
                        var separator = '.';
                    } else if (pieces[index] == '#,##') {
                        var separator = ',';
                    } else if (pieces[index] == '# ##') {
                        var separator = ' ';
                    } else {
                        var separator = '';
                    }
                    if (values[index] == null || values[index] == '') {
                        values[index] = input;
                    } else {
                        values[index] += input;
                        if (separator) {
                            values[index] = values[index].match(/[0-9]/g).join('');
                            var t = [];
                            var s = 0;
                            for (var j = values[index].length - 1; j >= 0 ; j--) {
                                t.push(values[index][j]);
                                s++;
                                if (! (s % 3)) {
                                    t.push(separator);
                                }
                            }
                            t = t.reverse();
                            values[index] = t.join('');
                            if (values[index].substr(0,1) == separator) {
                                values[index] = values[index].substr(1);
                            } 
                        }
                    }
                    return true;
                } else {
                    if (pieces[index] == '#.##' && input == '.') {
                        // Do nothing
                    } else if (pieces[index] == '#,##' && input == ',') {
                        // Do nothing
                    } else if (pieces[index] == '# ##' && input == ' ') {
                        // Do nothing
                    } else {
                        if (values[index]) {
                            index++;
                            if (pieces[index]) {
                                if (pieces[index] == input) {
                                    values[index] = input;
                                    return true;
                                } else {
                                    if (pieces[index] == '0' && pieces[index+1] == input) {
                                        index++;
                                        values[index] = input;
                                        return true;
                                    }
                                }
                            }
                        }
                    }

                    return false;
                }
            } else if (pieces[index] == '0') {
                if (input.match(/[0-9]/g)) {
                    values[index] = input;
                    index++;
                    return true;
                } else {
                    return false;
                }
            } else if (pieces[index] == 'a') {
                if (input.match(/[a-zA-Z]/g)) {
                    values[index] = input;
                    index++;
                    return true;
                } else {
                    return false;
                }
            } else {
                if (pieces[index] != null) {
                    if (pieces[index] == '\\a') {
                        var v = 'a';
                    } else if (pieces[index] == '\\0') {
                        var v = '0';
                    } else if (pieces[index] == '[-]') {
                        if (input == '-' || input == '+') {
                            var v = input;
                        } else {
                            var v = ' ';
                        }
                    } else {
                        var v = pieces[index];
                    }
                    values[index] = v;
                    if (input == v) {
                        index++;
                        return true;
                    }
                }
            }

            index++;
        } while (pieces[index]);
    }

    /**
     * Create tokens for the mask
     */
    obj.prepare = function(mask) {
        pieces = [];
        for (var i = 0; i < mask.length; i++) {
            if (mask[i].match(/[0-9]|[a-z]|\\/g)) {
                if (mask[i] == 'y' && mask[i+1] == 'y' && mask[i+2] == 'y' && mask[i+3] == 'y') {
                    pieces.push('yyyy');
                    i += 3;
                } else if (mask[i] == 'y' && mask[i+1] == 'y') {
                    pieces.push('yy');
                    i++;
                } else if (mask[i] == 'm' && mask[i+1] == 'm' && mask[i+2] == 'm' && mask[i+3] == 'm') {
                    pieces.push('mmmm');
                    i += 3;
                } else if (mask[i] == 'm' && mask[i+1] == 'm' && mask[i+2] == 'm') {
                    pieces.push('mmm');
                    i += 2;
                } else if (mask[i] == 'm' && mask[i+1] == 'm') {
                    pieces.push('mm');
                    i++;
                } else if (mask[i] == 'd' && mask[i+1] == 'd') {
                    pieces.push('dd');
                    i++;
                } else if (mask[i] == 'h' && mask[i+1] == 'h' && mask[i+2] == '2' && mask[i+3] == '4') {
                    pieces.push('hh24');
                    i += 3;
                } else if (mask[i] == 'h' && mask[i+1] == 'h') {
                    pieces.push('hh');
                    i++;
                } else if (mask[i] == 'm' && mask[i+1] == 'i') {
                    pieces.push('mi');
                    i++;
                } else if (mask[i] == 's' && mask[i+1] == 's') {
                    pieces.push('ss');
                    i++;
                } else if (mask[i] == 'a' && mask[i+1] == 'm') {
                    pieces.push('am');
                    i++;
                } else if (mask[i] == 'p' && mask[i+1] == 'm') {
                    pieces.push('pm');
                    i++;
                } else if (mask[i] == '\\' && mask[i+1] == '0') {
                    pieces.push('\\0');
                    i++;
                } else if (mask[i] == '\\' && mask[i+1] == 'a') {
                    pieces.push('\\a');
                    i++;
                } else {
                    pieces.push(mask[i]);
                }
            } else {
                if (mask[i] == '#' && mask[i+1] == '.' && mask[i+2] == '#' && mask[i+3] == '#') {
                    pieces.push('#.##');
                    i += 3;
                } else if (mask[i] == '#' && mask[i+1] == ',' && mask[i+2] == '#' && mask[i+3] == '#') {
                    pieces.push('#,##');
                    i += 3;
                } else if (mask[i] == '#' && mask[i+1] == ' ' && mask[i+2] == '#' && mask[i+3] == '#') {
                    pieces.push('# ##');
                    i += 3;
                } else if (mask[i] == '[' && mask[i+1] == '-' && mask[i+2] == ']') {
                    pieces.push('[-]');
                    i += 2;
                } else {
                    pieces.push(mask[i]);
                }
            }
        }
    }

    /** 
     * Thanks for the collaboration
     */
    obj.fromKeyCode = function(e) {
        var _to_ascii = {
            '188': '44',
            '109': '45',
            '190': '46',
            '191': '47',
            '192': '96',
            '220': '92',
            '222': '39',
            '221': '93',
            '219': '91',
            '173': '45',
            '187': '61', //IE Key codes
            '186': '59', //IE Key codes
            '189': '45'  //IE Key codes
        }

        var shiftUps = {
            "96": "~",
            "49": "!",
            "50": "@",
            "51": "#",
            "52": "$",
            "53": "%",
            "54": "^",
            "55": "&",
            "56": "*",
            "57": "(",
            "48": ")",
            "45": "_",
            "61": "+",
            "91": "{",
            "93": "}",
            "92": "|",
            "59": ":",
            "39": "\"",
            "44": "<",
            "46": ">",
            "47": "?"
        };

        var c = e.which;

        if (_to_ascii.hasOwnProperty(c)) {
            c = _to_ascii[c];
        }

        if (!e.shiftKey && (c >= 65 && c <= 90)) {
            c = String.fromCharCode(c + 32);
        } else if (e.shiftKey && shiftUps.hasOwnProperty(c)) {
            c = shiftUps[c];
        } else if (96 <= c && c <= 105) {
            c = String.fromCharCode(c - 48);
        } else {
            c = String.fromCharCode(c);
        }

        return c;
    }

    return obj;
})();

jSuites.mobile = (function(el, options) {
    var obj = {};
    obj.options = {};

    if (jSuites.el) {
        jSuites.el.addEventListener('mousedown', function(e) {
            if (e.target.classList.contains('option-title')) {
                if (e.target.classList.contains('selected')) {
                    e.target.classList.remove('selected');
                } else {
                    e.target.classList.add('selected');
                }
            }
        });
    }

    return obj;
})();

jSuites.pages = (function() {
    var container = null;
    var current = null;

    // Create a page
    var createPage = function(options, callback) {
        // Create page
        var page = document.createElement('div');
        page.classList.add('page');

        // Always hidden
        page.style.display = 'none';

        // Keep options
        page.options = options ? options : {};

        if (! current) {
            container.appendChild(page);
        } else {
            container.insertBefore(page, current.nextSibling);
        }

        jSuites.ajax({
            url: page.options.url,
            method: 'GET',
            success: function(result) {
                // Push to refresh controls
                jSuites.refresh(page, page.options.onpush);

                // Open page
                page.innerHTML = result;
                // Get javascript
                var script = page.getElementsByTagName('script');
                // Run possible inline scripts
                for (var i = 0; i < script.length; i++) {
                    // Get type
                    var type = script[i].getAttribute('type');
                    if (! type || type == 'text/javascript') {
                        eval(script[i].innerHTML);
                    }
                }
                // Set title
                page.setTitle = function(text) {
                    this.children[0].children[0].children[1].innerHTML = text;
                }
                // Show page
                if (! page.options.closed) {
                    showPage(page);
                }
                // Onload callback
                if (typeof(page.options.onload) == 'function') {
                    page.options.onload(page);
                }
                // Force callback
                if (typeof(callback) == 'function') {
                    callback(page);
                }
            }
        });

        return page;
    }

    var showPage = function(page, ignoreHistory, callback) {
        if (current) {
            if (current == page) {
                current = page;
            } else {
                // Keep scroll in the top
                window.scrollTo({ top: 0 });

                // Show page
                page.style.display = '';

                var a = Array.prototype.indexOf.call(container.children, current);
                var b = Array.prototype.indexOf.call(container.children, page);

                // Leave
                if (typeof(current.options.onleave) == 'function') {
                    current.options.onleave(current, page, ignoreHistory);
                }

                jSuites.slideLeft(container, (a < b ? 0 : 1), function() {
                    current.style.display = 'none';
                    current = page;
                });

                // Enter
                if (typeof(page.options.onenter) == 'function') {
                    page.options.onenter(page, current, ignoreHistory);
                }
            }
        } else {
            // Show
            page.style.display = '';

            // Keep current
            current = page;

            // Enter
            if (typeof(page.options.onenter) == 'function') {
                page.options.onenter(page);
            }
        }

        // Add history
        if (! ignoreHistory) {
            // Add history
            window.history.pushState({ route: page.options.route }, page.options.title, page.options.route);
        }

        // Callback
        if (typeof(callback) == 'function') {
            callback(page);
        }
    }

    // Init method
    var obj = function(route, mixed) {

        // Create page container
        if (! container) {
            container = document.querySelector('.pages');
            if (! container) {
                container = document.createElement('div');
                container.className = 'pages';
            }

            // Append container to the application
            if (jSuites.el) {
                jSuites.el.appendChild(container);
            } else {
                document.body.appendChild(container);
            }
        }

        if (! obj.pages[route]) {
            if (! route) {
                alert('Error, no route provided');
            } else {
                if (typeof(mixed) == 'function') {
                    var options = {};
                    var callback = mixed;
                } else {
                    // Page options
                    var options = mixed ? mixed : {};
                }

                // Closed
                options.closed = mixed && mixed.closed ? 1 : 0;
                // Keep Route
                options.route = route;

                // New page url
                if (! options.url) {
                    var routePath = route.split('#');
                    options.url = jSuites.pages.path + routePath[0] + '.html';
                }
                // Title
                if (! options.title) {
                    options.title = 'Untitled';
                }

                // Create new page
                obj.pages[route] = createPage(options, callback ? callback : null);
            }
        } else {
            // Update config
            if (mixed) {
                // History
                var ignoreHistory = 0;

                if (typeof(mixed) == 'function') {
                    var callback = mixed;
                } else {
                    if (typeof(mixed.onenter) == 'function') {
                        obj.pages[route].options.onenter = mixed.onenter;
                    }
                    if (typeof(mixed.onleave) == 'function') {
                        obj.pages[route].options.onleave = mixed.onleave;
                    }

                    // Ignore history
                    ignoreHistory = mixed.ignoreHistory ? 1 : 0; 
                }
            }

            showPage(obj.pages[route], ignoreHistory, callback ? callback : null);
        }
    }

    obj.pages = {};

    // Get page
    obj.get = function(route) {
        if (obj.pages[route]) {
            return obj.pages[route]; 
        }
    }

    obj.getContainer = function() {
        return container;
    }

    obj.destroy = function() {
        // Current is null
        current = null;
        // Destroy containers
        obj.pages = {};
        // Reset container
        if (container) {
            container.innerHTML = '';
        }
    }

    return obj;
})();

// Path
jSuites.pages.path = 'pages';

// Panel
jSuites.panel = (function() {
    // No initial panel declared
    var panel = null;

    var obj = function(route) {
        if (! panel) {
            obj.create(jSuites.pages.path + route + '.html');
        }

        // Show panel
        panel.style.display = '';

        // Add animation
        if (panel.classList.contains('panel-left')) {
            jSuites.slideLeft(panel, 1);
        } else {
            jSuites.slideRight(panel, 1);
        }
    }

    obj.create = function(route) {
        if (! panel) {
            // Create element
            panel = document.createElement('div');
            panel.classList.add('panel');
            panel.classList.add('panel-left');
            panel.style.display = 'none';

            // Bind to the app
            if (jSuites.el) {
                jSuites.el.appendChild(panel);
            } else {
                document.body.appendChild(panel);
            }
        }

        // Remote content
        if (route) {
	        var url = jSuites.pages.path + route + '.html';

            jSuites.ajax({
                url: url,
                method: 'GET',
                success: function(result) {
                    // Set content
                    panel.innerHTML = result;
                    // Get javascript
                    var script = panel.getElementsByTagName('script');
                    // Run possible inline scripts
                    for (var i = 0; i < script.length; i++) {
                        // Get type
                        var type = script[i].getAttribute('type');
                        if (! type || type == 'text/javascript') {
                            eval(script[i].innerHTML);
                        }
                    }
                }
            });
        }
    }

    obj.close = function() {
        if (panel) {
            // Animation
            if (panel.classList.contains('panel-left')) {
                jSuites.slideLeft(panel, 0, function() {
                    panel.style.display = 'none';
                });
            } else {
                jSuites.slideRight(panel, 0, function() {
                    panel.style.display = 'none';
                });
            }
        }
    }

    obj.get = function() {
        return panel;
    }

    obj.destroy = function() {
        panel.remove();
        panel = null;
    }

    return obj;
})();

jSuites.toolbar = (function(el, options) {
    var obj = {};
    obj.options = options;

    obj.selectItem = function(element) {
        var elements = toolbarContent.children;
        for (var i = 0; i < elements.length; i++) {
            elements[i].classList.remove('selected');
        }
        element.classList.add('selected');
    }

    obj.hide = function() {
        jSuites.slideBottom(toolbar, 0, function() {
            toolbar.style.display = 'none';
        });
    }

    obj.show = function() {
        toolbar.style.display = '';
        jSuites.slideBottom(toolbar, 1);
    }

    obj.get = function() {
        return toolbar;
    }

    obj.setBadge = function(index, value) {
        toolbarContent.children[index].children[1].firstChild.innerHTML = value;
    }

    obj.destroy = function() {
        toolbar.remove();
        toolbar = null;
    }

    var toolbar = document.createElement('div');
    toolbar.classList.add('jtoolbar');
    toolbar.onclick = function(e) {
        var element = jSuites.getElement(e.target, 'jtoolbar-item');
        if (element) {
            obj.selectItem(element);
        }
    }

    var toolbarContent = document.createElement('div');
    toolbar.appendChild(toolbarContent);

    for (var i = 0; i < options.items.length; i++) {
        var toolbarItem = document.createElement('div');
        toolbarItem.classList.add('jtoolbar-item');
        if (options.items[i].route) {
            toolbarItem.setAttribute('data-href', options.items[i].route);
            jSuites.pages(options.items[i].route, {
                closed: true,
                onenter: function() {
                    obj.selectItem(toolbarItem);
                }
            });
        }

        if (options.items[i].icon) {
            var toolbarIcon = document.createElement('i');
            toolbarIcon.classList.add('material-icons');
            toolbarIcon.innerHTML = options.items[i].icon;
            toolbarItem.appendChild(toolbarIcon);
        }

        var toolbarBadge = document.createElement('div');
        toolbarBadge.classList.add('jbadge');
        var toolbarBadgeContent = document.createElement('div');
        toolbarBadgeContent.innerHTML = options.items[i].badge ? options.items[i].badge : '';
        toolbarBadge.appendChild(toolbarBadgeContent);
        toolbarItem.appendChild(toolbarBadge);

        if (options.items[i].title) {
            var toolbarTitle = document.createElement('span');
            toolbarTitle.innerHTML = options.items[i].title;
            toolbarItem.appendChild(toolbarTitle);
        }

        toolbarContent.appendChild(toolbarItem);
    }

    el.toolbar = obj;

    el.appendChild(toolbar);

    return obj;
});

jSuites.actionsheet = (function() {
    var actionsheet = document.createElement('div');
    actionsheet.className = 'jactionsheet';
    actionsheet.style.display = 'none';

    var actionContent = document.createElement('div');
    actionContent.className = 'jactionsheet-content';
    actionsheet.appendChild(actionContent);

    var obj = function(options) {
        if (options) {
            obj.options = options;
        }

        // Reset container
        actionContent.innerHTML = '';

        // Create new elements
        for (var i = 0; i < obj.options.length; i++) {
            var actionGroup = document.createElement('div');
            actionGroup.className = 'jactionsheet-group';

            for (var j = 0; j < obj.options[i].length; j++) {
                var v = obj.options[i][j];
                var actionItem = document.createElement('div');
                var actionInput = document.createElement('input');
                actionInput.type = 'button';
                actionInput.value = v.title;
                if (v.className) {
                    actionInput.className = v.className; 
                }
                if (v.onclick) {
                    actionInput.onclick = v.onclick; 
                }
                if (v.action == 'cancel') {
                    actionInput.style.color = 'red';
                }
                actionItem.appendChild(actionInput);
                actionGroup.appendChild(actionItem);
            }

            actionContent.appendChild(actionGroup);
        }

        // Show
        actionsheet.style.display = '';

        // Append
        jSuites.el.appendChild(actionsheet);

        // Animation
        jSuites.slideBottom(actionContent, true);
    }

    obj.close = function() {
        if (actionsheet.style.display != 'none') {
            // Remove any existing actionsheet
            jSuites.slideBottom(actionContent, false, function() {
                actionsheet.remove();
                actionsheet.style.display = 'none';
            });
        }
    }

    var mouseUp = function(e) {
        obj.close();
    }

    actionsheet.addEventListener('mouseup', mouseUp);

    obj.options = {};

    return obj;
})();

/**
 * (c) jSuites modal
 * https://github.com/paulhodel/jsuites
 *
 * @author: Paul Hodel <paul.hodel@gmail.com>
 * @description: Modal
 */

jSuites.modal = (function(el, options) {
    var obj = {};
    obj.options = {};

    // Default configuration
    var defaults = {
        url: null,
        onopen: null,
        onclose: null,
        closed: false,
        width: null,
        height: null,
        title: null,
    };

    // Loop through our object
    for (var property in defaults) {
        if (options && options.hasOwnProperty(property)) {
            obj.options[property] = options[property];
        } else {
            obj.options[property] = defaults[property];
        }
    }

    // Title
    if (! obj.options.title && el.getAttribute('title')) {
        obj.options.title = el.getAttribute('title');
    }

    var temp = document.createElement('div');
    for (var i = 0; i < el.children.length; i++) {
        temp.appendChild(el.children[i]);
    }

    obj.content = document.createElement('div');
    obj.content.className = 'jmodal_content';
    obj.content.innerHTML = el.innerHTML;

    for (var i = 0; i < temp.children.length; i++) {
        obj.content.appendChild(temp.children[i]);
    }

    obj.container = document.createElement('div');
    obj.container.className = 'jmodal';
    obj.container.appendChild(obj.content);

    if (obj.options.width) {
        obj.container.style.width = obj.options.width;
    }
    if (obj.options.height) {
        obj.container.style.height = obj.options.height;
    }
    if (obj.options.title) {
        obj.container.setAttribute('title', obj.options.title);
    } else {
        obj.container.classList.add('no-title');
    }
    el.innerHTML = '';
    el.style.display = 'none';
    el.appendChild(obj.container);

    // Backdrop
    var backdrop = document.createElement('div');
    backdrop.className = 'jmodal_backdrop';
    el.appendChild(backdrop);

    obj.open = function() {
        el.style.display = 'block';
        // Fullscreen
        const rect = obj.container.getBoundingClientRect();
        if (jSuites.getWindowWidth() < rect.width) {
            obj.container.style.top = '';
            obj.container.style.left = '';
            obj.container.classList.add('jmodal_fullscreen');
            jSuites.slideBottom(obj.container, 1);
        } else {
            backdrop.style.display = 'block';
        }
        // Current
        jSuites.modal.current = obj;
        // Event
        if (typeof(obj.options.onopen) == 'function') {
            obj.options.onopen(el, obj);
        }
    }

    obj.resetPosition = function() {
        obj.container.style.top = '';
        obj.container.style.left = '';
    }

    obj.isOpen = function() {
        return el.style.display != 'none' ? true : false;
    }

    obj.close = function() {
        el.style.display = 'none';
        // Backdrop
        backdrop.style.display = '';
        // Current
        jSuites.modal.current = null;
        // Remove fullscreen class
        obj.container.classList.remove('jmodal_fullscreen');
        // Event
        if (typeof(obj.options.onclose) == 'function') {
            obj.options.onclose(el, obj);
        }
    }

    if (! jSuites.modal.hasEvents) {
        jSuites.modal.current = obj;

        if ('ontouchstart' in document.documentElement === true) {
            document.addEventListener("touchstart", jSuites.modal.mouseDownControls);
        } else {
            document.addEventListener('mousedown', jSuites.modal.mouseDownControls);
            document.addEventListener('mousemove', jSuites.modal.mouseMoveControls);
            document.addEventListener('mouseup', jSuites.modal.mouseUpControls);
        }

        document.addEventListener('keydown', jSuites.modal.keyDownControls);

        jSuites.modal.hasEvents = true;
    }

    if (obj.options.url) {
        jSuites.ajax({
            url: obj.options.url,
            method: 'GET',
            success: function(data) {
                obj.content.innerHTML = data;

                if (! obj.options.closed) {
                    obj.open();
                }
            }
        });
    } else {
        if (! obj.options.closed) {
            obj.open();
        }
    }

    // Keep object available from the node
    el.modal = obj;

    return obj;
});

jSuites.modal.current = null;
jSuites.modal.position = null;

jSuites.modal.keyDownControls = function(e) {
    if (e.which == 27) {
        if (jSuites.modal.current) {
            jSuites.modal.current.close();
        }
    }
}

jSuites.modal.mouseUpControls = function(e) {
    if (jSuites.modal.current) {
        jSuites.modal.current.container.style.cursor = 'auto';
    }
    jSuites.modal.position = null;
}

jSuites.modal.mouseMoveControls = function(e) {
    if (jSuites.modal.current && jSuites.modal.position) {
        if (e.which == 1 || e.which == 3) {
            var position = jSuites.modal.position;
            jSuites.modal.current.container.style.top = (position[1] + (e.clientY - position[3]) + (position[5] / 2)) + 'px';
            jSuites.modal.current.container.style.left = (position[0] + (e.clientX - position[2]) + (position[4] / 2)) + 'px';
            jSuites.modal.current.container.style.cursor = 'move';
        } else {
            jSuites.modal.current.container.style.cursor = 'auto';
        }
    }
}

jSuites.modal.mouseDownControls = function(e) {
    jSuites.modal.position = [];

    if (e.target.classList.contains('jmodal')) {
        setTimeout(function() {
            // Get target info
            var rect = e.target.getBoundingClientRect();

            if (e.changedTouches && e.changedTouches[0]) {
                var x = e.changedTouches[0].clientX;
                var y = e.changedTouches[0].clientY;
            } else {
                var x = e.clientX;
                var y = e.clientY;
            }

            if (rect.width - (x - rect.left) < 50 && (y - rect.top) < 50) {
                setTimeout(function() {
                    jSuites.modal.current.close();
                }, 100);
            } else {
                if (e.target.getAttribute('title') && (y - rect.top) < 50) {
                    if (document.selection) {
                        document.selection.empty();
                    } else if ( window.getSelection ) {
                        window.getSelection().removeAllRanges();
                    }

                    jSuites.modal.position = [
                        rect.left,
                        rect.top,
                        e.clientX,
                        e.clientY,
                        rect.width,
                        rect.height,
                    ];
                }
            }
        }, 100);
    }
}


jSuites.notification = (function(options) {
    var obj = {};
    obj.options = {};

    // Default configuration
    var defaults = {
        icon: null,
        name: 'Notification',
        date: null,
        title: null,
        message: null,
        timeout: 4000,
        autoHide: true,
        closeable: true,
    };

    // Loop through our object
    for (var property in defaults) {
        if (options && options.hasOwnProperty(property)) {
            obj.options[property] = options[property];
        } else {
            obj.options[property] = defaults[property];
        }
    }

    var notification = document.createElement('div');
    notification.className = 'jnotification';

    var notificationContainer = document.createElement('div');
    notificationContainer.className = 'jnotification-container';
    notification.appendChild(notificationContainer);

    var notificationHeader = document.createElement('div');
    notificationHeader.className = 'jnotification-header';
    notificationContainer.appendChild(notificationHeader);

    var notificationImage = document.createElement('div');
    notificationImage.className = 'jnotification-image';
    notificationHeader.appendChild(notificationImage);

    if (obj.options.icon) {
        var notificationIcon = document.createElement('img');
        notificationIcon.src = obj.options.icon;
        notificationImage.appendChild(notificationIcon);
    }

    var notificationName = document.createElement('div');
    notificationName.className = 'jnotification-name';
    notificationName.innerHTML = obj.options.name;
    notificationHeader.appendChild(notificationName);

    if (obj.options.closeable == true) {
        var notificationClose = document.createElement('div');
        notificationClose.className = 'jnotification-close';
        notificationClose.onclick = function() {
            obj.hide();
        }
        notificationHeader.appendChild(notificationClose);
    }

    var notificationDate = document.createElement('div');
    notificationDate.className = 'jnotification-date';
    notificationHeader.appendChild(notificationDate);

    var notificationContent = document.createElement('div');
    notificationContent.className = 'jnotification-content';
    notificationContainer.appendChild(notificationContent);

    if (obj.options.title) {
        var notificationTitle = document.createElement('div');
        notificationTitle.className = 'jnotification-title';
        notificationTitle.innerHTML = obj.options.title;
        notificationContent.appendChild(notificationTitle);
    }

    var notificationMessage = document.createElement('div');
    notificationMessage.className = 'jnotification-message';
    notificationMessage.innerHTML = obj.options.message;
    notificationContent.appendChild(notificationMessage);

    obj.show = function() {
        document.body.appendChild(notification);
        if (jSuites.getWindowWidth() > 800) { 
            jSuites.fadeIn(notification);
        } else {
            jSuites.slideTop(notification, 1);
        }
    }

    obj.hide = function() {
        if (jSuites.getWindowWidth() > 800) { 
            jSuites.fadeOut(notification, function() {
                notification.parentNode.removeChild(notification);
            });
        } else {
            jSuites.slideTop(notification, 0, function() {
                notification.parentNode.removeChild(notification);
            });
        }
    };

    obj.show();

    if (obj.options.autoHide == true) {
        setTimeout(function() {
            obj.hide();
        }, obj.options.timeout);
    }

    if (jSuites.getWindowWidth() < 800) {
        notification.addEventListener("swipeup", function(e) {
            obj.hide();
            e.preventDefault();
            e.stopPropagation();
        });
    }

    return obj;
});

jSuites.rating = (function(el, options) {
    var obj = {};
    obj.options = {};

    // Default configuration
    var defaults = {
        number: 5,
        value: 0,
        tooltip: [ 'Very bad', 'Bad', 'Average', 'Good', 'Very good' ],
        onchange: null,
    };

    // Loop through the initial configuration
    for (var property in defaults) {
        if (options && options.hasOwnProperty(property)) {
            obj.options[property] = options[property];
        } else {
            obj.options[property] = defaults[property];
        }
    }

    // Class
    el.classList.add('jrating');

    // Add elements
    for (var i = 0; i < obj.options.number; i++) {
        var div = document.createElement('div');
        div.setAttribute('data-index', (i + 1))
        div.setAttribute('title', obj.options.tooltip[i])
        el.appendChild(div);
    }

    // Set value
    obj.setValue = function(index) {
        for (var i = 0; i < obj.options.number; i++) {
            if (i < index) {
                el.children[i].classList.add('jrating-selected');
            } else {
                el.children[i].classList.remove('jrating-over');
                el.children[i].classList.remove('jrating-selected');
            }
        }

        obj.options.value = index;

        if (typeof(obj.options.onchange) == 'function') {
            obj.options.onchange(el, index);
        }
    }

    obj.getValue = function() {
        return obj.options.value;
    }

    if (obj.options.value) {
        for (var i = 0; i < obj.options.number; i++) {
            if (i < obj.options.value) {
                el.children[i].classList.add('jrating-selected');
            }
        }
    }

    // Events
    el.addEventListener("click", function(e) {
        var index = e.target.getAttribute('data-index');
        if (index != undefined) {
            if (index == obj.options.value) {
                obj.setValue(0);
            } else {
                obj.setValue(index);
            }
        }
    });

    el.addEventListener("mouseover", function(e) {
        var index = e.target.getAttribute('data-index');
        for (var i = 0; i < obj.options.number; i++) {
            if (i < index) {
                el.children[i].classList.add('jrating-over');
            } else {
                el.children[i].classList.remove('jrating-over');
            }
        }
    });

    el.addEventListener("mouseout", function(e) {
        for (var i = 0; i < obj.options.number; i++) {
            el.children[i].classList.remove('jrating-over');
        }
    });

    el.rating = obj;

    return obj;
});


/**
 * (c) Image slider
 * https://github.com/paulhodel/jtools
 *
 * @author: Paul Hodel <paul.hodel@gmail.com>
 * @description: Image Slider
 */

jSuites.slider = (function(el, options) {
    var obj = {};
    obj.options = {};
    obj.currentImage = null;

    if (options) {
        obj.options = options;
    }

    // Items
    obj.options.items = [];

    if (! el.classList.contains('jslider')) {
        el.classList.add('jslider');

        // Create container
        var container = document.createElement('div');
        container.className = 'jslider-container';

        // Move children inside
        if (el.children.length > 0) {
            // Keep children items
            for (var i = 0; i < el.children.length; i++) {
                obj.options.items.push(el.children[i]);
            }
        }
        if (obj.options.items.length > 0) {
            for (var i = 0; i < obj.options.items.length; i++) {
                obj.options.items[i].classList.add('jfile');
                var index = obj.options.items[i].src.lastIndexOf('/');
                if (index < 0) {
                    obj.options.items[i].setAttribute('data-name', obj.options.items[i].src);
                } else {
                    obj.options.items[i].setAttribute('data-name', obj.options.items[i].src.substr(index + 1));
                }
                var index = obj.options.items[i].src.lastIndexOf('/');

                container.appendChild(obj.options.items[i]);
            }
        }
        el.appendChild(container);
        // Add close buttom
        var close = document.createElement('div');
        close.className = 'jslider-close';
        close.innerHTML = '';
        close.onclick =  function() {
            obj.close();
        }
        el.appendChild(close);
    } else {
        var container = el.querySelector('slider-container');
    }

    obj.show = function(target) {
        if (! target) {
            var target = container.children[0];
        }

        if (! container.classList.contains('jslider-preview')) {
            container.classList.add('jslider-preview');
            close.style.display = 'block';
        }

        // Hide all images
        for (var i = 0; i < container.children.length; i++) {
            container.children[i].style.display = 'none';
        }

        // Show clicked only
        target.style.display = 'block';

        // Is there any previous
        if (target.previousSibling) {
            container.classList.add('jslider-left');
        } else {
            container.classList.remove('jslider-left');
        }

        // Is there any next
        if (target.nextSibling) {
            container.classList.add('jslider-right');
        } else {
            container.classList.remove('jslider-right');
        }

        obj.currentImage = target;
    }

    obj.open = function() {
        obj.show();

        // Event
        if (typeof(obj.options.onopen) == 'function') {
            obj.options.onopen(el);
        }
    }

    obj.close = function() {
        container.classList.remove('jslider-preview');
        container.classList.remove('jslider-left');
        container.classList.remove('jslider-right');

        for (var i = 0; i < container.children.length; i++) {
            container.children[i].style.display = '';
        }

        close.style.display = '';

        obj.currentImage = null;

        // Event
        if (typeof(obj.options.onclose) == 'function') {
            obj.options.onclose(el);
        }
    }

    obj.reset = function() {
        container.innerHTML = '';
    }

    obj.addFile = function(v, ignoreEvents) {
        var img = document.createElement('img');
        img.setAttribute('data-lastmodified', v.lastmodified);
        img.setAttribute('data-name', v.name);
        img.setAttribute('data-size', v.size);
        img.setAttribute('data-extension', v.extension);
        img.setAttribute('data-cover', v.cover);
        img.setAttribute('src', v.file);
        img.className = 'jfile';
        container.appendChild(img);
        obj.options.items.push(img);

        // Onchange
        if (! ignoreEvents) {
            if (typeof(obj.options.onchange) == 'function') {
                obj.options.onchange(el, v);
            }
        }
    }

    obj.addFiles = function(files) {
        for (var i = 0; i < files.length; i++) {
            obj.addFile(files[i]);
        }
    }

    obj.next = function() {
        if (obj.currentImage.nextSibling) {
            obj.show(obj.currentImage.nextSibling);
        }
    }
    
    obj.prev = function() {
        if (obj.currentImage.previousSibling) {
            obj.show(obj.currentImage.previousSibling);
        }
    }

    obj.getData = function() {
        return jSuites.getFiles(container);
    }

    // Append data
    if (obj.options.data && obj.options.data.length) {
        for (var i = 0; i < obj.options.data.length; i++) {
            if (obj.options.data[i]) {
                obj.addFile(obj.options.data[i]);
            }
        }
    }

    // Allow insert
    if (obj.options.allowAttachment) {
        var attachmentInput = document.createElement('input');
        attachmentInput.type = 'file';
        attachmentInput.className = 'slider-attachment';
        attachmentInput.setAttribute('accept', 'image/*');
        attachmentInput.style.display = 'none';
        attachmentInput.onchange = function() {
            var reader = [];

            for (var i = 0; i < this.files.length; i++) {
                var type = this.files[i].type.split('/');

                if (type[0] == 'image') {
                    var extension = this.files[i].name;
                    extension = extension.split('.');
                    extension = extension[extension.length-1];

                    var file = {
                        size: this.files[i].size,
                        name: this.files[i].name,
                        extension: extension,
                        cover: 0,
                        lastmodified: this.files[i].lastModified,
                    }

                    reader[i] = new FileReader();
                    reader[i].addEventListener("load", function (e) {
                        file.file = e.target.result;
                        obj.addFile(file);
                    }, false);

                    reader[i].readAsDataURL(this.files[i]);
                } else {
                    alert('The extension is not allowed');
                }
            };
        }

        var attachmentIcon = document.createElement('i');
        attachmentIcon.innerHTML = 'attachment';
        attachmentIcon.className = 'jslider-attach material-icons';
        attachmentIcon.onclick = function() {
            jSuites.click(attachmentInput);
        }

        el.appendChild(attachmentInput);
        el.appendChild(attachmentIcon);
    }

    // Push to refresh
    var longTouchTimer = null;

    var mouseDown = function(e) {
        if (e.target.tagName == 'IMG') {
            // Remove
            var targetImage = e.target;
            longTouchTimer = setTimeout(function() {
                if (e.target.src.substr(0,4) == 'data') {
                    e.target.remove();
                } else {
                    if (e.target.classList.contains('jremove')) {
                        e.target.classList.remove('jremove');
                    } else {
                        e.target.classList.add('jremove');
                    }
                }

                // Onchange
                if (typeof(obj.options.onchange) == 'function') {
                    obj.options.onchange(el, e.target);
                }
            }, 1000);
        }
    }

    var mouseUp = function(e) {
        if (longTouchTimer) {
            clearTimeout(longTouchTimer);
        }

        // Open slider
        if (e.target.tagName == 'IMG') {
            if (! e.target.classList.contains('jremove')) {
                obj.show(e.target);
            }
        } else {
            // Arrow controls
            if (e.target.clientWidth - e.offsetX < 40) {
                // Show next image
                obj.next();
            } else if (e.offsetX < 40) {
                // Show previous image
                obj.prev();
            }
        }
    }

    container.addEventListener('mousedown', mouseDown);
    container.addEventListener('touchstart', mouseDown);
    container.addEventListener('mouseup', mouseUp);
    container.addEventListener('touchend', mouseUp);

    // Add global events
    el.addEventListener("swipeleft", function(e) {
        obj.next();
        e.preventDefault();
        e.stopPropagation();
    });

    el.addEventListener("swiperight", function(e) {
        obj.prev();
        e.preventDefault();
        e.stopPropagation();
    });


    el.slider = obj;

    return obj;
});

/**
 * (c) jTools v1.0.1 - Element sorting
 * https://github.com/paulhodel/jtools
 *
 * @author: Paul Hodel <paul.hodel@gmail.com>
 * @description: Element drag and drop sorting
 */

jSuites.sorting = (function(el, options) {
    el.classList.add('jsorting');

    el.addEventListener('dragstart', function(e) {
        e.target.classList.add('dragging');
    });

    el.addEventListener('dragover', function(e) {
        e.preventDefault();

        if (e.target.clientHeight / 2 > e.offsetY) {
            e.path[0].style.borderTop = '1px dotted #ccc';
            e.path[0].style.borderBottom = '';
        } else {
            e.path[0].style.borderTop = '';
            e.path[0].style.borderBottom = '1px dotted #ccc';
        }
    });

    el.addEventListener('dragleave', function(e) {
        e.path[0].style.borderTop = '';
        e.path[0].style.borderBottom = '';
    });

    el.addEventListener('dragend', function(e) {
        e.path[1].querySelector('.dragging').classList.remove('dragging');
    });

    el.addEventListener('drop', function(e) {
        var element = e.path[1].querySelector('.dragging');

        if (e.target.clientHeight / 2 > e.offsetY) {
            e.path[1].insertBefore(element, e.path[0]);
        } else {
            e.path[1].insertBefore(element, e.path[0].nextSibling);
        }

        e.path[0].style.borderTop = '';
        e.path[0].style.borderBottom = '';
    });

    for (var i = 0; i < el.children.length; i++) {
        el.children[i].setAttribute('draggable', 'true');
    };

    return el;
});

jSuites.tabs = (function(el, options) {
    var obj = {};
    obj.options = {};

    // Default configuration
    var defaults = {
        data: null,
        allowCreate: false,
        onload: null,
        onchange: null,
        oncreate: null,
        animation: false,
        create: null,
        autoName: false,
        prefixName: '',
        hideHeaders: false,
    };

    // Loop through the initial configuration
    for (var property in defaults) {
        if (options && options.hasOwnProperty(property)) {
            obj.options[property] = options[property];
        } else {
            obj.options[property] = defaults[property];
        }
    }

    // Class
    el.classList.add('jtabs');

    if (obj.options.animation == true) {
        // Border
        var border = document.createElement('div');
        border.className = 'jtabs-border';
        el.appendChild(border);

        var setBorder = function(index) {
            var rect = headers.children[index].getBoundingClientRect();
            var rectContent = content.children[index].getBoundingClientRect();
            border.style.width = rect.width + 'px';
            border.style.left = (rect.left - rectContent.left) + 'px';
            border.style.top = rect.height + 'px';
        }
    }

    // Set value
    obj.open = function(index) {
        for (var i = 0; i < headers.children.length; i++) {
            headers.children[i].classList.remove('jtabs-selected');
            if (content.children[i]) {
                content.children[i].classList.remove('jtabs-selected');
            }
        }

        headers.children[index].classList.add('jtabs-selected');
        if (content.children[index]) {
            content.children[index].classList.add('jtabs-selected');
        }

        // Hide
        if (obj.options.hideHeaders == true && (headers.children.length < 2 && obj.options.allowCreate == false)) {
            headers.style.display = 'none';
        } else {
            headers.style.display = '';
            // Set border
            if (obj.options.animation == true) {
                setTimeout(function() {
                    setBorder(index);
                }, 100);
            }
        }
    }

    obj.selectIndex = function(a) {
        var index = Array.prototype.indexOf.call(headers.children, a);
        if (index >= 0) {
            obj.open(index);
        }
    }

    obj.create = function(title, div) {
        if (typeof(obj.options.create) == 'function') {
            obj.options.create();
        } else {
            obj.appendElement(title, div);

            if (typeof(obj.options.oncreate) == 'function') {
                obj.options.oncreate(el, div)
            }
        }
    }

    obj.appendElement = function(title, div) {
        if (! title) {
            if (obj.options.autoName == true) {
                var title = obj.options.prefixName;
                title += ' ' + (parseInt(content.children.length) + 1);
            } else {
                var title = prompt('Title?', '');
            }
        }

        if (title) {
            // Add headers
            var header = document.createElement('div');
            header.innerHTML = title;
            if (obj.options.allowCreate) {
                headers.insertBefore(header, headers.lastChild);
            } else {
                headers.appendChild(header);
            }

            // Add content
            if (! div) {
                var div = document.createElement('div');
            }
            content.appendChild(div);

            // Open new tab
            obj.selectIndex(header);
        }
    }

    // Create from data
    if (obj.options.data) {
        // Make sure the component is blank
        el.innerHTML = '';
        var headers = document.createElement('div');
        var content = document.createElement('div');
        headers.classList.add('jtabs-headers');
        content.classList.add('jtabs-content');
        el.appendChild(headers);
        el.appendChild(content);

        for (var i = 0; i < obj.options.data.length; i++) {
            var headersItem = document.createElement('div');
            headers.appendChild(headersItem);
            var contentItem = document.createElement('div');
            content.appendChild(contentItem);

            headersItem.innerHTML = obj.options.data[i].title;
            if (obj.options.data[i].content) {
                contentItem.innerHTML = obj.options.data[i].content;
            } else if (obj.options.data[i].url) {
                jSuites.ajax({
                    url: obj.options.data[i].url,
                    type: 'GET',
                    success: function(result) {
                        contentItem.innerHTML = result;
                    },
                    complete: function() {
                        if (typeof(obj.options.onload) == 'function') {
                            obj.options.onload(el);

                            obj.open(0);
                        }
                    }
                });
            }
        }
    } else if (el.children[0] && el.children[1]) {
        // Create from existing elements
        var headers = el.children[0];
        var content = el.children[1];
        headers.classList.add('jtabs-headers');
        content.classList.add('jtabs-content');
    } else {
        el.innerHTML = '';
        var headers = document.createElement('div');
        var content = document.createElement('div');
        headers.classList.add('jtabs-headers');
        content.classList.add('jtabs-content');
        el.appendChild(headers);
        el.appendChild(content);
    }

    // New
    if (obj.options.allowCreate == true) {
        var add = document.createElement('i');
        add.className = 'jtabs-add';
        headers.appendChild(add);
    }

    // Events
    headers.addEventListener("click", function(e) {
        if (e.target.tagName == 'DIV') {
            obj.selectIndex(e.target);
        } else {
            obj.create();
        }
    });

    if (headers.children.length) {
        obj.open(0);
    }

    el.tabs = obj;

    return obj;
});

jSuites.tags = (function(el, options) {
    var obj = {};
    obj.options = {};

    /**
     * @typedef {Object} defaults
     * @property {(string|Array)} value - Initial value of the compontent
     * @property {number} limit - Max number of tags inside the element
     * @property {string} search - The URL for suggestions
     * @property {string} placeholder - The default instruction text on the element
     * @property {validation} validation - Method to validate the tags
     * @property {requestCallback} onbeforechange - Method to be execute before any changes on the element
     * @property {requestCallback} onchange - Method to be execute after any changes on the element
     * @property {requestCallback} onfocus - Method to be execute when on focus
     * @property {requestCallback} onblur - Method to be execute when on blur
     * @property {requestCallback} onload - Method to be execute when the element is loaded
     */
    var defaults = {
        value: null,
        limit: null,
        limitMessage: 'The limit of entries is: ',
        search: null,
        placeholder: null,
        validation: null,
        onbeforechange: null,
        onchange: null,
        onfocus: null,
        onblur: null,
        onload: null,
        colors: null,
    };

    // Loop through though the default configuration
    for (var property in defaults) {
        if (options && options.hasOwnProperty(property)) {
            obj.options[property] = options[property];
        } else {
            obj.options[property] = defaults[property];
        }
    }

    // Search helpers
    var searchContainer = null;
    var searchTerms = null;
    var searchIndex = 0;
    var searchTimer = 0;

    /**
     * Add a new tag to the element
     * @param {(?string|Array)} value - The value of the new element
     */
    obj.add = function(value, focus) {
        if (typeof(obj.options.onbeforechange) == 'function') {
            var v = obj.options.onbeforechange(el, obj, value);
            if (v != null) {
                value = v;
            }
        }

        // Close search
        if (searchContainer) {
            searchContainer.style.display = '';
        }

        if (obj.options.limit > 0 && el.children.length >= obj.options.limit) {
            alert(obj.options.limitMessage + ' ' + obj.options.limit);
        } else {
            // Get node
            var node = getSelectionStart();

            // Mix argument string or array
            if (! value || typeof(value) == 'string') {
                var div = document.createElement('div');
                div.innerHTML = value ? value : '<br>';
                if (node && node.parentNode.classList.contains('jtags')) {
                    el.insertBefore(div, node.nextSibling);
                } else {
                    el.appendChild(div);
                }
            } else {
                if (node && node.parentNode.classList.contains('jtags')) {
                    if (! node.innerText.replace("\n", "")) {
                        el.removeChild(node);
                    }
                }

                for (var i = 0; i <= value.length; i++) {
                    if (! obj.options.limit || el.children.length < obj.options.limit) {
                        var div = document.createElement('div');
                        div.innerHTML = value[i] ? value[i] : '<br>';
                        el.appendChild(div);
                    }
                }
            }

            // Place caret
            if (focus) {
                setTimeout(function() {
                    caret(div);
                }, 0);
            }

            // Filter
            filter();

            if (typeof(obj.options.onchange) == 'function') {
                obj.options.onchange(el, obj, value ? value : '');
            }
        }
    }

    /**
     * Get all tags in the element
     * @return {Array} data - All tags as an array
     */
    obj.getData = function() {
        var data = [];
        for (var i = 0; i < el.children.length; i++) {
            var value = obj.getValue(i);
            if (value) {
                var id = el.children[i].getAttribute('data-id');
                if (! id) {
                    id = value;
                }
                data.push({ id: id, value: value });
            }
        }
        return data;
    }

    /**
     * Get the value of one tag. Null for all tags
     * @param {?number} index - Tag index number. Null for all tags.
     * @return {string} value - All tags separated by comma
     */
    obj.getValue = function(index) {
        var value = null;

        if (index != null) {
            // Get one individual value
            value = el.children[index].innerText.replace("\n", "");
        } else {
            // Get all
            var data = [];
            for (var i = 0; i < el.children.length; i++) {
                value = el.children[i].innerText.replace("\n", "");
                if (value) {
                    data.push(obj.getValue(i));
                }
            }
            value = data.join(',');
        }

        return value;
    }

    /**
     * Set the value of the element based on a string separeted by (,|;|\r\n)
     * @param {string} value - A string with the tags
     */
    obj.setValue = function(text) {
        // Remove whitespaces
        text = text.trim();

        if (text) {
            // Tags
            var data = extractTags(text);
            // Add tags to the element
            obj.add(data);
        }
    }

    obj.reset = function() {
        el.innerHTML = '<div><br></div>';
    }

    /**
     * Verify if all tags in the element are valid
     * @return {boolean}
     */
    obj.isValid = function() {
        var test = 0;
        for (var i = 0; i < el.children.length; i++) {
            if (el.children[i].classList.contains('jtags_error')) {
                test++;
            }
        }
        return test == 0 ? true : false;
    }

    /**
     * Add one element from the suggestions to the element
     * @param {object} item - Node element in the suggestions container
     */ 
    obj.selectIndex = function(item) {
        // Reset terms
        searchTerms = '';
        var node = getSelectionStart();
        // Append text to the caret
        node.innerText = item.children[1].innerText;
        // Set node id
        if (item.children[1].getAttribute('data-id')) {
            node.setAttribute('data-id', item.children[1].getAttribute('data-id'));
        }
        // Close container
        if (searchContainer) {
            searchContainer.style.display = '';
            searchContainer.innerHTML = '';
        }
        // Remove any error
        node.classList.remove('jtags_error');
        // Add new item
        obj.add();
    }

    /**
     * Search for suggestions
     * @param {object} node - Target node for any suggestions
     */
    obj.search = function(node) {
        // Create and append search container to the DOM
        if (! searchContainer) {
            var div = document.createElement('div');
            div.style.position = 'relative';
            el.parentNode.insertBefore(div, el.nextSibling);

            // Create container
            searchContainer = document.createElement('div');
            searchContainer.classList.add('jtags_search');
            div.appendChild(searchContainer);
        }

        // Search for
        var terms = node.anchorNode.nodeValue;

        // Search
        if (node.anchorNode.nodeValue && terms != searchTerms) {
            // Terms
            searchTerms = node.anchorNode.nodeValue;
            // Reset index
            searchIndex = 0;
            // Get remove results
            jSuites.ajax({
                url: obj.options.search + searchTerms,
                method: 'GET',
                dataType: 'json',
                success: function(data) {
                    // Reset container
                    searchContainer.innerHTML = '';

                    // Print results
                    if (! data.length) {
                        // Show container
                        searchContainer.style.display = '';
                    } else {
                        // Show container
                        searchContainer.style.display = 'block';

                        // Show items
                        var len = data.length < 11 ? data.length : 10;
                        for (var i = 0; i < len; i++) {
                            var div = document.createElement('div');
                            if (i == 0) {
                                div.classList.add('selected');
                            }
                            var img = document.createElement('img');
                            if (data[i].image) {
                                img.src = data[i].image;
                            } else {
                                img.style.display = 'none';
                            }
                            div.appendChild(img);

                            var item = document.createElement('div');
                            item.setAttribute('data-id', data[i].id);
                            item.innerHTML = data[i].name;
                            div.onclick = function() {
                                // Add item
                                obj.selectIndex(this);
                            }
                            div.appendChild(item);
                            // Append item to the container
                            searchContainer.appendChild(div);
                        }
                    }
                }
            });
        }
    }

    // Destroy tags element
    obj.destroy = function() {
        // Bind events
        el.removeEventListener('mouseup', tagsMouseUp);
        el.removeEventListener('keydown', tagsKeyDown);
        el.removeEventListener('keyup', tagsKeyUp);
        el.removeEventListener('paste', tagsPaste);
        el.removeEventListener('focus', tagsFocus);
        el.removeEventListener('blur', tagsBlur);
        // Remove element
        el.parentNode.removeChild(el);
    }

    var getRandomColor = function(index) {
        var rand = function(min, max) {
            return min + Math.random() * (max - min);
        }
        return 'hsl(' + rand(1, 360) + ',' + rand(40, 70) + '%,' + rand(65, 72) + '%)';
    }

    /**
     * Filter tags
     */
    var filter = function() {
        for (var i = 0; i < el.children.length; i++) {
            // Create label design
            if (! obj.getValue(i)) {
                el.children[i].classList.remove('jtags_label');
            } else {
                el.children[i].classList.add('jtags_label');

                // Validation in place
                if (typeof(obj.options.validation) == 'function') {
                    if (obj.getValue(i)) {
                        if (! obj.options.validation(el.children[i], el.children[i].innerText, el.children[i].getAttribute('data-id'))) {
                            el.children[i].classList.add('jtags_error');
                        } else {
                            el.children[i].classList.remove('jtags_error');
                        }
                    } else {
                        el.children[i].classList.remove('jtags_error');
                    }
                }
            }
        }
    }

    /**
     * Place caret in the element node
     */
    var caret = function(e) {
        var range = document.createRange();
        var sel = window.getSelection();
        range.setStart(e, e.innerText.length);
        range.collapse(true);
        sel.removeAllRanges();
        sel.addRange(range);
    }

    /**
     * Selection
     */
    var getSelectionStart = function() {
        var node = document.getSelection().anchorNode;
        if (node) {
            return (node.nodeType == 3 ? node.parentNode : node);
        } else {
            return null;
        }
    }

    /**
     * Extract tags from a string
     * @param {string} text - Raw string
     * @return {Array} data - Array with extracted tags
     */
    var extractTags = function(text) {
        /** @type {Array} */
        var data = [];

        /** @type {string} */
        var word = '';

        // Remove whitespaces
        text = text.trim();

        if (text) {
            for (var i = 0; i < text.length; i++) {
                if (text[i] == ',' || text[i] == ';' || text[i] == '\r\n') {
                    if (word) {
                        data.push(word);
                        word = '';
                    }
                } else {
                    word += text[i];
                }
            }

            if (word) {
                data.push(word);
            }
        }

        return data;
    }

    /** @type {number} */
    var anchorOffset = 0;

    /**
     * Processing event keydown on the element
     * @param e {object}
     */
    var tagsKeyDown = function(e) {
        // Anchoroffset
        anchorOffset = window.getSelection().anchorOffset;

        // Verify content
        if (! el.children.length) {
            var div = document.createElement('div');
            div.innerHTML = '<br>';
            el.appendChild(div);
        }
        // Comma
        if (e.which == 9 || e.which == 186 || e.which == 188) {
            var n = window.getSelection().anchorOffset;
            if (n > 1) {
                if (! obj.options.limit || el.children.length < obj.options.limit) {
                    obj.add('', true);
                }
            }
            e.preventDefault();
        } else if (e.which == 13) {
            // Enter
            if (searchContainer && searchContainer.style.display != '') {
                obj.selectIndex(searchContainer.children[searchIndex]);
            } else {
                var n = window.getSelection().anchorOffset;
                if (n > 1) {
                    if (! obj.options.limit || el.children.length < obj.options.limit) {
                        obj.add('', true);
                    }
                }
            }
            e.preventDefault();
        } else if (e.which == 38) {
            // Up
            if (searchContainer && searchContainer.style.display != '') {
                searchContainer.children[searchIndex].classList.remove('selected');
                if (searchIndex > 0) {
                    searchIndex--;
                }
                searchContainer.children[searchIndex].classList.add('selected');
                e.preventDefault();
            }
        } else if (e.which == 40) {
            // Down
            if (searchContainer && searchContainer.style.display != '') {
                searchContainer.children[searchIndex].classList.remove('selected');
                if (searchIndex < 9) {
                    searchIndex++;
                }
                searchContainer.children[searchIndex].classList.add('selected');
                e.preventDefault();
            }
        }
    }

    /**
     * Processing event keyup on the element
     * @param e {object}
     */
    var tagsKeyUp = function(e) {
        if (e.which == 39) {
            var n = window.getSelection().anchorOffset;
            if (n > 1 && n == anchorOffset) {
                obj.add();
            }
        } else if (e.which == 13 || e.which == 38 || e.which == 40) {
            e.preventDefault();
        } else {
            if (searchTimer) {
                clearTimeout(searchTimer);
            }

            searchTimer = setTimeout(function() {
                // Current node
                var node = window.getSelection();
                // Search
                if (obj.options.search) {
                    obj.search(node);
                }
                searchTimer = null;
            }, 300);
        }

        filter();
    }

    /**
     * Processing event paste on the element
     * @param e {object}
     */
    var tagsPaste =  function(e) {
        if (e.clipboardData || e.originalEvent.clipboardData) {
            var html = (e.originalEvent || e).clipboardData.getData('text/html');
            var text = (e.originalEvent || e).clipboardData.getData('text/plain');
        } else if (window.clipboardData) {
            var html = window.clipboardData.getData('Html');
            var text = window.clipboardData.getData('Text');
        }

        obj.setValue(text);
        e.preventDefault();
    }

    /**
     * Processing event mouseup on the element
     * @param e {object}
     */
    var tagsMouseUp = function(e) {
        if (e.target.parentNode && e.target.parentNode.classList.contains('jtags')) {
            if (e.target.classList.contains('jtags_label') || e.target.classList.contains('jtags_error')) {
                const rect = e.target.getBoundingClientRect();
                if (rect.width - (e.clientX - rect.left) < 16) {
                    el.removeChild(e.target);
                    el.focus();
                }
            }
        }

        if (searchContainer) {
            searchContainer.style.display = '';
        }
    }

    /**
     * Processing event focus on the element
     * @param e {object}
     */
    var tagsFocus = function(e) {
        if (! el.children.length || obj.getValue(el.children.length - 1)) {
            if (! obj.options.limit || el.children.length < obj.options.limit) {
                var div = document.createElement('div');
                div.innerHTML = '<br>';
                el.appendChild(div);
            }
        }

        if (typeof(obj.options.onfocus) == 'function') {
            obj.options.onfocus(el, obj, obj.getValue());
        }
    }

    /**
     * Processing event blur on the element
     * @param e {object}
     */
    var tagsBlur = function(e) {
        if (searchContainer) {
            setTimeout(function() {
                searchContainer.style.display = '';
            }, 200);
        }

        for (var i = 0; i < el.children.length - 1; i++) {
            // Create label design
            if (! obj.getValue(i)) {
                el.removeChild(el.children[i]);
            }
        }

        if (typeof(obj.options.onblur) == 'function') {
            obj.options.onblur(el, obj, obj.getValue());
        }
    }

    // Bind events
    el.addEventListener('mouseup', tagsMouseUp);
    el.addEventListener('keydown', tagsKeyDown);
    el.addEventListener('keyup', tagsKeyUp);
    el.addEventListener('paste', tagsPaste);
    el.addEventListener('focus', tagsFocus);
    el.addEventListener('blur', tagsBlur);

    // Prepare container
    el.classList.add('jtags');
    el.setAttribute('contenteditable', true);
    el.setAttribute('spellcheck', false);

    if (obj.options.placeholder) {
        el.placeholder = obj.options.placeholder;
    }

    // Make sure element is empty
    if (obj.options.value) {
        obj.setValue(obj.options.value);
    } else {
        el.innerHTML = '<div><br></div>';
    }

    if (typeof(obj.options.onload) == 'function') {
        obj.options.onload(el, obj);
    }

    el.tags = obj;

    return obj;
});

jSuites.tracker = (function(el, options) {
    var obj = {};
    obj.options = {};

    // Default configuration
    var defaults = {
        url: null,
        message: 'Are you sure? There are unsaved information in your form',
        ignore: false,
        currentHash: null,
        submitButton:null,
        onload: null,
        onbeforesave: null,
        onsave: null,
    };

    // Loop through our object
    for (var property in defaults) {
        if (options && options.hasOwnProperty(property)) {
            obj.options[property] = options[property];
        } else {
            obj.options[property] = defaults[property];
        }
    }

    obj.setUrl = function(url) {
        obj.options.url = url;
    }

    obj.load = function() {
        jSuites.ajax({
            url: obj.options.url,
            method: 'GET',
            dataType: 'json',
            success: function(data) {
                var elements = el.querySelectorAll("input, select, textarea");
 
                for (var i = 0; i < elements.length; i++) {
                    var name = elements[i].getAttribute('name');
                    if (data[name]) {
                        elements[i].value = data[name];
                    }
                }

                if (typeof(obj.options.onload) == 'function') {
                    obj.options.onload(el, data);
                }
            }
        });
    }

    obj.save = function() {
        var test = obj.validate();

        if (test) {
            jSuites.alert(test);
        } else {
            var data = obj.getElements(true);

            if (typeof(obj.options.onbeforesave) == 'function') {
                var data = obj.options.onbeforesave(el, data);

                if (data === false) {
                    console.log('Onbeforesave returned false');
                    return; 
                }
            }

            jSuites.ajax({
                url: obj.options.url,
                method: 'POST',
                dataType: 'json',
                data: data,
                success: function(result) {
                    jSuites.alert(result.message);

                    if (typeof(obj.options.onsave) == 'function') {
                        var data = obj.options.onsave(el, result);
                    }

                    obj.reset();
                }
            });
        }
    }

    obj.validateElement = function(element) {
        var emailChecker = function(data) {
            var pattern = new RegExp(/^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/);
            return pattern.test(data) ? true : false; 
        }

        var passwordChecker = function(data) {
            return (data.length > 5) ? true : false;
        }

        var addError = function(element) {
            // Add error in the element
            element.classList.add('error');
            // Submit button
            if (obj.options.submitButton) {
                obj.options.submitButton.setAttribute('disabled', true);
            }
            // Return error message
            return element.getAttribute('data-error') || 'There is an error in the form';
        }

        var delError = function(element) {
            var error = false;
            // Remove class from this element
            element.classList.remove('error');
            // Get elements in the form
            var elements = el.querySelectorAll("input, select, textarea");
            // Run all elements 
            for (var i = 0; i < elements.length; i++) {
                if (elements[i].getAttribute('data-validation')) {
                    if (elements[i].classList.contains('error')) {
                        error = true;
                    }
                }
            }

            if (obj.options.submitButton) {
                if (error) {
                    obj.options.submitButton.setAttribute('disabled', true);
                } else {
                    obj.options.submitButton.removeAttribute('disabled');
                }
            }
        }

        // Blank
        var test = '';
        if (! element.value) {
            test = addError(element);
        } else if (element.getAttribute('data-email') && ! emailChecker(element.value)) {
            test = addError(element);
        } else if (element.getAttribute('data-password') && ! emailChecker(element.value)) {
            test = addError(element);
        } else {
            if (element.classList.contains('error')) {
                delError(element);
            }
        }

        return test;
    }

    // Run form validation
    obj.validate = function() {
        var test = '';
        // Get elements in the form
        var elements = el.querySelectorAll("input, select, textarea");
        // Run all elements 
        for (var i = 0; i < elements.length; i++) {
            if (elements[i].getAttribute('data-validation')) {
                if (test) {
                    test += "<br>\r\n";
                }
                test += obj.validateElement(elements[i]);
            }
        }
        return test;
    }

    // Check the form
    obj.getError = function() {
        // Validation
        return obj.validation() ? true : false;
    }

    // Return the form hash
    obj.setHash = function() {
        return obj.getHash(obj.getElements());
    }

    // Get the form hash
    obj.getHash = function(str) {
        var hash = 0, i, chr;

        if (str.length === 0) {
            return hash;
        } else {
            for (i = 0; i < str.length; i++) {
              chr = str.charCodeAt(i);
              hash = ((hash << 5) - hash) + chr;
              hash |= 0;
            }
        }

        return hash;
    }

    // Is there any change in the form since start tracking?
    obj.isChanged = function() {
        var hash = obj.setHash();
        return (obj.options.currentHash != hash);
    }

    // Restart tracking
    obj.resetTracker = function() {
        obj.options.currentHash = obj.setHash();
        obj.options.ignore = false;
    }

    obj.reset = function() {
        obj.options.currentHash = obj.setHash();
        obj.options.ignore = false;
    }

    // Ignore flag
    obj.setIgnore = function(ignoreFlag) {
        obj.options.ignore = ignoreFlag ? true : false;
    }

    // Get form elements
    obj.getElements = function(asArray) {
        var data = {};
        var elements = el.querySelectorAll("input, select, textarea");

        for (var i = 0; i < elements.length; i++) {
            var element = elements[i];
            var name = element.name;
            var value = element.value;

            if (name) {
                data[name] = value;
            }
        }

        return asArray == true ? data : JSON.stringify(data);
    }

    // Start tracking in one second
    setTimeout(function() {
        obj.options.currentHash = obj.setHash();
    }, 1000);

    // Alert
    window.addEventListener("beforeunload", function (e) {
        if (obj.isChanged() && obj.options.ignore == false) {
            var confirmationMessage =  obj.options.message? obj.options.message : "\o/";

            if (confirmationMessage) {
                if (typeof e == 'undefined') {
                    e = window.event;
                }

                if (e) {
                    e.returnValue = confirmationMessage;
                }

                return confirmationMessage;
            } else {
                return void(0);
            }
        }
    });

    // Validations
    el.addEventListener("keyup", function(e) {
        if (e.target.getAttribute('data-validation')) {
            obj.validateElement(e.target);
        }
    });

    el.tracker = obj;

    return obj;
});



    return jSuites;

})));

/***/ }),

/***/ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js":
/*!****************************************************************************!*\
  !*** ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js ***!
  \****************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var isOldIE = function isOldIE() {
  var memo;
  return function memorize() {
    if (typeof memo === 'undefined') {
      // Test for IE <= 9 as proposed by Browserhacks
      // @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805
      // Tests for existence of standard globals is to allow style-loader
      // to operate correctly into non-standard environments
      // @see https://github.com/webpack-contrib/style-loader/issues/177
      memo = Boolean(window && document && document.all && !window.atob);
    }

    return memo;
  };
}();

var getTarget = function getTarget() {
  var memo = {};
  return function memorize(target) {
    if (typeof memo[target] === 'undefined') {
      var styleTarget = document.querySelector(target); // Special case to return head of iframe instead of iframe itself

      if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {
        try {
          // This will throw an exception if access to iframe is blocked
          // due to cross-origin restrictions
          styleTarget = styleTarget.contentDocument.head;
        } catch (e) {
          // istanbul ignore next
          styleTarget = null;
        }
      }

      memo[target] = styleTarget;
    }

    return memo[target];
  };
}();

var stylesInDom = [];

function getIndexByIdentifier(identifier) {
  var result = -1;

  for (var i = 0; i < stylesInDom.length; i++) {
    if (stylesInDom[i].identifier === identifier) {
      result = i;
      break;
    }
  }

  return result;
}

function modulesToDom(list, options) {
  var idCountMap = {};
  var identifiers = [];

  for (var i = 0; i < list.length; i++) {
    var item = list[i];
    var id = options.base ? item[0] + options.base : item[0];
    var count = idCountMap[id] || 0;
    var identifier = "".concat(id, " ").concat(count);
    idCountMap[id] = count + 1;
    var index = getIndexByIdentifier(identifier);
    var obj = {
      css: item[1],
      media: item[2],
      sourceMap: item[3]
    };

    if (index !== -1) {
      stylesInDom[index].references++;
      stylesInDom[index].updater(obj);
    } else {
      stylesInDom.push({
        identifier: identifier,
        updater: addStyle(obj, options),
        references: 1
      });
    }

    identifiers.push(identifier);
  }

  return identifiers;
}

function insertStyleElement(options) {
  var style = document.createElement('style');
  var attributes = options.attributes || {};

  if (typeof attributes.nonce === 'undefined') {
    var nonce =  true ? __webpack_require__.nc : undefined;

    if (nonce) {
      attributes.nonce = nonce;
    }
  }

  Object.keys(attributes).forEach(function (key) {
    style.setAttribute(key, attributes[key]);
  });

  if (typeof options.insert === 'function') {
    options.insert(style);
  } else {
    var target = getTarget(options.insert || 'head');

    if (!target) {
      throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");
    }

    target.appendChild(style);
  }

  return style;
}

function removeStyleElement(style) {
  // istanbul ignore if
  if (style.parentNode === null) {
    return false;
  }

  style.parentNode.removeChild(style);
}
/* istanbul ignore next  */


var replaceText = function replaceText() {
  var textStore = [];
  return function replace(index, replacement) {
    textStore[index] = replacement;
    return textStore.filter(Boolean).join('\n');
  };
}();

function applyToSingletonTag(style, index, remove, obj) {
  var css = remove ? '' : obj.media ? "@media ".concat(obj.media, " {").concat(obj.css, "}") : obj.css; // For old IE

  /* istanbul ignore if  */

  if (style.styleSheet) {
    style.styleSheet.cssText = replaceText(index, css);
  } else {
    var cssNode = document.createTextNode(css);
    var childNodes = style.childNodes;

    if (childNodes[index]) {
      style.removeChild(childNodes[index]);
    }

    if (childNodes.length) {
      style.insertBefore(cssNode, childNodes[index]);
    } else {
      style.appendChild(cssNode);
    }
  }
}

function applyToTag(style, options, obj) {
  var css = obj.css;
  var media = obj.media;
  var sourceMap = obj.sourceMap;

  if (media) {
    style.setAttribute('media', media);
  } else {
    style.removeAttribute('media');
  }

  if (sourceMap && typeof btoa !== 'undefined') {
    css += "\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))), " */");
  } // For old IE

  /* istanbul ignore if  */


  if (style.styleSheet) {
    style.styleSheet.cssText = css;
  } else {
    while (style.firstChild) {
      style.removeChild(style.firstChild);
    }

    style.appendChild(document.createTextNode(css));
  }
}

var singleton = null;
var singletonCounter = 0;

function addStyle(obj, options) {
  var style;
  var update;
  var remove;

  if (options.singleton) {
    var styleIndex = singletonCounter++;
    style = singleton || (singleton = insertStyleElement(options));
    update = applyToSingletonTag.bind(null, style, styleIndex, false);
    remove = applyToSingletonTag.bind(null, style, styleIndex, true);
  } else {
    style = insertStyleElement(options);
    update = applyToTag.bind(null, style, options);

    remove = function remove() {
      removeStyleElement(style);
    };
  }

  update(obj);
  return function updateStyle(newObj) {
    if (newObj) {
      if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap) {
        return;
      }

      update(obj = newObj);
    } else {
      remove();
    }
  };
}

module.exports = function (list, options) {
  options = options || {}; // Force single-tag solution on IE6-9, which has a hard limit on the # of <style>
  // tags it will allow on a page

  if (!options.singleton && typeof options.singleton !== 'boolean') {
    options.singleton = isOldIE();
  }

  list = list || [];
  var lastIdentifiers = modulesToDom(list, options);
  return function update(newList) {
    newList = newList || [];

    if (Object.prototype.toString.call(newList) !== '[object Array]') {
      return;
    }

    for (var i = 0; i < lastIdentifiers.length; i++) {
      var identifier = lastIdentifiers[i];
      var index = getIndexByIdentifier(identifier);
      stylesInDom[index].references--;
    }

    var newLastIdentifiers = modulesToDom(newList, options);

    for (var _i = 0; _i < lastIdentifiers.length; _i++) {
      var _identifier = lastIdentifiers[_i];

      var _index = getIndexByIdentifier(_identifier);

      if (stylesInDom[_index].references === 0) {
        stylesInDom[_index].updater();

        stylesInDom.splice(_index, 1);
      }
    }

    lastIdentifiers = newLastIdentifiers;
  };
};

/***/ }),

/***/ "./node_modules/webpack/buildin/global.js":
/*!***********************************!*\
  !*** (webpack)/buildin/global.js ***!
  \***********************************/
/*! no static exports found */
/***/ (function(module, exports) {

var g;

// This works in non-strict mode
g = (function() {
	return this;
})();

try {
	// This works if eval is allowed (see CSP)
	g = g || new Function("return this")();
} catch (e) {
	// This works if the window reference is available
	if (typeof window === "object") g = window;
}

// g can still be undefined, but nothing to do about it...
// We return undefined, instead of nothing here, so it's
// easier to handle this case. if(!global) { ...}

module.exports = g;


/***/ }),

/***/ "./runestone/activecode/css/activecode.css":
/*!*************************************************!*\
  !*** ./runestone/activecode/css/activecode.css ***!
  \*************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var api = __webpack_require__(/*! ../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
            var content = __webpack_require__(/*! !../../../node_modules/css-loader/dist/cjs.js!./activecode.css */ "./node_modules/css-loader/dist/cjs.js!./runestone/activecode/css/activecode.css");

            content = content.__esModule ? content.default : content;

            if (typeof content === 'string') {
              content = [[module.i, content, '']];
            }

var options = {};

options.insert = "head";
options.singleton = false;

var update = api(content, options);



module.exports = content.locals || {};

/***/ }),

/***/ "./runestone/activecode/js/acfactory.js":
/*!**********************************************!*\
  !*** ./runestone/activecode/js/acfactory.js ***!
  \**********************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return ACFactory; });
/* harmony import */ var _activecode_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./activecode.js */ "./runestone/activecode/js/activecode.js");
/* harmony import */ var _activecode_js_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./activecode_js.js */ "./runestone/activecode/js/activecode_js.js");
/* harmony import */ var _activecode_html_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./activecode_html.js */ "./runestone/activecode/js/activecode_html.js");
/* harmony import */ var _activecode_sql_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./activecode_sql.js */ "./runestone/activecode/js/activecode_sql.js");
/* harmony import */ var _livecode_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./livecode.js */ "./runestone/activecode/js/livecode.js");
/* harmony import */ var _timed_activecode__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./timed_activecode */ "./runestone/activecode/js/timed_activecode.js");







class ACFactory {
    constructor() {
        this.foo = "bar";
    }
    static createActiveCode(orig, lang, addopts) {
        var opts = {
            orig: orig,
            useRunestoneServices: eBookConfig.useRunestoneServices,
            python3: eBookConfig.python3,
        };
        if (addopts) {
            for (var attrname in addopts) {
                opts[attrname] = addopts[attrname];
            }
        }
        if (lang === undefined) {
            lang = $(opts.orig).find("[data-lang]").data("lang");
        }
        if (opts.timed == true) {
            if (lang === "python") {
                return new _timed_activecode__WEBPACK_IMPORTED_MODULE_5__["TimedActiveCode"](opts);
            } else if (
                lang === "java" ||
                lang === "cpp" ||
                lang === "c" ||
                lang === "python3"
            ) {
                return new _timed_activecode__WEBPACK_IMPORTED_MODULE_5__["TimedLiveCode"](opts);
            } else if (lang === "javascript") {
                return new _timed_activecode__WEBPACK_IMPORTED_MODULE_5__["TimedJSActiveCode"](opts);
            } else if (lang === "htmlmixed") {
                return new _timed_activecode__WEBPACK_IMPORTED_MODULE_5__["TimedHTMLActiveCode"](opts);
            } else if (lang === "sql") {
                return new _timed_activecode__WEBPACK_IMPORTED_MODULE_5__["TimedSQLActiveCode"](opts);
            } else {
                return new _timed_activecode__WEBPACK_IMPORTED_MODULE_5__["TimedActiveCode"](opts);
            }
        } else {
            if (lang === "javascript") {
                return new _activecode_js_js__WEBPACK_IMPORTED_MODULE_1__["default"](opts);
            } else if (lang === "htmlmixed") {
                return new _activecode_html_js__WEBPACK_IMPORTED_MODULE_2__["default"](opts);
            } else if (lang === "sql") {
                return new _activecode_sql_js__WEBPACK_IMPORTED_MODULE_3__["default"](opts);
            } else if (
                ["java", "cpp", "c", "python3", "python2", "octave"].indexOf(
                    lang
                ) > -1
            ) {
                return new _livecode_js__WEBPACK_IMPORTED_MODULE_4__["default"](opts);
            } else {
                // default is python
                return new _activecode_js__WEBPACK_IMPORTED_MODULE_0__["ActiveCode"](opts);
            }
        }
    }
    // used by web2py controller(s)
    static addActiveCodeToDiv(outerdivid, acdivid, sid, initialcode, language) {
        var thepre, newac;
        var acdiv = document.getElementById(acdivid);
        $(acdiv).empty();
        thepre = document.createElement("textarea");
        thepre["data-component"] = "activecode";
        thepre.id = outerdivid;
        $(thepre).data("lang", language);
        $(acdiv).append(thepre);
        var opts = {
            orig: thepre,
            useRunestoneServices: true,
        };
        var addopts = {
            sid: sid,
            graderactive: true,
        };
        if (language === "htmlmixed") {
            addopts["vertical"] = true;
        }
        newac = ACFactory.createActiveCode(thepre, language, addopts);
        var savediv = newac.divid;
        newac.divid = savediv;
        newac.editor.setSize(500, 300);
        setTimeout(function () {
            newac.editor.refresh();
        }, 500);
    }
    static createActiveCodeFromOpts(opts) {
        return ACFactory.createActiveCode(opts.orig, opts.lang, opts);
    }
    static createScratchActivecode() {
        /* set up the scratch Activecode editor in the search menu */
        // use the URL to assign a divid - each page should have a unique Activecode block id.
        // Remove everything from the URL but the course and page name
        // todo:  this could probably be eliminated and simply moved to the template file

        if (eBookConfig.enableScratchAC == false) return;

        var divid = eBookConfig.course + "_scratch_ac";
        divid = divid.replace(/[#.]/g, ""); // in case book title has characters that will mess up our selectors
        eBookConfig.scratchDiv = divid;
        let stdin = "";
        var lang = eBookConfig.acDefaultLanguage
            ? eBookConfig.acDefaultLanguage
            : "python";
        if (lang === "java" || lang === "cpp" || lang === "python3") {
            stdin = `data-stdin="text for stdin"`;
        }
        // generate the HTML
        var html = `<div id="ac_modal_${divid}" class="modal fade">
              <div class="modal-dialog scratch-ac-modal">
                <div class="modal-content">
                  <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                    <h4 class="modal-title">Scratch ActiveCode</h4>
                  </div>
                  <div class="modal-body">
                  <div data-component="activecode" id=${divid}>
                  <div id=${divid}_question class="ac_question"><p>Use this area for writing code or taking notes.</p></div>
                  <textarea data-codelens="true" data-lang="${lang}" ${stdin}>




                  </textarea>
                  </div>
                  </div>
                </div>
              </div>
            </div>`;
        var el = $(html);
        $("body").append(el);
        el.on("shown.bs.modal show.bs.modal", function () {
            el.find(".CodeMirror").each(function (i, e) {
                e.CodeMirror.refresh();
                e.CodeMirror.focus();
            });
        });
    }
    static toggleScratchActivecode() {
        var divid = "ac_modal_" + eBookConfig.scratchDiv;
        var div = $("#" + divid);
        $(`#${eBookConfig.scratchDiv}`).removeClass("ac_section");
        div.modal("toggle");
    }
}

//
// Page Initialization
//

$(document).ready(function () {
    ACFactory.createScratchActivecode();
    $("[data-component=activecode]").each(function (index) {
        if ($(this).closest("[data-component=timedAssessment]").length == 0) {
            // If this element exists within a timed component, don't render it here
            try {
                window.edList[this.id] = ACFactory.createActiveCode(
                    this,
                    $(this).find("textarea").data("lang")
                );
            } catch (err) {
                console.log(`Error rendering Activecode Problem ${this.id}
                Details: ${err}`);
            }
        }
    });
    if (loggedout) {
        for (let k in window.edList) {
            window.edList[k].disableSaveLoad();
        }
    } else {
        for (let k in window.edList) {
            window.edList[k].enableSaveLoad();
        }
    }
});

if (typeof window.component_factory === "undefined") {
    window.component_factory = {};
}

window.component_factory.activecode = ACFactory.createActiveCodeFromOpts;

// This is the easiest way to expose this outside the module.
window.ACFactory = ACFactory;

// This seems a bit hacky and possibly brittle, but its hard to know how long it will take to
// figure out the login/logout status of the user.  Sometimes its immediate, and sometimes its
// long.  So to be safe we'll do it both ways..
var loggedout;
$(document).bind("runestone:logout", function () {
    loggedout = true;
});
$(document).bind("runestone:logout", function () {
    for (let k in window.edList) {
        if (window.edList.hasOwnProperty(k)) {
            window.edList[k].disableSaveLoad();
        }
    }
});


/***/ }),

/***/ "./runestone/activecode/js/activecode-i18n.en.js":
/*!*******************************************************!*\
  !*** ./runestone/activecode/js/activecode-i18n.en.js ***!
  \*******************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

$.i18n().load({
    en: {
        msg_activecode_load_history: "Load History",
        msg_activecode_audio_tour: "Audio Tour",
        msg_activecode_loaded_code: "Loaded your saved code.",
        msg_activecode_no_saved_code: "No saved code.",
        msg_activecode_run_code: "Run",
        msg_activecode_show_feedback: "Show Feedback",
        msg_activecode_show_code: "Show Code",
        msg_activecode_hide_code: "Hide Code",
        msg_activecode_show_codelens: "Show CodeLens",
        msg_activecode_show_in_codelens: "Show in CodeLens",
        msg_activecode_hide_codelens: "Hide Codelens",

        msg_activecode_parse_error:
            "A parse error means that Python does not understand the syntax on the line the error message points out. Common examples are forgetting commas beteween arguments or forgetting a : on a for statement",
        msg_activecode_parse_error_fix:
            "To fix a parse error you just need to look carefully at the line with the error and possibly the line before it.  Make sure it conforms to all of Python's rules.",
        msg_activecode_type_error:
            "Type errors most often occur when an expression tries to combine two objects with types that should not be combined.  Like raising a string to a power",
        msg_activecode_type_error_fix:
            "To fix a type error you will most likely need to trace through your code and make sure the variables have the types you expect them to have.  It may be helpful to print out each variable along the way to be sure its value is what you think it should be.",
        msg_activecode_name_error:
            "A name error almost always means that you have used a variable before it has a value.  Often this may be a simple typo, so check the spelling carefully.",
        msg_activecode_name_error_fix:
            "Check the right hand side of assignment statements and your function calls, this is the most likely place for a NameError to be found.",
        msg_activecode_value_error:
            "A ValueError most often occurs when you pass a parameter to a function and the function is expecting one type and you pass another.",
        msg_activecode_value_error_fix:
            "The error message gives you a pretty good hint about the name of the function as well as the value that is incorrect. Look at the error message closely and then trace back to the variable containing the problematic value.",
        msg_activecode_attribute_error:
            "This error message is telling you that the object on the left hand side of the dot, does not have the attribute or method on the right hand side.",
        msg_activecode_attribute_error_fix:
            "The most common variant of this message is that the object undefined does not have attribute X.  This tells you that the object on the left hand side of the dot is not what you think. Trace the variable back and print it out in various places until you discover where it becomes undefined.  Otherwise check the attribute on the right hand side of the dot for a typo.",
        msg_activecode_token_error:
            "Most of the time this error indicates that you have forgotten a right parenthesis or have forgotten to close a pair of quotes.",
        msg_activecode_token_error_fix:
            "Check each line of your program and make sure that your parenthesis are balanced.",
        msg_activecode_time_limit_error:
            "Your program is running too long.  Most programs in this book should run in less than 10 seconds easily. This probably indicates your program is in an infinite loop.",
        msg_activecode_time_limit_error_fix:
            "Add some print statements to figure out if your program is in an infinte loop.  If it is not you can increase the run time with sys.setExecutionLimit(msecs)",
        msg_activecode_general_error:
            "Your program is running for too long.  Most programs in this book should run in less than 30 seconds easily. This probably indicates your program is in an infinite loop.",
        msg_activecode_general_error_fix:
            "Add some print statements to figure out if your program is in an infinte loop.  If it is not you can increase the run time with sys.setExecutionLimit(msecs)",
        msg_activecode_syntax_error:
            "This message indicates that Python can't figure out the syntax of a particular statement.  Some examples are assigning to a literal, or a function call",
        msg_activecode_syntax_error_fix:
            "Check your assignment statements and make sure that the left hand side of the assignment is a variable, not a literal or a function.",
        msg_activecode_key_error:
            "This message indicates that you are trying to access an element of a dictionary, but the dictionary does not have that the key you are using.",
        msg_activecode_key_error_fix:
            "You may have a typo in the name of your key.  It is also good practice to check if the key exists using a statement like if key in mydict.  You can also use mydict.get(key,defaultvalue) so that if the key is not in the dictionary you get the default value instead of an error.",
        msg_activecode_index_error:
            "This message means that you are trying to index past the end of a string or a list.  For example if your list has 3 things in it and you try to access the item at position 3 or more.",
        msg_activecode_index_error_fix:
            "Remember that the first item in a list or string is at index position 0, quite often this message comes about because you are off by one.  Remember in a list of length 3 the last legal index is 2",
        msg_activecode_uri_error: "",
        msg_activecode_uri_error_fix: "",
        msg_activecode_import_error:
            "This error message indicates that you are trying to import a module that does not exist",
        msg_activecode_import_error_fix:
            "One problem may simply be that you have a typo.  It may also be that you are trying to import a module that exists in 'real' Python, but does not exist in this book.  If this is the case, please submit a feature request to have the module added.",
        msg_activecode_reference_error:
            "This is most likely an internal error, particularly if the message references the console.",
        msg_activecode_reference_error_fix:
            "Try refreshing the webpage, and if the error continues, submit a bug report along with your code",
        msg_activecode_zero_division_error:
            "This tells you that you are trying to divide by 0. Typically this is because the value of the variable in the denominator of a division expression has the value 0",
        msg_activecode_zero_division_error_fix:
            "You may need to protect against dividing by 0 with an if statment, or you may need to rexamine your assumptions about the legal values of variables, it could be an earlier statment that is unexpectedly assigning a value of zero to the variable in question.",
        msg_activecode_range_error:
            "This message almost always shows up in the form of Maximum call stack size exceeded.",
        msg_activecode_range_error_fix:
            "This always occurs when a function calls itself.  Its pretty likely that you are not doing this on purpose. Except in the chapter on recursion.  If you are in that chapter then its likely you haven't identified a good base case.",
        msg_activecode_internal_error:
            "An Internal error may mean that you've triggered a bug in our Python",
        msg_activecode_internal_error_fix:
            "Report this error, along with your code as a bug.",
        msg_activecode_indentation_error:
            "This error occurs when you have not indented your code properly.  This is most likely to happen as part of an if, for, while or def statement.",
        msg_activecode_indentation_error_fix:
            "Check your if, def, for, and while statements to be sure the lines are properly indented beneath them.  Another source of this error comes from copying and pasting code where you have accidentally left some bits of code lying around that don't belong there anymore.",
        msg_activecode_not_implemented_error:
            "This error occurs when you try to use a builtin function of Python that has not been implemented in this in-browser version of Python.",
        msg_activecode_not_implemented_error_fix:
            "For now the only way to fix this is to not use the function.  There may be workarounds.  If you really need this builtin function then file a bug report and tell us how you are trying to use the function.",

        msg_activecode_file_not_found: "File not found: '$1'",
        msg_activecode_no_file_or_dir:
            "[Errno 2] No such file or directory: '$1'",
        msg_activecode_starting: "Click the play button to begin the $1",
        msg_activecode_playing: "Playing the $1",
        msg_activecode_loading_audio:
            "Loading audio.  Please wait.   If the tour doesn't start soon click on the leftmost control button (Play first audio in tour)",
        msg_activecode_pause_current_audio: "Pause current audio",
        msg_activecode_pause_audio: "Pause audio",
        msg_activecode_play_paused_audio: "Play paused audio",
        msg_activecode_audio_paused:
            "The $1 has been paused. Click on the play button to resume the tour.",
        msg_activecode_input_prg: "Input for Program",
        msg_activecode_were_compiling_err:
            "There were errors compiling your code. See below.",
        msg_activecode_time_limit_exc: "Time Limit Exceeded on your program",
        msg_activecode_server_err: "A server error occurred: $1 $2",
        msg_activecode_compiling_running:
            "Compiling and Running your Code Now...",
        msg_activecode_server_comm_err: "Error communicating with the server.",
        msg_activecode_save_run: "Save & Run",
        msg_activecode_render: "Save & Render",
        msg_activecode_assertion_error:
            "An assertion error happens when python encounters an assertion statement.  Python evaluates the expression to the right of the word assert; if that expression isTrue everything is fine and the program continues.  If the expression is False Python raises an error and stops.",
        msg_activecode_assertion_error_fix:
            "Check the expression to the right of assert.  The expression is False and you will need to determine why that is.  You may want to simply print out the indidividual parts of the expression to understand why it is evaluating to False.",
        msg_activecode_load_db: "Loading DB...",
    },
});


/***/ }),

/***/ "./runestone/activecode/js/activecode-i18n.pt-br.js":
/*!**********************************************************!*\
  !*** ./runestone/activecode/js/activecode-i18n.pt-br.js ***!
  \**********************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

$.i18n().load({
    "pt-br": {
        msg_activecode_load_history: "Carregar histórico",
        msg_activecode_audio_tour: "Tour de áudio",
        msg_activecode_loaded_code: "Código salvo carregado.",
        msg_activecode_no_saved_code: "Não há código salvo.",
        msg_activecode_run_code: "Executar",
        msg_activecode_show_feedback: "Exibir Feedbacks",
        msg_activecode_show_code: "Mostrar Código",
        msg_activecode_hide_code: "Ocultar Código",
        msg_activecode_show_codelens: "Mostrar CodeLens",
        msg_activecode_show_in_codelens: "Mostrar em CodeLens",
        msg_activecode_hide_codelens: "Ocultar Codelens",

        msg_activecode_parse_error:
            "Um erro de Parse significa que Python não entende a sintaxe da linha que a mensagem de erro aponta. Exemplos comuns são esquecer vírgulas entre argumentos ou esquecer ':' em um comando for.",
        msg_activecode_parse_error_fix:
            "Para corrigir um erro de Parse, você só precisa olhar cuidadosamente a linha com o erro e possivelmente a linha antes dela. Tenha certeza que estão de acordo com todas as regras de Python.",
        msg_activecode_type_error:
            "Erros de tipagem ocorrem mais frequentemente quando uma expressão tenta combinar dois objetos com tipos que não deveriam ser combinados. Como elevar uma String a uma potência.",
        msg_activecode_type_error_fix:
            "Para corrigir um erro de tipagem você provavelmente precisará percorrer seu código e ter certeza de que as variáveis possuem os tipos que você espera que elas tenham. Pode ser útil imprimir cada variável ao longo do caminho para checar se elas possuem o valor que você acha que deveriam ter.",
        msg_activecode_name_error:
            "Um erro de nome quase sempre significa que você usou uma variável antes dela ter um valor. Frequentemente isso pode ser um erro de digitação, então cheque a ortografia cuidadosamente.",
        msg_activecode_name_error_fix:
            "Cheque o lado direito das atribuições e suas chamadas de funções, são os lugares mais prováveis de encontrar um erro de nome.",
        msg_activecode_value_error:
            "Um erro de valor ocorre mais frequentemente quando uma função está esperando um tipo mas você passa um parâmetro de outro tipo.",
        msg_activecode_value_error_fix:
            "A mensagem de erro te dá uma boa dica sobre o nome da função e o valor incorreto. Leia atentamente a mensagem de erro e volte à variável que contém o valor problemático.",
        msg_activecode_attribute_error:
            "Essa mensagem de erro está te dizendo que o objeto do lado esquerdo do ponto não tem o atributo ou método do seu lado direito.",
        msg_activecode_attribute_error_fix:
            "A variante mais comum dessa mensagem é que o objeto indefinido não tem o atributo X. Isso diz que o objeto do lado esquerdo do ponto não é o que você pensa. Rastreie essa variável e imprima-a em vários lugares até descobrir onde ela se torna indefinida. Caso contrário, cheque se há erro de digitação no atributo do lado direito do ponto.",
        msg_activecode_token_error:
            "Na maioria das vezes este erro indica que você esqueceu de fechar um parênteses ou aspas.",
        msg_activecode_token_error_fix:
            "Cheque cada linha do seu programa e certifique-se de que todos os parênteses estão fechados.",
        msg_activecode_time_limit_error:
            "Seu programa está demorando muito. Os programas deste livro deveriam rodar em menos de 10 segundos. Isso provavelmente indica que seu programa está em um loop infinito.",
        msg_activecode_time_limit_error_fix:
            "Imprima mensagens em algumas linhas para descobrir se seu programa está em um loop infinito. Caso não esteja, você pode aumentar o tempo limite de execução com o comando sys.setExecutionLimit(ms)",
        msg_activecode_general_error:
            "Seu programa está demorando muito. Os programas deste livro deveriam rodar em menos de 30 segundos. Isso provavelmente indica que seu programa está em um loop infinito.",
        msg_activecode_general_error_fix:
            "Imprima mensagens em algumas linhas para descobrir se seu programa está em um loop infinito. Caso não esteja, você pode aumentar o tempo limite de execução com o comando sys.setExecutionLimit(ms)",
        msg_activecode_syntax_error:
            "Esta mensagem indica que Python não entendeu a sintaxe de um comando. Alguns exemplos são atribuição de um literal, ou uma chamada de função",
        msg_activecode_syntax_error_fix:
            "Cheque as atribuições de seu programa e certifique-se de que o lado esquerdo é uma variável, e não um literal ou função.",
        msg_activecode_key_error:
            "Esta mensagem indica que você está tentando acessar um elemento cuja chave não existe no dicionário.",
        msg_activecode_key_error_fix:
            "Você pode ter um erro de digitação no nome da sua chave. É uma boa prática checar se a chave existe usando um comando 'if (chave) in meu_dicionario'. Você também pode usar o comando 'meu_dicionario.get(chave, valorPadrão)' para obter o valor padrão ao invés de um erro caso a chave não exista.",
        msg_activecode_index_error:
            "Você está tentando acessar um índice além do final de uma string ou lista. Por exemplo, se sua lista possui 3 elementos nela e você tenta acessar o item na posição 3 ou mais.",
        msg_activecode_index_error_fix:
            "Lembre-se que o primeiro elemento de uma lista ou string está no índice 0, normalmente esta mensagem é exibida porque você errou o índice por 1. Em uma lista de tamanho 3, o último índice válido é 2",
        msg_activecode_uri_error: "",
        msg_activecode_uri_error_fix: "",
        msg_activecode_import_error:
            "Esta mensagem indica que você está tentando importar um módulo que não existe",
        msg_activecode_import_error_fix:
            "Um problema pode ser apenas um erro de digitação. Também pode ser que você está tentando importar um módulo que existe em Python 'real', mas não existe neste livro. Se este é o caso, por favor envie um pedido para ter esse módulo adicionado.",
        msg_activecode_reference_error:
            "Isso parece ser um erro interno, especialmente se a mensagem faz referência ao console.",
        msg_activecode_reference_error_fix:
            "Tente atualizar a página. Se o erro persistir, envie um relatório de bug junto com seu código",
        msg_activecode_zero_division_error:
            "Isso diz que você está tentando dividir por 0. Normalmente é porque o valor da variável no denominador de uma divisão tem o valor 0",
        msg_activecode_zero_division_error_fix:
            "Você pode proteger contra dividir por 0 com um comando 'if', ou você pode precisar reavaliar suas suposições sobre os valores das variáveis, pois um comando anterior pode ter inesperadamente atribuído o valor 0 a essa variável.",
        msg_activecode_range_error:
            "Esta mensagem quase sempre aparece na forma de 'Tamanho máximo da pilha de chamadas excedido'.",
        msg_activecode_range_error_fix:
            "Isso sempre ocorre quando uma função chama ela mesma. Provavelmente você não está fazendo isso de propósito, exceto no capítulo sobre recursão. Se você está nesse capítulo, então provavelmente ainda não identificou um bom caso base.",
        msg_activecode_internal_error:
            "Um erro interno pode significar que você desencadeou um bug no nosso Python",
        msg_activecode_internal_error_fix:
            "Reporte este erro como um bug, juntamente com seu código",
        msg_activecode_indentation_error:
            "Este erro ocorre quando você não indentou seu código devidamente. Isso é mais provável de ocorrer como parte de um comando if, for, while ou def.",
        msg_activecode_indentation_error_fix:
            "Cheque seus comandos if, def, for e while para ter certeza de que as linhas estão devidamente indentadas abaixo delas. Outra fonte deste erro é copiar e colar código em que você acidentalmente deixou restos de código que não pertencem mais ali.",
        msg_activecode_not_implemented_error:
            "Este erro ocorre quando você tenta usar uma função embutida de Python que não foi implementada nesta versão browser de Python.",
        msg_activecode_not_implemented_error_fix:
            "Por enquanto a única maneira de consertar isso é não usando a função. Podem haver soluções alternativas. Se você realmente precisa desta função embutida, envie-nos um relatório de bug e digac omo está tentando usar a função.",

        msg_activecode_file_not_found: "Arquivo não encontrado: '$1'",
        msg_activecode_no_file_or_dir:
            "[Erro nº 2] Arquivo ou diretório inexistente: '$1'",
        msg_activecode_starting: "Clique no botão de execução para começar $1",
        msg_activecode_playing: "Executando $1",
        msg_activecode_loading_audio:
            "Carregando áudio. Por favor, espere. Se o tour não começar logo, clique em 'Stop Tour' e tente novamente.",
        msg_activecode_pause_current_audio: "Pausar áudio atual",
        msg_activecode_pause_audio: "Pausar áudio",
        msg_activecode_play_paused_audio: "Reproduzir áudio pausado",
        msg_activecode_audio_paused:
            "$1 foi pausado. Clique no botão de execução para retomar o tour.",
        msg_activecode_input_prg: "Entrada para o programa",
        msg_activecode_were_compiling_err:
            "Houveram erros ao compilar seu código. Veja abaixo.",
        msg_activecode_time_limit_exc: "Limite de tempo excedido no seu programa",
        msg_activecode_server_err: "Um erro de servidor ocorreu: $1 $2",
        msg_activecode_compiling_running:
            "Compilando e executando seu código...",
        msg_activecode_server_comm_err: "Erro ao comunicar com o servidor.",
        msg_activecode_save_run: "Salvar & Executar",
        msg_activecode_render: "Salvar & Renderizar",
        msg_activecode_assertion_error:
            "Um erro de asserção ocorre quando python encontra um comando 'assert'. Python avalia a expressão do lado direito; se é verdadeira, o programa continua normalmente. Se é falsa, python gera um erro e para a execução." ,
        msg_activecode_assertion_error_fix:
            "Cheque a expressão do lado direito de 'assert'. Essa expressão é falsa e é preciso verificar o motivo. Você pode querer imprimir cada parte da expressão e entender porque é falsa.",
        msg_activecode_load_db: "Carregando banco de dados...",
    },
});


/***/ }),

/***/ "./runestone/activecode/js/activecode.js":
/*!***********************************************!*\
  !*** ./runestone/activecode/js/activecode.js ***!
  \***********************************************/
/*! exports provided: ActiveCode */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ActiveCode", function() { return ActiveCode; });
/* harmony import */ var _common_js_runestonebase_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/js/runestonebase.js */ "./runestone/common/js/runestonebase.js");
/* harmony import */ var _audiotour__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./audiotour */ "./runestone/activecode/js/audiotour.js");
/* harmony import */ var _activecode_i18n_en__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./activecode-i18n.en */ "./runestone/activecode/js/activecode-i18n.en.js");
/* harmony import */ var _activecode_i18n_en__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_activecode_i18n_en__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var _activecode_i18n_pt_br__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./activecode-i18n.pt-br */ "./runestone/activecode/js/activecode-i18n.pt-br.js");
/* harmony import */ var _activecode_i18n_pt_br__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_activecode_i18n_pt_br__WEBPACK_IMPORTED_MODULE_3__);
/* harmony import */ var codemirror__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! codemirror */ "./node_modules/codemirror/lib/codemirror.js");
/* harmony import */ var codemirror__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(codemirror__WEBPACK_IMPORTED_MODULE_4__);
/* harmony import */ var codemirror_mode_python_python_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! codemirror/mode/python/python.js */ "./node_modules/codemirror/mode/python/python.js");
/* harmony import */ var codemirror_mode_python_python_js__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(codemirror_mode_python_python_js__WEBPACK_IMPORTED_MODULE_5__);
/* harmony import */ var codemirror_mode_css_css_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! codemirror/mode/css/css.js */ "./node_modules/codemirror/mode/css/css.js");
/* harmony import */ var codemirror_mode_css_css_js__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(codemirror_mode_css_css_js__WEBPACK_IMPORTED_MODULE_6__);
/* harmony import */ var codemirror_mode_htmlmixed_htmlmixed_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! codemirror/mode/htmlmixed/htmlmixed.js */ "./node_modules/codemirror/mode/htmlmixed/htmlmixed.js");
/* harmony import */ var codemirror_mode_htmlmixed_htmlmixed_js__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(codemirror_mode_htmlmixed_htmlmixed_js__WEBPACK_IMPORTED_MODULE_7__);
/* harmony import */ var codemirror_mode_xml_xml_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! codemirror/mode/xml/xml.js */ "./node_modules/codemirror/mode/xml/xml.js");
/* harmony import */ var codemirror_mode_xml_xml_js__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(codemirror_mode_xml_xml_js__WEBPACK_IMPORTED_MODULE_8__);
/* harmony import */ var codemirror_mode_javascript_javascript_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! codemirror/mode/javascript/javascript.js */ "./node_modules/codemirror/mode/javascript/javascript.js");
/* harmony import */ var codemirror_mode_javascript_javascript_js__WEBPACK_IMPORTED_MODULE_9___default = /*#__PURE__*/__webpack_require__.n(codemirror_mode_javascript_javascript_js__WEBPACK_IMPORTED_MODULE_9__);
/* harmony import */ var codemirror_mode_sql_sql_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! codemirror/mode/sql/sql.js */ "./node_modules/codemirror/mode/sql/sql.js");
/* harmony import */ var codemirror_mode_sql_sql_js__WEBPACK_IMPORTED_MODULE_10___default = /*#__PURE__*/__webpack_require__.n(codemirror_mode_sql_sql_js__WEBPACK_IMPORTED_MODULE_10__);
/* harmony import */ var codemirror_mode_clike_clike_js__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! codemirror/mode/clike/clike.js */ "./node_modules/codemirror/mode/clike/clike.js");
/* harmony import */ var codemirror_mode_clike_clike_js__WEBPACK_IMPORTED_MODULE_11___default = /*#__PURE__*/__webpack_require__.n(codemirror_mode_clike_clike_js__WEBPACK_IMPORTED_MODULE_11__);
/* harmony import */ var codemirror_mode_octave_octave_js__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! codemirror/mode/octave/octave.js */ "./node_modules/codemirror/mode/octave/octave.js");
/* harmony import */ var codemirror_mode_octave_octave_js__WEBPACK_IMPORTED_MODULE_12___default = /*#__PURE__*/__webpack_require__.n(codemirror_mode_octave_octave_js__WEBPACK_IMPORTED_MODULE_12__);
/* harmony import */ var _css_activecode_css__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./../css/activecode.css */ "./runestone/activecode/css/activecode.css");
/* harmony import */ var _css_activecode_css__WEBPACK_IMPORTED_MODULE_13___default = /*#__PURE__*/__webpack_require__.n(_css_activecode_css__WEBPACK_IMPORTED_MODULE_13__);
/* harmony import */ var codemirror_lib_codemirror_css__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! codemirror/lib/codemirror.css */ "./node_modules/codemirror/lib/codemirror.css");
/* harmony import */ var codemirror_lib_codemirror_css__WEBPACK_IMPORTED_MODULE_14___default = /*#__PURE__*/__webpack_require__.n(codemirror_lib_codemirror_css__WEBPACK_IMPORTED_MODULE_14__);
/**
 *
 * Created by bmiller on 3/19/15.
 */
/* Define global variables for ESLint */
/* global Sk */




















var isMouseDown = false;
document.onmousedown = function () {
    isMouseDown = true;
};

document.onmouseup = function () {
    isMouseDown = false;
};
window.edList = {};

var socket, connection, doc;
var chatcodesServer = "chat.codes";

// separate into constructor and init
class ActiveCode extends _common_js_runestonebase_js__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(opts) {
        super(opts);
        var suffStart;
        var orig = $(opts.orig).find("textarea")[0];
        this.useRunestoneServices = opts.useRunestoneServices;
        this.python3 = opts.python3;
        this.alignVertical = opts.vertical;
        this.origElem = orig;
        this.origText = this.origElem.textContent;
        this.divid = opts.orig.id;
        this.code = $(orig).text() || "\n\n\n\n\n";
        this.language = $(orig).data("lang");
        this.timelimit = $(orig).data("timelimit");
        this.includes = $(orig).data("include");
        this.hidecode = $(orig).data("hidecode");
        this.chatcodes = $(orig).data("chatcodes");
        this.hidehistory = $(orig).data("hidehistory");
        this.question = $(opts.orig).find(`#${this.divid}_question`)[0];
        this.tie = $(orig).data("tie");
        this.dburl = $(orig).data("dburl");
        this.runButton = null;
        this.enabledownload = $(orig).data("enabledownload");
        this.downloadButton = null;
        this.saveButton = null;
        this.loadButton = null;
        this.outerDiv = null;
        this.partner = "";
        this.logResults = true;
        if (!eBookConfig.allow_pairs || $(orig).data("nopair")) {
            this.enablePartner = false;
        } else {
            this.enablePartner = true;
        }
        this.output = null; // create pre for output
        this.graphics = null; // create div for turtle graphics
        this.codecoach = null;
        this.codelens = null;
        this.controlDiv = null;
        this.historyScrubber = null;
        this.timestamps = ["Original"];
        this.autorun = $(orig).data("autorun");
        if (this.chatcodes && eBookConfig.enable_chatcodes) {
            if (!socket) {
                socket = new WebSocket("wss://" + chatcodesServer);
            }
            if (!connection) {
                connection = new window.sharedb.Connection(socket);
            }
            if (!doc) {
                doc = connection.get("chatcodes", "channels");
            }
        }
        if (this.graderactive) {
            this.hidecode = false;
        }
        if (this.includes) {
            this.includes = this.includes.split(/\s+/);
        }
        let prefixEnd = this.code.indexOf("^^^^");
        if (prefixEnd > -1) {
            this.prefix = this.code.substring(0, prefixEnd);
            this.code = this.code.substring(prefixEnd + 5);
        }
        suffStart = this.code.indexOf("====");
        if (suffStart > -1) {
            this.suffix = this.code.substring(suffStart + 5);
            this.code = this.code.substring(0, suffStart);
        }
        this.history = [this.code];
        this.createEditor();
        this.createOutput();
        this.createControls();
        if ($(orig).data("caption")) {
            this.caption = $(orig).data("caption");
        } else {
            this.caption = "ActiveCode";
        }
        this.addCaption("runestone");
        setTimeout(
            function () {
                this.editor.refresh();
            }.bind(this),
            1000
        );
        if (this.autorun) {
            $(document).ready(this.runProg.bind(this));
        }
    }

    createEditor(index) {
        this.containerDiv = document.createElement("div");
        var linkdiv = document.createElement("div");
        linkdiv.id = this.divid.replace(/_/g, "-").toLowerCase(); // :ref: changes _ to - so add this as a target
        $(this.containerDiv).addClass("ac_section alert alert-warning");
        var codeDiv = document.createElement("div");
        $(codeDiv).addClass("ac_code_div col-md-12");
        this.codeDiv = codeDiv;
        this.containerDiv.id = this.divid;
        this.containerDiv.lang = this.language;
        this.outerDiv = this.containerDiv;
        $(this.origElem).replaceWith(this.containerDiv);
        if (linkdiv.id !== this.divid) {
            // Don't want the 'extra' target if they match.
            this.containerDiv.appendChild(linkdiv);
        }
        this.containerDiv.appendChild(codeDiv);
        var edmode = this.containerDiv.lang;
        if (edmode === "sql") {
            edmode = "text/x-sql";
        } else if (edmode === "java") {
            edmode = "text/x-java";
        } else if (edmode === "cpp") {
            edmode = "text/x-c++src";
        } else if (edmode === "c") {
            edmode = "text/x-csrc";
        } else if (edmode === "python3") {
            edmode = "python";
        } else if (edmode === "octave" || edmode === "MATLAB") {
            edmode = "text/x-octave";
        }
        var editor = codemirror__WEBPACK_IMPORTED_MODULE_4___default()(codeDiv, {
            value: this.code,
            lineNumbers: true,
            mode: edmode,
            indentUnit: 4,
            matchBrackets: true,
            autoMatchParens: true,
            extraKeys: {
                Tab: "indentMore",
                "Shift-Tab": "indentLess",
            },
        });
        // Make the editor resizable
        $(editor.getWrapperElement()).resizable({
            resize: function () {
                editor.setSize($(this).width(), $(this).height());
                editor.refresh();
            },
        });
        // give the user a visual cue that they have changed but not saved
        editor.on(
            "change",
            function (ev) {
                if (
                    editor.acEditEvent == false ||
                    editor.acEditEvent === undefined
                ) {
                    // change events can come before any real changes for various reasons, some unknown
                    // this avoids unneccsary log events and updates to the activity counter
                    if (this.origText === editor.getValue()) {
                        return;
                    }
                    $(editor.getWrapperElement()).css(
                        "border-top",
                        "2px solid #b43232"
                    );
                    $(editor.getWrapperElement()).css(
                        "border-bottom",
                        "2px solid #b43232"
                    );
                    this.isAnswered = true;
                    this.logBookEvent({
                        event: "activecode",
                        act: "edit",
                        div_id: this.divid,
                    });
                }
                editor.acEditEvent = true;
            }.bind(this)
        ); // use bind to preserve *this* inside the on handler.
        //Solving Keyboard Trap of ActiveCode: If user use tab for navigation outside of ActiveCode, then change tab behavior in ActiveCode to enable tab user to tab out of the textarea
        $(window).keydown(function (e) {
            var code = e.keyCode ? e.keyCode : e.which;
            if (code == 9 && $("textarea:focus").length === 0) {
                editor.setOption("extraKeys", {
                    Tab: function (cm) {
                        $(document.activeElement)
                            .closest(".tab-content")
                            .nextSibling.focus();
                    },
                    "Shift-Tab": function (cm) {
                        $(document.activeElement)
                            .closest(".tab-content")
                            .previousSibling.focus();
                    },
                });
            }
        });
        this.editor = editor;
        if (this.hidecode) {
            $(this.codeDiv).css("display", "none");
        }
    }

    async runButtonHander() {
        try {
            await this.runProg();
        } catch (e) {
            console.log(`there was an error ${e} running the code`);
        }
        if (this.logResults) {
            this.logCurrentAnswer();
        }
        this.renderFeedback();
    }

    createControls() {
        var ctrlDiv = document.createElement("div");
        var butt;
        $(ctrlDiv).addClass("ac_actions");
        $(ctrlDiv).addClass("col-md-12");
        // Run
        butt = document.createElement("button");
        $(butt).text($.i18n("msg_activecode_run_code"));
        $(butt).addClass("btn btn-success run-button");
        ctrlDiv.appendChild(butt);
        this.runButton = butt;
        console.log("adding click function for run");
        this.runButton.onclick = this.runButtonHander.bind(this);
        $(butt).attr("type", "button");

        if (this.enabledownload || eBookConfig.downloadsEnabled) {
            this.addDownloadButton(ctrlDiv);
        }
        if (!this.hidecode && !this.hidehistory) {
            this.addHistoryButton(ctrlDiv);
        }
        if ($(this.origElem).data("gradebutton") && !this.graderactive) {
            this.addFeedbackButton(ctrlDiv);
        }
        // Show/Hide Code
        if (this.hidecode) {
            this.enableHideShow(ctrlDiv);
        }
        // CodeLens
        if ($(this.origElem).data("codelens") && !this.graderactive) {
            this.enableCodeLens(ctrlDiv);
        }
        // Audio Tour
        if ($(this.origElem).data("audio")) {
            this.enableAudioTours(ctrlDiv);
        }
        if (eBookConfig.isInstructor) {
            this.enableInstructorSharing(ctrlDiv);
        }
        if (this.enablePartner) {
            this.setupPartner(ctrlDiv);
        }
        if (this.chatcodes && eBookConfig.enable_chatcodes) {
            this.enableChatCodes(ctrlDiv);
        }

        $(this.outerDiv).prepend(ctrlDiv);
        if (this.question) {
            if ($(this.question).html().match(/^\s+$/)) {
                $(this.question).remove();
            } else {
                $(this.outerDiv).prepend(this.question);
            }
        }
        this.controlDiv = ctrlDiv;
    }

    addFeedbackButton(ctrlDiv) {
        let butt = document.createElement("button");
        $(butt).addClass("ac_opt btn btn-default");
        $(butt).text($.i18n("msg_activecode_show_feedback"));
        $(butt).css("margin-left", "10px");
        $(butt).attr("type", "button");
        this.gradeButton = butt;
        ctrlDiv.appendChild(butt);
        $(butt).click(this.createGradeSummary.bind(this));
    }

    addHistoryButton(ctrlDiv) {
        let butt = document.createElement("button");
        $(butt).text($.i18n("msg_activecode_load_history"));
        $(butt).addClass("btn btn-default");
        $(butt).attr("type", "button");
        ctrlDiv.appendChild(butt);
        this.histButton = butt;
        $(butt).click(this.addHistoryScrubber.bind(this));
        if (this.graderactive) {
            this.addHistoryScrubber(true);
        }
    }

    addDownloadButton(ctrlDiv) {
        let butt = document.createElement("button");
        $(butt).text("Download");
        $(butt).addClass("btn save-button");
        ctrlDiv.appendChild(butt);
        this.downloadButton = butt;
        $(butt).click(this.downloadFile.bind(this, this.language));
        $(butt).attr("type", "button");
    }

    enableHideShow(ctrlDiv) {
        $(this.runButton).attr("disabled", "disabled");
        let butt = document.createElement("button");
        $(butt).addClass("ac_opt btn btn-default");
        $(butt).text($.i18n("msg_activecode_show_code"));
        $(butt).css("margin-left", "10px");
        $(butt).attr("type", "button");
        this.showHideButt = butt;
        ctrlDiv.appendChild(butt);
        $(butt).click(
            function () {
                $(this.codeDiv).toggle();
                if (this.historyScrubber == null) {
                    this.addHistoryScrubber(true);
                } else {
                    $(this.historyScrubber.parentElement).toggle();
                }
                if (
                    $(this.showHideButt).text() ==
                    $.i18n("msg_activecode_show_code")
                ) {
                    $(this.showHideButt).text(
                        $.i18n("msg_activecode_hide_code")
                    );
                } else {
                    $(this.showHideButt).text(
                        $.i18n("msg_activecode_show_code")
                    );
                }
                if ($(this.runButton).attr("disabled")) {
                    $(this.runButton).removeAttr("disabled");
                } else {
                    $(this.runButton).attr("disabled", "disabled");
                }
            }.bind(this)
        );
    }

    enableCodeLens(ctrlDiv) {
        let butt = document.createElement("button");
        $(butt).addClass("ac_opt btn btn-default");
        $(butt).text($.i18n("msg_activecode_show_codelens"));
        $(butt).css("margin-left", "10px");
        this.clButton = butt;
        ctrlDiv.appendChild(butt);
        $(butt).click(this.showCodelens.bind(this));
    }

    enableAudioTours(ctrlDiv) {
        let butt = document.createElement("button");
        $(butt).addClass("ac_opt btn btn-default");
        $(butt).text($.i18n("msg_activecode_audio_tour"));
        $(butt).css("margin-left", "10px");
        this.atButton = butt;
        ctrlDiv.appendChild(butt);
        $(butt).click(
            function () {
                new _audiotour__WEBPACK_IMPORTED_MODULE_1__["default"](
                    this.divid,
                    this.code,
                    1,
                    $(this.origElem).data("audio")
                );
            }.bind(this)
        );
    }

    enableInstructorSharing(ctrlDiv) {
        let butt = document.createElement("button");
        $(butt).addClass("btn btn-info");
        $(butt).text("Share Code");
        $(butt).css("margin-left", "10px");
        this.shareButt = butt;
        ctrlDiv.appendChild(butt);
        $(butt).click(
            async function () {
                if (
                    !confirm(
                        "You are about to share this code with ALL of your students.  Are you sure you want to continue?"
                    )
                ) {
                    return;
                }
                let data = {
                    divid: this.divid,
                    code: this.editor.getValue(),
                    lang: this.language,
                };
                let request = new Request(
                    eBookConfig.ajaxURL + "broadcast_code.json",
                    {
                        method: "POST",
                        headers: this.jsonHeaders,
                        body: JSON.stringify(data),
                    }
                );
                let post_promise = await fetch(request);
                let status = await post_promise.json();
                if (status.mess === "success") {
                    alert(`Shared Code with ${status.share_count} students`);
                } else {
                    alert("Sharing Failed");
                }
            }.bind(this)
        );
    }

    setupPartner(ctrlDiv) {
        var checkPartner = document.createElement("input");
        checkPartner.type = "checkbox";
        checkPartner.id = `${this.divid}_part`;
        ctrlDiv.appendChild(checkPartner);
        var plabel = document.createElement("label");
        plabel.for = `${this.divid}_part`;
        $(plabel).text("Pair?");
        ctrlDiv.appendChild(plabel);
        $(checkPartner).click(
            function () {
                if (this.partner) {
                    this.partner = false;
                    $(partnerTextBox).hide();
                    this.partner = "";
                    partnerTextBox.value = "";
                    $(plabel).text("Pair?");
                } else {
                    let didAgree = localStorage.getItem("partnerAgree");
                    if (!didAgree) {
                        didAgree = confirm(
                            "Pair Programming should only be used with the consent of your instructor." +
                            "Your partner must be a registered member of the class and have agreed to pair with you." +
                            "By clicking OK you certify that both of these conditions have been met."
                        );
                        if (didAgree) {
                            localStorage.setItem("partnerAgree", "true");
                        } else {
                            return;
                        }
                    }
                    this.partner = true;
                    $(plabel).text("with: ");
                    $(partnerTextBox).show();
                }
            }.bind(this)
        );
        var partnerTextBox = document.createElement("input");
        partnerTextBox.type = "text";
        ctrlDiv.appendChild(partnerTextBox);
        $(partnerTextBox).hide();
        $(partnerTextBox).change(
            function () {
                this.partner = partnerTextBox.value;
            }.bind(this)
        );
    }

    // This is probably obsolete.  Not sure if anyone at Michigan will come back
    // to working on this again.
    enableChatCodes(ctrlDiv) {
        var chatBar = document.createElement("div");
        var channels = document.createElement("span");
        var topic = window.location.host + "-" + this.divid;
        ctrlDiv.appendChild(chatBar);
        $(chatBar).text("Chat: ");
        $(chatBar).append(channels);
        let butt = document.createElement("a");
        $(butt).addClass("ac_opt btn btn-default");
        $(butt).text("Create Channel");
        $(butt).css("margin-left", "10px");
        $(butt).attr("type", "button");
        $(butt).attr("target", "_blank");
        $(butt).attr(
            "href",
            "http://" +
            chatcodesServer +
            "/new?" +
            $.param({
                topic: window.location.host + "-" + this.divid,
                code: this.editor.getValue(),
                lang: "Python",
            })
        );
        this.chatButton = butt;
        chatBar.appendChild(butt);
        var updateChatCodesChannels = function () {
            var data = doc.data;
            var i = 1;
            $(channels).html("");
            data["channels"].forEach(function (channel) {
                if (!channel.archived && topic === channel.topic) {
                    var link = $("<a />");
                    var href =
                        "http://" + chatcodesServer + "/" + channel.channelName;
                    link.attr({
                        href: href,
                        target: "_blank",
                    });
                    link.text(" " + channel.channelName + "(" + i + ") ");
                    $(channels).append(link);
                    i++;
                }
            });
            if (i === 1) {
                $(channels).text("(no active converstations on this problem)");
            }
        };
        doc.subscribe(updateChatCodesChannels);
        doc.on("op", updateChatCodesChannels);
    }

    enableSaveLoad() {
        $(this.runButton).text($.i18n("msg_activecode_save_run"));
    }

    // Activecode -- If the code has not changed wrt the scrubber position value then don't save the code or reposition the scrubber
    //  -- still call runlog, but add a parameter to not save the code
    // add an initial load history button
    // if there is no edit then there is no append   to_save (True/False)
    async addHistoryScrubber(pos_last) {
        let response;
        var reqData = {
            acid: this.divid,
        };
        if (this.sid !== undefined) {
            reqData["sid"] = this.sid;
        }
        console.log("before get hist");
        if (
            eBookConfig.practice_mode ||
            (this.isTimed && !this.assessmentTaken)
        ) {
            // If this is timed and already taken we should restore history info
            this.renderScrubber();
        } else {
            let request = new Request(eBookConfig.ajaxURL + "gethist.json", {
                method: "POST",
                headers: this.jsonHeaders,
                body: JSON.stringify(reqData),
            });
            try {
                response = await fetch(request);
                let data = await response.json();
                if (data.history !== undefined) {
                    this.history = this.history.concat(data.history);
                    for (let t in data.timestamps) {
                        this.timestamps.push(
                            new Date(data.timestamps[t]).toLocaleString()
                        );
                    }
                }
            } catch (e) {
                console.log("unable to fetch history");
            }
            this.renderScrubber(pos_last);
        }
        return "success";
    }

    renderScrubber(pos_last) {
        console.log("making a new scrubber");
        var scrubberDiv = document.createElement("div");
        $(scrubberDiv).css("display", "inline-block");
        $(scrubberDiv).css("margin-left", "10px");
        $(scrubberDiv).css("margin-right", "10px");
        $(scrubberDiv).css({
            "min-width": "200px",
            "max-width": "300px",
        });
        var scrubber = document.createElement("div");
        this.timestampP = document.createElement("span");
        this.slideit = function () {
            this.editor.setValue(this.history[$(scrubber).slider("value")]);
            var curVal = this.timestamps[$(scrubber).slider("value")];
            let pos = $(scrubber).slider("value");
            let outOf = this.history.length;
            $(this.timestampP).text(`${curVal} - ${pos + 1} of ${outOf}`);
            this.logBookEvent({
                event: "activecode",
                act: "slide:" + curVal,
                div_id: this.divid,
            });
        };
        $(scrubber).slider({
            max: this.history.length - 1,
            value: this.history.length - 1,
        });
        $(scrubber).css("margin", "10px");
        $(scrubber).on("slide", this.slideit.bind(this));
        $(scrubber).on("slidechange", this.slideit.bind(this));
        scrubberDiv.appendChild(scrubber);
        scrubberDiv.appendChild(this.timestampP);
        // If there is a deadline set then position the scrubber at the last submission
        // prior to the deadline
        if (this.deadline) {
            let i = 0;
            let done = false;
            while (i < this.history.length && !done) {
                if (new Date(this.timestamps[i]) > this.deadline) {
                    done = true;
                } else {
                    i += 1;
                }
            }
            i = i - 1;
            scrubber.value = Math.max(i, 0);
            this.editor.setValue(this.history[scrubber.value]);
            $(scrubber).slider("value", scrubber.value);
        } else if (pos_last) {
            scrubber.value = this.history.length - 1;
            this.editor.setValue(this.history[scrubber.value]);
        } else {
            scrubber.value = 0;
        }
        let pos = $(scrubber).slider("value");
        let outOf = this.history.length;
        let ts = this.timestamps[$(scrubber).slider("value")];
        $(this.timestampP).text(`${ts} - ${pos + 1} of ${outOf}`);
        $(this.histButton).remove();
        this.histButton = null;
        this.historyScrubber = scrubber;
        $(scrubberDiv).insertAfter(this.runButton);
    } // end definition of helper

    createOutput() {
        // Create a parent div with two elements:  pre for standard output and a div
        // to hold turtle graphics output.  We use a div in case the turtle changes from
        // using a canvas to using some other element like svg in the future.
        var outDiv = document.createElement("div");
        $(outDiv).addClass("ac_output col-md-12");
        this.outDiv = outDiv;
        this.output = document.createElement("pre");
        this.output.id = this.divid + "_stdout";
        $(this.output).css("visibility", "hidden");
        this.graphics = document.createElement("div");
        this.graphics.id = this.divid + "_graphics";
        $(this.graphics).addClass("ac-canvas");
        // This bit of magic adds an event which waits for a canvas child to be created on our
        // newly created div.  When a canvas child is added we add a new class so that the visible
        // canvas can be styled in CSS.  Which a the moment means just adding a border.
        $(this.graphics).on(
            "DOMNodeInserted",
            "canvas",
            function () {
                $(this.graphics).addClass("visible-ac-canvas");
            }.bind(this)
        );
        var clearDiv = document.createElement("div");
        $(clearDiv).css("clear", "both"); // needed to make parent div resize properly
        this.outerDiv.appendChild(clearDiv);
        outDiv.appendChild(this.output);
        outDiv.appendChild(this.graphics);
        this.outerDiv.appendChild(outDiv);
        var lensDiv = document.createElement("div");
        lensDiv.id = `${this.divid}_codelens`;
        $(lensDiv).addClass("col-md-12");
        $(lensDiv).css("display", "none");
        this.codelens = lensDiv;
        this.outerDiv.appendChild(lensDiv);
        var coachDiv = document.createElement("div");
        $(coachDiv).addClass("col-md-12");
        $(coachDiv).css("display", "none");
        this.codecoach = coachDiv;
        this.outerDiv.appendChild(coachDiv);
        clearDiv = document.createElement("div");
        $(clearDiv).css("clear", "both"); // needed to make parent div resize properly
        this.outerDiv.appendChild(clearDiv);
    }

    disableSaveLoad() {
        $(this.saveButton).addClass("disabled");
        $(this.saveButton).attr("title", "Login to save your code");
        $(this.loadButton).addClass("disabled");
        $(this.loadButton).attr("title", "Login to load your code");
    }

    downloadFile(lang) {
        var fnb = this.divid;
        var d = new Date();
        var fileName =
            fnb +
            "_" +
            d
                .toJSON()
                .substring(0, 10) // reverse date format
                .split("-")
                .join("") +
            "." +
            languageExtensions[lang];
        var code = this.editor.getValue();
        if ("Blob" in window) {
            var textToWrite = code.replace(/\n/g, "\r\n");
            var textFileAsBlob = new Blob([textToWrite], {
                type: "text/plain",
            });
            if ("msSaveOrOpenBlob" in navigator) {
                navigator.msSaveOrOpenBlob(textFileAsBlob, fileName);
            } else {
                var downloadLink = document.createElement("a");
                downloadLink.download = fileName;
                downloadLink.innerHTML = "Download File";
                downloadLink.href = window.URL.createObjectURL(textFileAsBlob);
                downloadLink.style.display = "none";
                document.body.appendChild(downloadLink);
                downloadLink.click();
            }
        } else {
            alert("Your browser does not support the HTML5 Blob.");
        }
    }

    async createGradeSummary() {
        // get grade and comments for this assignment
        // get summary of all grades for this student
        // display grades in modal window
        var data = {
            div_id: this.divid,
        };
        let request = new Request(eBookConfig.ajaxURL + "getassignmentgrade", {
            method: "POST",
            headers: this.jsonHeaders,
            body: JSON.stringify(data),
        });
        let response = await fetch(request);
        let report = await response.json();
        var body;
        // check for report['message']
        if (report) {
            if (report["version"] == 2) {
                // new version; would be better to embed this in HTML for the activecode
                body =
                    "<h4>Grade Report</h4>" +
                    "<p>This question: " +
                    report["grade"];
                if (report["released"]) {
                    body += " out of " + report["max"];
                }
                body += "</p> <p>";
                if (report["released"] == false) {
                    body += "Preliminary Comments: ";
                }
                body += report["comment"] + "</p>";
            } else {
                body =
                    "<h4>Grade Report</h4>" +
                    "<p>This assignment: " +
                    report["grade"] +
                    "</p>" +
                    "<p>" +
                    report["comment"] +
                    "</p>" +
                    "<p>Number of graded assignments: " +
                    report["count"] +
                    "</p>" +
                    "<p>Average score: " +
                    report["avg"] +
                    "</p>";
            }
        } else {
            body = "<h4>The server did not return any grade information</h4>";
        }
        var html = `<div class="modal fade">
                  <div class="modal-dialog compare-modal">
                    <div class="modal-content">
                      <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                        <h4 class="modal-title">Assignment Feedback</h4>
                      </div>
                      <div class="modal-body">
                        ${body}
                      </div>
                    </div>
                  </div>
                </div>`;
        var el = $(html);
        el.modal();

        return response;
    }

    async showCodelens() {
        if (this.codelens.style.display == "none") {
            this.codelens.style.display = "block";
            this.clButton.innerText = $.i18n("msg_activecode_hide_codelens");
        } else {
            this.codelens.style.display = "none";
            this.clButton.innerText = $.i18n("msg_activecode_show_in_codelens");
            return;
        }
        var cl = this.codelens.firstChild;
        if (cl) {
            this.codelens.removeChild(cl);
        }
        var code = await this.buildProg(false);
        var myVars = {};
        myVars.code = code;
        myVars.origin = "opt-frontend.js";
        myVars.cumulative = false;
        myVars.heapPrimitives = false;
        myVars.drawParentPointers = false;
        myVars.textReferences = false;
        myVars.showOnlyOutputs = false;
        myVars.rawInputLstJSON = JSON.stringify([]);
        if (this.language == "python") {
            if (this.python3) {
                myVars.py = 3;
            } else {
                myVars.py = 2;
            }
        } else if (this.langauge == "javascript") {
            myVars.py = "js";
        } else {
            myVars.py = this.language;
        }
        myVars.curInstr = 0;
        myVars.codeDivWidth = 350;
        myVars.codeDivHeight = 400;
        var srcURL = "https://pythontutor.com/iframe-embed.html";
        var srcVars = $.param(myVars);
        var embedUrlStr = `${srcURL}#${srcVars}`;
        var myIframe = document.createElement("iframe");
        myIframe.setAttribute("id", this.divid + "_codelens");
        myIframe.setAttribute("width", "800");
        myIframe.setAttribute("height", "500");
        myIframe.setAttribute("style", "display:block");
        myIframe.style.background = "#fff";
        //myIframe.setAttribute("src",srcURL)
        myIframe.src = embedUrlStr;
        this.codelens.appendChild(myIframe);
        this.logBookEvent({
            event: "codelens",
            act: "view",
            div_id: this.divid,
        });
    }
    // <iframe id="%(divid)s_codelens" width="800" height="500" style="display:block"src="#">
    // </iframe>
    showCodeCoach() {
        var myIframe;
        var srcURL;
        var cl;
        var div_id = this.divid;
        if (this.codecoach === null) {
            this.codecoach = document.createElement("div");
            this.codecoach.style.display = "block";
        }
        cl = this.codecoach.firstChild;
        if (cl) {
            this.codecoach.removeChild(cl);
        }
        srcURL = eBookConfig.app + "/admin/diffviewer?divid=" + div_id;
        myIframe = document.createElement("iframe");
        myIframe.setAttribute("id", div_id + "_coach");
        myIframe.setAttribute("width", "800px");
        myIframe.setAttribute("height", "500px");
        myIframe.setAttribute("style", "display:block");
        myIframe.style.background = "#fff";
        myIframe.style.width = "100%";
        myIframe.src = srcURL;
        this.codecoach.appendChild(myIframe);
        $(this.codecoach).show();
        this.logBookEvent({
            event: "coach",
            act: "view",
            div_id: this.divid,
        });
    }

    toggleEditorVisibility() { }

    addErrorMessage(err) {
        // Add the error message
        this.errLastRun = true;
        var errHead = $("<h3>").html("Error");
        this.eContainer = this.outerDiv.appendChild(
            document.createElement("div")
        );
        this.eContainer.className = "error alert alert-danger";
        this.eContainer.id = this.divid + "_errinfo";
        this.eContainer.appendChild(errHead[0]);
        var errText = this.eContainer.appendChild(
            document.createElement("pre")
        );
        // But, adjust the line numbers.  If the line number is <= pretextLines then it is in included code
        // if it is greater than the number of included lines but less than the pretext + current editor then it is in the student code.
        // adjust the line number we display by eliminating the pre-included code.
        if (err.traceback.length >= 1) {
            var errorLine = err.traceback[0].lineno;
            if (errorLine <= this.pretextLines) {
                errText.innerHTML =
                    "An error occurred in the hidden, included code. Sorry we can't give you a more helpful error message";
                return;
            } else if (errorLine > this.progLines + this.pretextLines) {
                errText.innerHTML = `An error occurred after the end of your code.
One possible reason is that you have an unclosed parenthesis or string.
Another possibility is that there is an error in the hidden test code.
Yet another is that there is an internal error.  The internal error message is: ${err.message}`;
                return;
            } else {
                if (this.pretextLines > 0) {
                    err.traceback[0].lineno =
                        err.traceback[0].lineno - this.pretextLines + 1;
                }
            }
        }
        var errString = err.toString();
        var to = errString.indexOf(":");
        var errName = errString.substring(0, to);
        errText.innerHTML = errString;
        $(this.eContainer).append("<h3>Description</h3>");
        var errDesc = this.eContainer.appendChild(document.createElement("p"));
        errDesc.innerHTML = errorText[errName];
        $(this.eContainer).append("<h3>To Fix</h3>");
        var errFix = this.eContainer.appendChild(document.createElement("p"));
        errFix.innerHTML = errorText[errName + "Fix"];
        var moreInfo = "../ErrorHelp/" + errName.toLowerCase() + ".html";
        //console.log("Runtime Error: " + err.toString());
    }
    setTimeLimit(timer) {
        var timelimit = this.timelimit;
        if (timer !== undefined) {
            timelimit = timer;
        }
        // set execLimit in milliseconds  -- for student projects set this to
        // 25 seconds -- just less than Chrome's own timer.
        if (
            this.code.indexOf("ontimer") > -1 ||
            this.code.indexOf("onclick") > -1 ||
            this.code.indexOf("onkey") > -1 ||
            this.code.indexOf("setDelay") > -1
        ) {
            Sk.execLimit = null;
        } else {
            if (timelimit === "off") {
                Sk.execLimit = null;
            } else if (timelimit) {
                Sk.execLimit = timelimit;
            } else {
                Sk.execLimit = 25000;
            }
        }
    }
    builtinRead(x) {
        if (
            Sk.builtinFiles === undefined ||
            Sk.builtinFiles["files"][x] === undefined
        )
            throw $.i18n("msg_activecode_file_not_found", x);
        return Sk.builtinFiles["files"][x];
    }
    fileReader(divid) {
        let elem = document.getElementById(divid);
        let data = "";
        let result = "";
        if (elem == null && Sk.builtinFiles.files.hasOwnProperty(divid)) {
            return Sk.builtinFiles["files"][divid];
        } else {
            // try remote file unless it ends with .js or .py -- otherwise we'll ask the server for all
            // kinds of modules that we are trying to import
            if (!(divid.endsWith(".js") || divid.endsWith(".py"))) {
                $.ajax({
                    async: false,
                    url: `/runestone/ajax/get_datafile?course_id=${eBookConfig.course}&acid=${divid}`,
                    success: function (data) {
                        result = JSON.parse(data).data;
                    },
                    error: function (err) {
                        result = null;
                    },
                });
                if (result) {
                    return result;
                }
            }
        }
        if (elem == null && result === null) {
            throw new Sk.builtin.IOError(
                $.i18n("msg_activecode_no_file_or_dir", divid)
            );
        } else {
            if (elem.nodeName.toLowerCase() == "textarea") {
                data = elem.value;
            } else {
                data = elem.textContent;
            }
        }
        return data;
    }
    outputfun(text) {
        // bnm python 3
        var pyStr = function (x) {
            if (x instanceof Array) {
                return "[" + x.join(", ") + "]";
            } else {
                return x;
            }
        };
        var x = text;
        if (!this.python3) {
            if (x.charAt(0) == "(") {
                x = x.slice(1, -1);
                x = "[" + x + "]";
                try {
                    var xl = eval(x);
                    xl = xl.map(pyStr);
                    x = xl.join(" ");
                } catch (err) { }
            }
        }
        $(this.output).css("visibility", "visible");
        text = x;
        text = text
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;")
            .replace(/\n/g, "<br/>");
        return Promise.resolve().then(
            function () {
                setTimeout(
                    function () {
                        $(this.output).append(text);
                    }.bind(this),
                    0
                );
            }.bind(this)
        );
    }

    filewriter(fobj, bytes) {
        let filecomponent = document.getElementById(fobj.name);
        if (!filecomponent) {
            let container = document.createElement("div");
            $(container).addClass("runestone");
            let tab = document.createElement("div");
            $(tab).addClass("datafile_caption");
            tab.innerHTML = `Data file: <code>${fobj.name}</code>`;
            filecomponent = document.createElement("textarea");
            filecomponent.rows = 10;
            filecomponent.cols = 50;
            filecomponent.id = fobj.name;
            $(filecomponent).css("margin-bottom", "5px");
            $(filecomponent).addClass("ac_output");
            container.appendChild(tab);
            container.appendChild(filecomponent);
            this.outerDiv.appendChild(container);
        } else {
            if (fobj.pos$ == 0) {
                $(filecomponent).val("");
            }
        }
        let current = $(filecomponent).val();
        current = current + bytes.v;
        $(filecomponent).val(current);
        $(filecomponent).css("display", "block");
        fobj.pos$ = current.length;
        return current.length;
    }

    async getIncludedCode(divid) {
        if (window.edList[divid]) {
            return Promise.resolve(window.edList[divid].editor.getValue());
        } else {
            let request = new Request(
                `/runestone/ajax/get_datafile?course_id=${eBookConfig.course}&acid=${divid}`,
                {
                    method: "GET",
                    headers: this.jsonHeaders,
                }
            );
            let wresult = await fetch(request);
            return wresult.json();
        }
    }

    async buildProg(useSuffix) {
        // assemble code from prefix, suffix, and editor for running.
        var pretext;
        var prog = this.editor.getValue() + "\n";
        if (this.prefix) {
            prog = this.prefix + prog;
        }
        this.pretext = "";
        this.pretextLines = 0;
        this.progLines = prog.match(/\n/g).length + 1;
        if (this.includes) {
            // iterate over the includes, in-order prepending to prog
            pretext = "";
            for (var x = 0; x < this.includes.length; x++) {
                let iCode = await this.getIncludedCode(this.includes[x]);
                pretext = pretext + iCode + "\n";
            }
            this.pretext = pretext;
            if (this.pretext) {
                this.pretextLines = (this.pretext.match(/\n/g) || "").length;
            }
            prog = pretext + prog;
        }
        if (useSuffix && this.suffix) {
            prog = prog + this.suffix;
        }
        return Promise.resolve(prog);
    }

    async manage_scrubber(saveCode) {
        if (this.historyScrubber === null && !this.autorun) {
            await this.addHistoryScrubber();
        }
        if (
            this.historyScrubber &&
            this.history[$(this.historyScrubber).slider("value")] !=
            this.editor.getValue()
        ) {
            saveCode = "True";
            this.history.push(this.editor.getValue());
            this.timestamps.push(new Date().toLocaleString());
            $(this.historyScrubber).slider(
                "option",
                "max",
                this.history.length - 1
            );
            $(this.historyScrubber).slider(
                "option",
                "value",
                this.history.length - 1
            );
            this.slideit();
        } else {
            saveCode = "False";
        }
        if (this.historyScrubber == null) {
            saveCode = "False";
        }
        return saveCode;
    }

    async checkCurrentAnswer() {
        try {
            await this.runProg();
        } catch (e) {
            console.log(`error running code ${e}`);
        }
    }

    logCurrentAnswer() {
        this.logRunEvent({
            div_id: this.divid,
            code: this.editor.getValue(),
            lang: this.language,
            errinfo: this.errinfo,
            to_save: this.saveCode,
            prefix: this.pretext,
            suffix: this.suffix,
            partner: this.partner,
        }); // Log the run event
        // If unit tests were run there will be a unit_results
        if (this.unit_results) {
            this.logBookEvent({
                act: this.unit_results,
                div_id: this.divid,
                event: "unittest",
            });
        }
    }

    renderFeedback() {
        // The python unit test code builds the table as it is running the tests
        // In "normal" usage this is displayed immediately.
        // However in exam mode we make a div which is offscreen
        if (this.unit_results_divid) {
            if (this.unit_results_divid.indexOf("_offscreen_") > 0) {
                let urDivid = `${this.divid}_offscreen_unit_results`;
                let unitFeedback = document.getElementById(urDivid);
                let tmp = document.body.removeChild(unitFeedback);
                if ($(this.containerDiv).find(`#${urDivid}`).length > 0) {
                    tmp = $(this.containerDiv).find(`#${urDivid}`)[0];
                } else {
                    this.containerDiv.appendChild(tmp);
                }
                $(tmp).show();
            } else {
                let urDivid = this.divid + "_unit_results";
                if (
                    $(this.containerDiv).find(`#${urDivid}`).length == 0 &&
                    $(this.containerDiv).find(`#${urDivid}_offscreen_unit_results`).length == 0
                ) {
                    let urResults = document.getElementById(urDivid);
                    this.containerDiv.appendChild(urResults);
                }
            }
        }
    }

    /* runProg has several async elements to it.
     * 1. Skulpt runs the python program asynchronously
     * 2. The history is restored asynchronously
     * 3. Logging is asynchronous
     *
     * This method returns the skulpt Promise and so the promise will resolve when skulpt is finished.
     * when finished this.unit_results will contain the results of any unit tests that have been run.
     * The table of results is constructed and added to the DOM by the python unittest.gui module in skulpt.
     *
     */
    async runProg(noUI, logResults) {
        console.log("starting runProg");
        if (typeof logResults === "undefined") {
            this.logResults = true;
        } else {
            this.logResults = logResults;
        }
        if (typeof noUI !== "boolean") {
            noUI = false;
        }
        var prog = await this.buildProg(true);
        this.saveCode = "True";
        $(this.output).text("");
        while ($(`#${this.divid}_errinfo`).length > 0) {
            $(`#${this.divid}_errinfo`).remove();
        }
        //$(this.eContainer).remove();
        if (this.codelens) {
            this.codelens.style.display = "none";
        }
        if (this.clButton) {
            this.clButton.innerText = $.i18n("msg_activecode_show_in_codelens");
        }
        Sk.configure({
            output: this.outputfun.bind(this),
            read: this.fileReader,
            filewrite: this.filewriter.bind(this),
            __future__: Sk.python3,
            nonreadopen: true,
            //        python3: this.python3,
            imageProxy: "http://image.runestone.academy:8080/320x",
            inputfunTakesPrompt: true,
            jsonpSites: ["https://itunes.apple.com"],
        });
        Sk.divid = this.divid;
        Sk.logResults = logResults;
        if (this.graderactive && this.containerDiv.closest(".loading")) {
            Sk.gradeContainer = this.containerDiv.closest(".loading").id;
        } else {
            Sk.gradeContainer = this.divid;
        }
        this.setTimeLimit();
        (Sk.TurtleGraphics || (Sk.TurtleGraphics = {})).target = this.graphics;
        Sk.canvas = this.graphics.id; //todo: get rid of this here and in image
        if (!noUI) {
            this.saveCode = await this.manage_scrubber(this.saveCode);
            $(this.runButton).attr("disabled", "disabled");
            $(this.historyScrubber).off("slidechange");
            $(this.historyScrubber).slider("disable");
            $(this.outDiv).show({
                duration: 700,
                queue: false,
            });
        }
        try {
            await Sk.misceval.asyncToPromise(function () {
                return Sk.importMainWithBody("<stdin>", false, prog, true);
            });
            if (!noUI) {
                if (this.slideit) {
                    $(this.historyScrubber).on(
                        "slidechange",
                        this.slideit.bind(this)
                    );
                }
                $(this.historyScrubber).slider("enable");
            }
            this.errLastRun = false;
            this.errinfo = "success";
        } catch (err) {
            if (!noUI) {
                $(this.historyScrubber).on(
                    "slidechange",
                    this.slideit.bind(this)
                );
                $(this.historyScrubber).slider("enable");
            }
            this.errinfo = err.toString();
            this.addErrorMessage(err);
        } finally {
            $(this.runButton).removeAttr("disabled");
            if (typeof window.allVisualizers != "undefined") {
                $.each(window.allVisualizers, function (i, e) {
                    e.redrawConnectors();
                });
            }
        }
    }

    disableInteraction() {
        $(this.runButton).hide();
        $(this.codeDiv).addClass("ac-disabled");
    }
}

var languageExtensions = {
    python: "py",
    html: "html",
    javascript: "js",
    java: "java",
    python2: "py",
    python3: "py",
    cpp: "cpp",
    c: "c",
    sql: "sql",
    octave: "m",
};

var errorText = {};

errorText.ParseError = $.i18n("msg_sctivecode_parse_error");
errorText.ParseErrorFix = $.i18n("msg_sctivecode_parse_error_fix");
errorText.TypeError = $.i18n("msg_activecode_type_error");
errorText.TypeErrorFix = $.i18n("msg_activecode_type_error_fix");
errorText.NameError = $.i18n("msg_activecode_name_error");
errorText.NameErrorFix = $.i18n("msg_activecode_name_error_fix");
errorText.ValueError = $.i18n("msg_activecode_value_error");
errorText.ValueErrorFix = $.i18n("msg_activecode_value_error_fix");
errorText.AttributeError = $.i18n("msg_activecode_attribute_error");
errorText.AttributeErrorFix = $.i18n("msg_activecode_attribute_error_fix");
errorText.TokenError = $.i18n("msg_activecode_token_error");
errorText.TokenErrorFix = $.i18n("msg_activecode_token_error_fix");
errorText.TimeLimitError = $.i18n("msg_activecode_time_limit_error");
errorText.TimeLimitErrorFix = $.i18n("msg_activecode_time_limit_error_fix");
errorText.Error = $.i18n("msg_activecode_general_error");
errorText.ErrorFix = $.i18n("msg_activecode_general_error_fix");
errorText.SyntaxError = $.i18n("msg_activecode_syntax_error");
errorText.SyntaxErrorFix = $.i18n("msg_activecode_syntax_error_fix");
errorText.IndexError = $.i18n("msg_activecode_index_error");
errorText.IndexErrorFix = $.i18n("msg_activecode_index_error_fix");
errorText.URIError = $.i18n("msg_activecode_uri_error");
errorText.URIErrorFix = $.i18n("msg_activecode_uri_error_fix");
errorText.ImportError = $.i18n("msg_activecode_import_error");
errorText.ImportErrorFix = $.i18n("msg_activecode_import_error_fix");
errorText.ReferenceError = $.i18n("msg_activecode_reference_error");
errorText.ReferenceErrorFix = $.i18n("msg_activecode_reference_error_fix");
errorText.ZeroDivisionError = $.i18n("msg_activecode_zero_division_error");
errorText.ZeroDivisionErrorFix = $.i18n(
    "msg_activecode_zero_division_error_fix"
);
errorText.RangeError = $.i18n("msg_activecode_range_error");
errorText.RangeErrorFix = $.i18n("msg_activecode_range_error_fix");
errorText.InternalError = $.i18n("msg_activecode_internal_error");
errorText.InternalErrorFix = $.i18n("msg_activecode_internal_error_fix");
errorText.IndentationError = $.i18n("msg_activecode_indentation_error");
errorText.IndentationErrorFix = $.i18n("msg_activecode_indentation_error_fix");
errorText.NotImplementedError = $.i18n("msg_activecode_not_implemented_error");
errorText.NotImplementedErrorFix = $.i18n(
    "msg_activecode_not_implemented_error_fix"
);
errorText.KeyError = $.i18n("msg_activecode_key_error");
errorText.KeyErrorFix = $.i18n("msg_activecode_key_error_fix");
errorText.AssertionError = $.i18n("msg_activecode_assertion_error");
errorText.AssertionErrorFix = $.i18n("msg_activecode_assertion_error_fix");

String.prototype.replaceAll = function (target, replacement) {
    return this.split(target).join(replacement);
};


/***/ }),

/***/ "./runestone/activecode/js/activecode_html.js":
/*!****************************************************!*\
  !*** ./runestone/activecode/js/activecode_html.js ***!
  \****************************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return HTMLActiveCode; });
/* harmony import */ var _activecode_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./activecode.js */ "./runestone/activecode/js/activecode.js");


class HTMLActiveCode extends _activecode_js__WEBPACK_IMPORTED_MODULE_0__["ActiveCode"] {
    constructor(opts) {
        super(opts);
        opts.alignVertical = true;
        this.code = $("<textarea />").html(this.origElem.innerHTML).text();
        $(this.runButton).text("Render");
        this.editor.setValue(this.code);
    }

    async runProg() {
        var prog = await this.buildProg(true);
        let saveCode = "True";
        this.saveCode = await this.manage_scrubber(saveCode);
        $(this.output).text("");
        if (!this.alignVertical) {
            $(this.codeDiv).switchClass("col-md-12", "col-md-6", {
                duration: 500,
                queue: false,
            });
        }
        $(this.outDiv).show({ duration: 700, queue: false });
        prog =
            "<script type=text/javascript>window.onerror = function(msg,url,line) {alert(msg+' on line: '+line);};</script>" +
            prog;
        this.output.srcdoc = prog;
    }

    createOutput() {
        this.alignVertical = true;
        var outDiv = document.createElement("div");
        $(outDiv).addClass("ac_output");
        if (this.alignVertical) {
            $(outDiv).addClass("col-md-12");
        } else {
            $(outDiv).addClass("col-md-5");
        }
        this.outDiv = outDiv;
        this.output = document.createElement("iframe");
        $(this.output).css("background-color", "white");
        $(this.output).css("position", "relative");
        $(this.output).css("height", "400px");
        $(this.output).css("width", "100%");
        outDiv.appendChild(this.output);
        this.outerDiv.appendChild(outDiv);
        var clearDiv = document.createElement("div");
        $(clearDiv).css("clear", "both"); // needed to make parent div resize properly
        this.outerDiv.appendChild(clearDiv);
    }
    enableSaveLoad() {
        $(this.runButton).text($.i18n("msg_activecode_render"));
    }
}


/***/ }),

/***/ "./runestone/activecode/js/activecode_js.js":
/*!**************************************************!*\
  !*** ./runestone/activecode/js/activecode_js.js ***!
  \**************************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return JSActiveCode; });
/* harmony import */ var _activecode_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./activecode.js */ "./runestone/activecode/js/activecode.js");

__webpack_require__(/*! ./skulpt.min.js */ "./runestone/activecode/js/skulpt.min.js");
__webpack_require__(/*! ./skulpt-stdlib.js */ "./runestone/activecode/js/skulpt-stdlib.js");

class JSActiveCode extends _activecode_js__WEBPACK_IMPORTED_MODULE_0__["ActiveCode"] {
    constructor(opts) {
        super(opts);
    }
    outputfun(a) {
        $(this.output).css("visibility", "visible");
        var str = "[";
        if (typeof a == "object" && a.length) {
            for (var i = 0; i < a.length; i++)
                if (typeof a[i] == "object" && a[i].length) {
                    str += (i == 0 ? "" : " ") + "[";
                    for (var j = 0; j < a[i].length; j++)
                        str +=
                            a[i][j] +
                            (j == a[i].length - 1
                                ? "]" + (i == a.length - 1 ? "]" : ",") + "\n"
                                : ", ");
                } else str += a[i] + (i == a.length - 1 ? "]" : ", ");
        } else {
            try {
                str = JSON.stringify(a);
            } catch (e) {
                str = a;
            }
        }
        return str;
    }
    async runProg() {
        var _this = this;
        var prog = await this.buildProg(true);
        var saveCode = "True";
        var write = function (str) {
            _this.output.innerHTML += _this.outputfun(str);
        };
        var writeln = function (str) {
            if (!str) str = "";
            _this.output.innerHTML += _this.outputfun(str) + "<br />";
        };
        this.saveCode = await this.manage_scrubber(saveCode);
        $(this.eContainer).remove();
        $(this.output).text("");
        $(this.outDiv).show({ duration: 700, queue: false });
        try {
            eval(prog);
            this.errinfo = "success";
        } catch (e) {
            this.addErrorMessage(e);
            this.errinfo = e;
        }
    }
}


/***/ }),

/***/ "./runestone/activecode/js/activecode_sql.js":
/*!***************************************************!*\
  !*** ./runestone/activecode/js/activecode_sql.js ***!
  \***************************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return SQLActiveCode; });
/* harmony import */ var _activecode_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./activecode.js */ "./runestone/activecode/js/activecode.js");

//require("sql.js");

var allDburls = {};

class SQLActiveCode extends _activecode_js__WEBPACK_IMPORTED_MODULE_0__["ActiveCode"] {
    constructor(opts) {
        super(opts);
        //  fnprefix sets the path to load the sql-wasm.wasm file
        var bookprefix;
        var fnprefix;
        if (eBookConfig.useRunestoneServices) {
            bookprefix = `${eBookConfig.app}/books/published/${eBookConfig.basecourse}`;
            fnprefix = bookprefix + "/_static";
        } else {
            bookprefix = "";
            fnprefix = "/_static";
        }
        this.config = {
            locateFile: (filename) => `${fnprefix}/${filename}`,
        };
        var self = this;
        initSqlJs(this.config).then(function (SQL) {
            // set up call to load database asynchronously if given
            if (self.dburl) {
                if (self.dburl.startsWith("/_static")) {
                    self.dburl = `${bookprefix}${self.dburl}`;
                }
                $(self.runButton).attr("disabled", "disabled");
                let buttonText = $(self.runButton).text();
                $(self.runButton).text($.i18n("msg_activecode_load_db"));
                if (!(self.dburl in allDburls)) {
                    allDburls[self.dburl] = {
                        status: "loading",
                        xWaitFor: jQuery.Deferred(),
                    };
                } else {
                    if (allDburls[self.dburl].status == "loading") {
                        allDburls[self.dburl].xWaitFor.done(function () {
                            self.db = new SQL.Database(
                                allDburls[self.dburl].db
                            );
                            $(self.runButton).removeAttr("disabled");
                            $(self.runButton).text(buttonText);
                        });
                        return;
                    }
                    self.db = new SQL.Database(allDburls[self.dburl].db);
                    $(self.runButton).removeAttr("disabled");
                    $(self.runButton).text(buttonText);
                    return;
                }
                var xhr = new XMLHttpRequest();
                // For example: https://github.com/lerocha/chinook-database/raw/master/ChinookDatabase/DataSources/Chinook_Sqlite.sqlite
                xhr.open("GET", self.dburl, true);
                xhr.responseType = "arraybuffer";
                xhr.onload = (e) => {
                    var uInt8Array = new Uint8Array(xhr.response);
                    self.db = new SQL.Database(uInt8Array);
                    $(self.runButton).text(buttonText);
                    $(self.runButton).removeAttr("disabled");
                    allDburls[self.dburl].db = uInt8Array;
                    allDburls[self.dburl].status = "ready";
                    allDburls[self.dburl].xWaitFor.resolve();
                    // contents is now [{columns:['col1','col2',...], values:[[first row], [second row], ...]}]
                };
                xhr.send();
            } else {
                self.db = new SQL.Database();
            }
        });
    }
    async runProg(noUI, logResults) {
        if (typeof logResults === "undefined") {
            this.logResults = true;
        } else {
            this.logResults = logResults;
        }
        if (typeof noUI !== "boolean") {
            noUI = false;
        }
        // Clear any old results
        this.saveCode = "True";
        let divid = this.divid + "_sql_out";
        let respDiv = document.getElementById(divid);
        if (respDiv) {
            respDiv.parentElement.removeChild(respDiv);
        }
        $(this.output).text("");
        // Run this query
        let query = await this.buildProg(false); // false --> Do not include suffix
        if (!this.db) {
            $(this.output).text(
                `Error: Database not initialized! DBURL: ${this.dburl}`
            );
            return;
        }

        let it = this.db.iterateStatements(query);
        this.results = [];
        try {
            for (let statement of it) {
                let columns = statement.getColumnNames();
                if (columns.length > 0) {
                    // data! probably a SELECT
                    let data = [];
                    while (statement.step()) {
                        data.push(statement.get());
                    }
                    this.results.push({
                        status: "success",
                        columns: columns,
                        values: data,
                        rowcount: data.length,
                    });
                } else {
                    let nsql = statement.getNormalizedSQL();
                    let prefix = nsql.substr(0, 6).toLowerCase();
                    statement.step(); // execute the query
                    // Try to detect INSERT/UPDATE/DELETE to give friendly feedback
                    // on rows modified - unfortunately, this won't catch such queries
                    // if they use CTEs.  There seems to be no reliable way of knowing
                    // when a SQLite query actually modified data.
                    if (
                        prefix === "insert" ||
                        prefix === "update" ||
                        prefix === "delete"
                    ) {
                        this.results.push({
                            status: "success",
                            operation: prefix,
                            rowcount: this.db.getRowsModified(),
                        });
                    } else {
                        this.results.push({ status: "success" });
                    }
                }
            }
        } catch (e) {
            this.results.push({
                status: "failure",
                message: e.toString(),
                sql: it.getRemainingSQL(),
            });
        }

        if (this.results.length === 0) {
            this.results.push({
                status: "failure",
                message: "No queries submitted.",
            });
        }

        try {
            this.saveCode = await this.manage_scrubber(this.saveCode);
            if (this.slideit) {
                $(this.historyScrubber).on(
                    "slidechange",
                    this.slideit.bind(this)
                );
            }
            $(this.historyScrubber).slider("enable");
        } catch (e) {
            console.log(`Failed to update scrubber ${e}`);
        }

        respDiv = document.createElement("div");
        respDiv.id = divid;
        this.outDiv.appendChild(respDiv);
        $(this.outDiv).show();
        for (let r of this.results) {
            let section = document.createElement("div");
            section.setAttribute("class", "ac_sql_result");
            respDiv.appendChild(section);
            if (r.status === "success") {
                if (r.columns) {
                    let tableDiv = document.createElement("div");
                    section.appendChild(tableDiv);
                    let maxHeight = 350;
                    if (this.results.length > 1) maxHeight = 200; // max height smaller if lots of results
                    createTable(r, tableDiv, maxHeight);
                    let messageBox = document.createElement("pre");
                    let rmsg = r.rowcount !== 1 ? " rows " : " row ";
                    let msg = "" + r.rowcount + rmsg + "returned";
                    if (r.rowcount > 100) {
                        msg = msg + " (only first 100 rows displayed)";
                    }
                    msg = msg + ".";
                    messageBox.textContent = msg;
                    messageBox.setAttribute("class", "ac_sql_result_success");
                    section.appendChild(messageBox);
                } else if (r.rowcount) {
                    let messageBox = document.createElement("pre");
                    let op = r.operation;
                    op = op + (op.charAt(op.length - 1) === "e" ? "d." : "ed.");
                    let rmsg = r.rowcount !== 1 ? " rows " : " row ";
                    messageBox.textContent = "" + r.rowcount + rmsg + op;
                    messageBox.setAttribute("class", "ac_sql_result_success");
                    section.appendChild(messageBox);
                } else {
                    let messageBox = document.createElement("pre");
                    messageBox.textContent = "Operation succeeded.";
                    messageBox.setAttribute("class", "ac_sql_result_success");
                    section.appendChild(messageBox);
                }
            } else {
                let messageBox = document.createElement("pre");
                messageBox.textContent = r.message;
                messageBox.setAttribute("class", "ac_sql_result_failure");
                section.appendChild(messageBox);
            }
        }

        // Now handle autograding
        if (this.suffix) {
            this.testResult = this.autograde(
                this.results[this.results.length - 1]
            );
        } else {
            $(this.output).css("visibility", "hidden");
        }

        return Promise.resolve("done");
    }

    logCurrentAnswer() {
        this.logRunEvent({
            div_id: this.divid,
            code: this.editor.getValue(),
            lang: this.language,
            errinfo: this.results[this.results.length - 1].status,
            to_save: this.saveCode,
            prefix: this.pretext,
            suffix: this.suffix,
            partner: this.partner,
        }); // Log the run event

        if (this.unit_results) {
            this.logBookEvent({
                event: "unittest",
                div_id: this.divid,
                course: eBookConfig.course,
                act: this.unit_results,
            });
        }
    }

    renderFeedback() {
        if (this.testResult) {
            $(this.output).text(this.testResult);
            $(this.output).css("visibility", "visible");
        }
    }

    autograde(result_table) {
        var tests = this.suffix.split(/\n/);
        this.passed = 0;
        this.failed = 0;
        // Tests should be of the form
        // assert row,col oper value for example
        // assert 4,4 == 3
        var result = "";
        tests = tests.filter(function (s) {
            return s.indexOf("assert") > -1;
        });
        for (let test of tests) {
            let wlist = test.split(/\s+/);
            wlist.shift();
            let loc = wlist.shift();
            let oper = wlist.shift();
            let expected = wlist.join(" ");
            let [row, col] = loc.split(",");
            result += this.testOneAssert(
                row,
                col,
                oper,
                expected,
                result_table
            );
            result += "\n";
        }
        let pct = (100 * this.passed) / (this.passed + this.failed);
        pct = pct.toLocaleString(undefined, { maximumFractionDigits: 2 });
        result += `You passed ${this.passed} out of ${
            this.passed + this.failed
        } tests for ${pct}%`;
        this.unit_results = `percent:${pct}:passed:${this.passed}:failed:${this.failed}`;

        return result;
    }
    testOneAssert(row, col, oper, expected, result_table) {
        // make sure row and col are in bounds
        let actual;
        let output = "";
        try {
            actual = result_table.values[row][col];
        } catch (e) {
            output = `Failed Not enough data to check row ${row} or column ${col}`;
            return output;
        }
        const operators = {
            "==": function (operand1, operand2) {
                return operand1 == operand2;
            },
            "!=": function (operand1, operand2) {
                return operand1 != operand2;
            },
            ">": function (operand1, operand2) {
                return operand1 > operand2;
            },
            "<": function (operand1, operand2) {
                return operand1 > operand2;
            },
        };
        let res = operators[oper](actual, expected);
        if (res) {
            output = `Pass: ${actual} ${oper} ${expected} in row ${row} column ${result_table.columns[col]}`;
            this.passed++;
        } else {
            output = `Failed ${actual} ${oper} ${expected} in row ${row} column ${result_table.columns[col]}`;
            this.failed++;
        }
        return output;
    }
}

function createTable(tableData, container, maxHeight) {
    let data = tableData.values;
    let trimRows = undefined;
    if (data.length === 0) {
        // kludge: no column headers will show up unless we do this
        data = [tableData.columns.map((e) => null)];
        trimRows = [0];
    }

    var hot = new Handsontable(container, {
        data: data,
        trimRows: trimRows,
        width: "100%",
        height: maxHeight,
        autoRowSize: true,
        autoColumnSize: { useHeaders: true },
        rowHeaders: false,
        colHeaders: tableData.columns,
        editor: false,
        maxRows: 100,
        filters: false,
        dropdownMenu: false,
        licenseKey: "non-commercial-and-evaluation",
    });

    // calculate actual height and resize
    let actualHeight = 40; // header height + small margin
    if (tableData.values.length > 0) {
        for (let i = 0; i < data.length; i++) {
            actualHeight = actualHeight + hot.getRowHeight(i);
            if (actualHeight > maxHeight) break;
        }
    }

    hot.updateSettings({ height: actualHeight });

    return hot;
}


/***/ }),

/***/ "./runestone/activecode/js/audiotour.js":
/*!**********************************************!*\
  !*** ./runestone/activecode/js/audiotour.js ***!
  \**********************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return AudioTour; });
/* harmony import */ var _common_js_runestonebase_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/js/runestonebase.js */ "./runestone/common/js/runestonebase.js");


// function to display the audio tours
class AudioTour extends _common_js_runestonebase_js__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(divid, code, bnum, audio_text) {
        // Bug Fix: If a class extends another class, this is undefined UNTIL super is called
        super();
        this.audio_tour = null;
        this.audio_code = null;
        this.windowcode = null;
        this.first_audio = null;
        this.prev_audio = null;
        this.pause_audio = null;
        this.next_audio = null;
        this.last_audio = null;
        this.status = null;
        this.stop_button = null;
        this.tourButtons = [];
        this.elem = null; // current audio element playing
        this.currIndex = null; // current index
        this.len = null; // current length of audio files for tour
        this.buttonCount = null; // number of audio tour buttons
        this.aname = null; // the audio file name
        this.ahash = null; // hash of the audio file name to the lines to highlight
        this.theDivid = null; // div id
        this.afile = null; // file name for audio
        this.playing = false; // flag to say if playing or not
        this.tourName = "";
        // Replacing has been done here to make sure special characters in the code are displayed correctly
        code = code.replaceAll("*doubleq*", '"');
        code = code.replaceAll("*singleq*", "'");
        code = code.replaceAll("*open*", "(");
        code = code.replaceAll("*close*", ")");
        code = code.replaceAll("*nline*", "<br/>");
        var codeArray = code.split("\n");
        var audio_hash = [];
        var bval = [];
        var atype = audio_text.replaceAll("*doubleq*", '"');
        var audio_type = atype.split("*atype*");
        for (let i = 0; i < audio_type.length - 1; i++) {
            audio_hash[i] = audio_type[i];
            var aword = audio_type[i].split(";");
            bval.push(aword[0]);
        }
        var first =
            "<pre><div id='" +
            divid +
            "_l1'>" +
            "1.   " +
            codeArray[0] +
            "</div>";
        var num_lines = codeArray.length;
        for (let i = 1; i < num_lines; i++) {
            if (i < 9) {
                first =
                    first +
                    "<div id='" +
                    divid +
                    "_l" +
                    (i + 1) +
                    "'>" +
                    (i + 1) +
                    ".   " +
                    codeArray[i] +
                    "</div>";
            } else if (i < 99) {
                first =
                    first +
                    "<div id='" +
                    divid +
                    "_l" +
                    (i + 1) +
                    "'>" +
                    (i + 1) +
                    ".  " +
                    codeArray[i] +
                    "</div>";
            } else {
                first =
                    first +
                    "<div id='" +
                    divid +
                    "_l" +
                    (i + 1) +
                    "'>" +
                    (i + 1) +
                    ". " +
                    codeArray[i] +
                    "</div>";
            }
        }
        first = first + "</pre>";
        //laying out the HTML content
        var bcount = 0;
        for (var i = 0; i < audio_type.length - 1; i++) {
            var newButton = document.createElement("button");
            newButton.className = "btn btn-success";
            newButton.innerHTML = bval[i].replace(/\"/g, "");
            this.tourButtons.push(newButton);
            bcount++;
        }
        this.audio_tour = document.createElement("div");
        this.audio_tour.align = "center";
        this.audio_code = document.createElement("p");
        this.windowcode = document.createElement("div");
        this.windowcode.align = "left";
        $(this.windowcode).html(first);
        this.first_audio = document.createElement("button");
        this.prev_audio = document.createElement("button");
        this.pause_audio = document.createElement("button");
        this.next_audio = document.createElement("button");
        this.last_audio = document.createElement("button");
        this.first_audio.className =
            "btn-default glyphicon glyphicon-fast-backward";
        this.prev_audio.className =
            "btn-default glyphicon glyphicon-step-backward";
        this.pause_audio.className = "btn-default glyphicon glyphicon-pause";
        this.next_audio.className =
            "btn-default glyphicon glyphicon-step-forward";
        this.last_audio.className =
            "btn-default glyphicon glyphicon-fast-forward";
        this.first_audio.setAttribute(
            "style",
            "height: 22px; width: 25px; border-radius: 4px; margin-right:2px;"
        );
        this.prev_audio.setAttribute(
            "style",
            "height: 22px; width: 25px; border-radius: 4px; margin-right:2px;"
        );
        this.pause_audio.setAttribute(
            "style",
            "height: 22px; width: 25px; border-radius: 4px; margin-right:2px;"
        );
        this.next_audio.setAttribute(
            "style",
            "height: 22px; width: 25px; border-radius: 4px; margin-right:2px;"
        );
        this.last_audio.setAttribute(
            "style",
            "height: 22px; width: 25px; border-radius: 4px; margin-right:2px;"
        );
        this.first_audio.name = "first_audio";
        this.prev_audio.name = "prev_audio";
        this.pause_audio.name = "pause_audio";
        this.next_audio.name = "next_audio";
        this.last_audio.name = "last_audio";
        this.first_audio.title = "Play first audio in tour";
        this.prev_audio.title = "Play previous audio in tour";
        this.pause_audio.title = "Pause current audio";
        this.next_audio.title = "Play next audio in tour";
        this.last_audio.title = "Play last audio in tour";
        this.first_audio.setAttribute("aria-label", "Play first audio in tour");
        this.prev_audio.setAttribute(
            "aria-label",
            "Play previous audio in tour"
        );
        this.pause_audio.setAttribute("aria-label", "Pause audio");
        this.next_audio.setAttribute("aria-label", "Play next audio in tour");
        this.last_audio.setAttribute("aria-label", "Play last audio in tour");
        this.first_audio.disabled = true;
        this.prev_audio.disabled = true;
        this.pause_audio.disabled = true;
        this.next_audio.disabled = true;
        this.last_audio.disabled = true;
        this.status = document.createElement("div");
        this.status.className = "alert alert-info";
        this.status.setAttribute("style", "display: none;");
        this.stop_button = document.createElement("button");
        this.stop_button.className = "btn btn-default";
        this.stop_button.innerHTML = "Stop tour";
        $(this.audio_tour).append(
            this.audio_code,
            this.windowcode,
            document.createElement("br"),
            this.first_audio,
            this.prev_audio,
            this.pause_audio,
            this.next_audio,
            this.last_audio,
            document.createElement("br"),
            this.status,
            document.createElement("br"),
            this.tourButtons,
            this.stop_button
        );
        $("#" + divid + " .ac_code_div").append(this.audio_tour);
        $("#" + divid + " .ac_code_div").css("width", "100%");
        $("#" + divid + " .CodeMirror.cm-s-default.ui-resizable").hide();
        $("#" + divid + " .ac_opt.btn.btn-default:last-child").hide();
        $(this.stop_button).click(
            function () {
                if (this.playing) {
                    this.elem.pause();
                }
                //log change to db
                this.logBookEvent({
                    event: "Audio",
                    act: "closeWindow",
                    div_id: divid,
                });
                $(this.audio_tour).remove();
                $(
                    "#" + divid + " .CodeMirror.cm-s-default.ui-resizable"
                ).show();
                $("#" + divid + " .ac_opt.btn.btn-default:last-child").show();
                $("#" + divid + " .ac_code_div").css("width", "");
            }.bind(this)
        );
        $(this.tourButtons[0]).click(
            function () {
                this.tour(divid, audio_hash[0], bcount);
            }.bind(this)
        );
        $(this.tourButtons[1]).click(
            function () {
                this.tour(divid, audio_hash[1], bcount);
            }.bind(this)
        );
        $(this.tourButtons[2]).click(
            function () {
                this.tour(divid, audio_hash[2], bcount);
            }.bind(this)
        );
        $(this.tourButtons[3]).click(
            function () {
                this.tour(divid, audio_hash[3], bcount);
            }.bind(this)
        );
        $(this.tourButtons[4]).click(
            function () {
                this.tour(divid, audio_hash[4], bcount);
            }.bind(this)
        );
        // handle the click to go to the next audio
        $(this.first_audio).click(
            function () {
                this.firstAudio();
            }.bind(this)
        );
        // handle the click to go to the next audio
        $(this.prev_audio).click(
            function () {
                this.prevAudio();
            }.bind(this)
        );
        // handle the click to pause or play the audio
        $(this.pause_audio).click(
            function () {
                this.pauseAndPlayAudio(divid);
            }.bind(this)
        );
        // handle the click to go to the next audio
        $(this.next_audio).click(
            function () {
                this.nextAudio();
            }.bind(this)
        );
        // handle the click to go to the next audio
        $(this.last_audio).click(
            function () {
                this.lastAudio();
            }.bind(this)
        );
        // make the image buttons look disabled
        $(this.first_audio).css("opacity", 0.25);
        $(this.prev_audio).css("opacity", 0.25);
        $(this.pause_audio).css("opacity", 0.25);
        $(this.next_audio).css("opacity", 0.25);
        $(this.last_audio).css("opacity", 0.25);
    }
    tour(divid, audio_type, bcount) {
        // set globals
        this.buttonCount = bcount;
        this.theDivid = divid;
        this.status.setAttribute(
            "style",
            "display: inline-block; margin-top: 7px; margin-bottom: 3px;"
        );
        // enable prev, pause/play and next buttons and make visible
        $(this.first_audio).removeAttr("disabled");
        $(this.prev_audio).removeAttr("disabled");
        $(this.pause_audio).removeAttr("disabled");
        $(this.next_audio).removeAttr("disabled");
        $(this.last_audio).removeAttr("disabled");
        $(this.first_audio).css("opacity", 1.0);
        $(this.prev_audio).css("opacity", 1.0);
        $(this.pause_audio).css("opacity", 1.0);
        $(this.next_audio).css("opacity", 1.0);
        $(this.last_audio).css("opacity", 1.0);
        // disable tour buttons
        for (var i = 0; i < bcount; i++)
            $(this.tourButtons[i]).attr("disabled", "disabled");
        var atype = audio_type.split(";");
        var name = atype[0].replaceAll('"', " ");
        this.tourName = name;
        $(this.status).html($.i18n("msg_activecode_starting", name));
        //log tour type to db
        this.logBookEvent({ event: "Audio", act: name, div_id: divid });
        var max = atype.length;
        var str = "";
        this.ahash = [];
        this.aname = [];
        for (i = 1; i < max - 1; i++) {
            var temp = atype[i].split(":");
            var temp_line = temp[0];
            var temp_aname = temp[1];
            var akey = temp_aname.substring(1, temp_aname.length);
            var lnums = temp_line.substring(1, temp_line.length);
            //alert("akey:"+akey+"lnum:"+lnums);
            // str+="<audio id="+akey+" preload='auto'><source src='http://ice-web.cc.gatech.edu/ce21/audio/"+
            // akey+".mp3' type='audio/mpeg'><source src='http://ice-web.cc.gatech.edu/ce21/audio/"+akey+
            // ".ogg' type='audio/ogg'>Your browser does not support the audio tag</audio>";
            //var dir =
            //    "http://media.interactivepython.org/" +
            //    eBookConfig.basecourse.toLowerCase() +
            //    "/audio/";
            var dir = "../_static/audio/"
            str += "<audio id=" + akey + " preload='auto' >";
            str += "<source src='" + dir + akey + ".wav' type='audio/wav'>";
            str += "<source src='" + dir + akey + ".mp3' type='audio/mpeg'>";
            str += "<source src='" + dir + akey + ".wav' type='audio/wav'>";
            str += "<source src='" + dir + akey + ".mp3' type='audio/mpeg'>";
            str += "<br />Your browser does not support the audio tag</audio>";
            this.ahash[akey] = lnums;
            this.aname.push(akey);
        }
        $(this.audio_code).html(str);
        this.len = this.aname.length; // set the number of audio file in the tour
        this.currIndex = 0;
        this.playCurrIndexAudio();
    }
    handlePlaying() {
        this.elem.pause();
        // unbind current ended
        $("#" + this.afile).unbind("ended");
        // unhighlight the prev lines
        this.unhighlightLines(
            this.theDivid,
            this.ahash[this.aname[this.currIndex]]
        );
    }
    firstAudio() {
        // if audio is this.playing handle it
        this.handlePlaying();
        //log change to db
        this.logBookEvent({
            event: "Audio",
            act: "first",
            div_id: this.theDivid,
        });
        // move to the first audio
        this.currIndex = 0;
        // start at the first audio
        this.playCurrIndexAudio();
    }
    prevAudio() {
        // if there is a previous audio
        if (this.currIndex > 0) {
            // if audio is this.playing handle it
            this.handlePlaying();
            //log change to db
            this.logBookEvent({
                event: "Audio",
                act: "prev",
                div_id: this.theDivid,
            });
            // move to previous to the current (but the current index has moved to the next)
            this.currIndex = this.currIndex - 1;
            // start at the prev audio
            this.playCurrIndexAudio();
        }
    }
    nextAudio() {
        // if audio is this.playing handle it
        this.handlePlaying();
        //log change to db
        this.logBookEvent({
            event: "Audio",
            act: "next",
            div_id: this.theDivid,
        });
        // if not at the end
        if (this.currIndex < this.len - 1) {
            // start at the next audio
            this.currIndex = this.currIndex + 1;
            this.playCurrIndexAudio();
        } else if (this.currIndex == this.len - 1) {
            this.handleTourEnd();
        }
    }
    lastAudio() {
        // if audio is this.playing handle it
        this.handlePlaying();
        //log change to db
        this.logBookEvent({
            event: "Audio",
            act: "last",
            div_id: this.theDivid,
        });
        // move to the last audio
        this.currIndex = this.len - 1;
        // start at last
        this.playCurrIndexAudio();
    }
    // play the audio at the current index
    playCurrIndexAudio() {
        // set this.playing to false
        this.playing = false;
        // play the current audio and highlight the lines
        this.playaudio(this.currIndex, this.aname, this.theDivid, this.ahash);
    }
    // handle the end of the tour
    handleTourEnd() {
        $(this.status).html("The " + this.tourName + " has ended.");
        this.pause_audio.className = "btn-default glyphicon glyphicon-pause";
        this.pause_audio.title = "Pause audio";
        this.pause_audio.setAttribute("aria-label", "Pause audio");
        $(this.first_audio).attr("disabled", "disabled");
        $(this.prev_audio).attr("disabled", "disabled");
        $(this.pause_audio).attr("disabled", "disabled");
        $(this.next_audio).attr("disabled", "disabled");
        $(this.last_audio).attr("disabled", "disabled");
        $(this.first_audio).css("opacity", 0.25);
        $(this.prev_audio).css("opacity", 0.25);
        $(this.pause_audio).css("opacity", 0.25);
        $(this.next_audio).css("opacity", 0.25);
        $(this.last_audio).css("opacity", 0.25);
        // enable the tour buttons
        for (var j = 0; j < this.buttonCount; j++)
            $(this.tourButtons[j]).removeAttr("disabled");
    }
    // only call this one after the first time
    outerAudio() {
        // unbind ended
        $("#" + this.afile).unbind("ended");
        // set this.playing to false
        this.playing = false;
        // unhighlight previous lines from the last audio
        this.unhighlightLines(
            this.theDivid,
            this.ahash[this.aname[this.currIndex]]
        );
        // increment the this.currIndex to point to the next one
        this.currIndex++;
        // if the end of the tour reset the buttons
        if (this.currIndex == this.len) {
            this.handleTourEnd();
        }
        // else not done yet so play the next audio
        else {
            // play the audio at the current index
            this.playCurrIndexAudio();
        }
    }
    // play the audio now that it is ready
    playWhenReady(afile, divid, ahash) {
        // unbind current
        $("#" + afile).unbind("canplaythrough");
        this.elem.currentTime = 0;
        this.playing = true;
        //console.log("in playWhenReady " + elem.duration);
        this.highlightLines(divid, ahash[afile]);
        if (
            this.pause_audio.className ===
            "btn-default glyphicon glyphicon-pause"
        ) {
            $(this.status).html(
                $.i18n("msg_activecode_playing", this.tourName)
            );
            $("#" + afile).bind(
                "ended",
                function () {
                    this.outerAudio();
                }.bind(this)
            );
            this.elem.play();
        } else {
            $("#" + afile).bind(
                "ended",
                function () {
                    this.outerAudio();
                }.bind(this)
            );
        }
    }
    // play the audio at the specified index i and set the duration and highlight the lines
    playaudio(i, aname, divid, ahash) {
        this.afile = aname[i];
        this.elem = document.getElementById(this.afile);
        // if this isn't ready to play yet - no duration yet then wait
        //console.log("in playaudio " + elem.duration);
        if (isNaN(this.elem.duration) || this.elem.duration == 0) {
            // set the status
            $(this.status).html($.i18n("msg_activecode_loading_audio"));
            $("#" + this.afile).bind(
                "canplaythrough",
                function () {
                    this.playWhenReady(this.afile, divid, ahash);
                }.bind(this)
            );
        }
        // otherwise it is ready so play it
        else {
            this.playWhenReady(this.afile, divid, ahash);
        }
    }
    // pause if this.playing and play if paused
    pauseAndPlayAudio(divid) {
        var btn = this.pause_audio;
        // if paused and clicked then continue from current
        if (this.elem.paused) {
            // calcualte the time left to play in milliseconds
            let counter = (this.elem.duration - this.elem.currentTime) * 1000;
            this.elem.play(); // start the audio from current spot
            this.pause_audio.className =
                "btn-default glyphicon glyphicon-pause";
            this.pause_audio.title = $.i18n(
                "msg_activecode_pause_current_audio"
            );
            this.pause_audio.setAttribute(
                "aria-label",
                $.i18n("msg_activecode_pause_audio")
            );
            $(this.status).html(
                $.i18n("msg_activecode_playing", this.tourName)
            );
            //log change to db
            this.logBookEvent({
                event: "Audio",
                act: "play",
                div_id: this.theDivid,
            });
        }
        // if audio was this.playing pause it
        else if (this.playing) {
            this.elem.pause(); // pause the audio
            this.pause_audio.className = "btn-default glyphicon glyphicon-play";
            this.pause_audio.title = $.i18n("msg_activecode_play_paused_audio");
            this.pause_audio.setAttribute(
                "aria-label",
                $.i18n("msg_activecode_play_paused_audio")
            );
            $(this.status).html(
                $.i18n("msg_activecode_audio_paused", this.tourName)
            );
            //log change to db
            this.logBookEvent({
                event: "Audio",
                act: "pause",
                div_id: this.theDivid,
            });
        }
    }
    // process the lines
    processLines(divid, lnum, color) {
        var comma = lnum.split(",");
        if (comma.length > 1) {
            for (let i = 0; i < comma.length; i++) {
                this.setBackgroundForLines(divid, comma[i], color);
            }
        } else {
            this.setBackgroundForLines(divid, lnum, color);
        }
    }
    // unhighlight the lines - set the background back to transparent
    unhighlightLines(divid, lnum) {
        this.processLines(divid, lnum, "transparent");
    }
    // highlight the lines - set the background to a yellow color
    highlightLines(divid, lnum) {
        this.processLines(divid, lnum, "#ffff99");
    }
    // set the background to the passed color
    setBackgroundForLines(divid, lnum, color) {
        var hyphen = lnum.split("-");
        var str;
        // if a range of lines
        if (hyphen.length > 1) {
            var start = parseInt(hyphen[0]);
            var end = parseInt(hyphen[1]) + 1;
            for (var k = start; k < end; k++) {
                //alert(k);
                str = "#" + divid + "_l" + k;
                if ($(str).text() != "") {
                    $(str).css("background-color", color);
                }
                //$(str).effect("highlight",{},(dur*1000)+4500);
            }
        } else {
            //alert(lnum);
            str = "#" + divid + "_l" + lnum;
            $(str).css("background-color", color);
            //$(str).effect("highlight",{},(dur*1000)+4500);
        }
    }
}


/***/ }),

/***/ "./runestone/activecode/js/extractUnitResults.js":
/*!*******************************************************!*\
  !*** ./runestone/activecode/js/extractUnitResults.js ***!
  \*******************************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return JUnitTestParser; });
var testString = `Starting Tests
Expected: Answer                   Actual: Answer                   Message: Checking method printAnswer()                     Passed: true
Expected: 6 line(s) of text        Actual: 0 line(s) of text        Message: Checking main method                              Passed: false
Hello World
Expected: String String            Actual: String String            Message: Checking Instance Variable Type(s)                Passed: true
Expected: Question                 Actual: Question                 Message: Checking method printQuestion()                   Passed: true
Debugging output
More debug output
Expected: 2 Private                Actual: 2 Private                Message: Checking Private Instance Variable(s)             Passed: true
Expected: pass                     Actual: pass                     Message: Checking constructor with parameters              Passed: true
Expected: fail                     Actual: fail                     Message: Checking default constructor                      Passed: true
Ending Tests
You got 6 out of 7 correct. 85.71%`;

class JUnitTestParser {
    constructor(output, parentId) {
        let patt = new RegExp(
            "Expected:\\s+(.*?)Actual:\\s+(.*?)Message:\\s+(.*?)Passed:\\s+(true|false)",
            "g"
        );
        this.textResults = "";
        let matches = output.matchAll(patt);
        let parent = document.createElement("div");
        parent.classList.add("unittest-results");
        let tbl = document.createElement("table");
        tbl.classList.add("ac-feedback");
        parent.appendChild(tbl);
        parent.setAttribute("id", `${parentId}_unit_results`);
        let tr = document.createElement("tr");
        tr.innerHTML =
            '<th class="ac-feedback">Result</th><th class="ac-feedback">Expected</th><th class="ac-feedback">Actual</th><th class="ac-feedback">Notes</th>';
        tbl.appendChild(tr);
        for (const match of matches) {
            let tr = document.createElement("tr");
            let td = document.createElement("td");
            td.classList.add("ac-feedback");
            if (match[match.length - 1] == "true") {
                td.innerHTML = "Pass";
                td.style =
                    "background-color: rgb(131, 211, 130); text-align: center;";
            } else {
                td.innerHTML = "Fail";
                td.style =
                    "background-color: rgb(222, 142, 150); text-align: center;";
            }
            tr.appendChild(td);
            tbl.appendChild(tr);
            for (let i = 1; i < match.length - 1; i++) {
                let td = document.createElement("td");
                td.innerHTML = match[i];
                td.classList.add("ac-feedback");
                tr.appendChild(td);
            }
            tbl.appendChild(tr);
            this.table = parent;
            this.textResults += match[0] + "\n";
            output = output.replace(match[0], "");
        }
        let match = output.match(
            /You got\s+(\d+) out of (\d+) correct.\s+(\d+\.\d+)%/
        );
        if (match) {
            output = output.replace(match[0], "");
            let pctString = document.createElement("span");
            pctString.innerHTML = match[0];
            this.pctString = pctString;
            this.pct = match[3];
            this.passed = match[1];
            this.failed = match[2] - match[1];
        }
        output = output.replace("Starting Tests", "");
        output = output.replace("Ending Tests", "");
        output = output.replace(/\n/g, "<br>");
        output = output.replace(/(<br>)+/g, "<br>");
        output = output.replaceAll("&lt;img", "<img");
        this.stdout = output;
    }
}

// let x = new ResultsToTable(testString);
// console.log(x.stdout);
// console.log(x.table);


/***/ }),

/***/ "./runestone/activecode/js/livecode.js":
/*!*********************************************!*\
  !*** ./runestone/activecode/js/livecode.js ***!
  \*********************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return LiveCode; });
/* harmony import */ var _activecode_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./activecode.js */ "./runestone/activecode/js/activecode.js");
/* harmony import */ var _md5_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./md5.js */ "./runestone/activecode/js/md5.js");
/* harmony import */ var _extractUnitResults_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./extractUnitResults.js */ "./runestone/activecode/js/extractUnitResults.js");




class LiveCode extends _activecode_js__WEBPACK_IMPORTED_MODULE_0__["ActiveCode"] {
    constructor(opts) {
        var orig = $(opts.orig).find("textarea")[0];
        super(opts);
        this.stdin = $(orig).data("stdin");
        this.datafile = $(orig).data("datafile");
        this.sourcefile = $(orig).data("sourcefile");
        this.compileargs = unescapeHtml($(orig).data("compileargs"));
        this.linkargs = unescapeHtml($(orig).data("linkargs"));
        this.runargs = unescapeHtml($(orig).data("runargs"));
        this.interpreterargs = unescapeHtml($(orig).data("interpreterargs"));
        this.API_KEY = "67033pV7eUUvqo07OJDIV8UZ049aLEK1";
        this.USE_API_KEY = true;
        this.JOBE_SERVER = eBookConfig.jobehost || eBookConfig.host;
        this.resource = eBookConfig.proxyuri_runs || "/runestone/proxy/jobeRun";
        this.jobePutFiles =
            eBookConfig.proxyuri_files || "/runestone/proxy/jobePushFile/";
        this.jobeCheckFiles =
            eBookConfig.proxyuri_files || "/runestone/proxy/jobeCheckFile/";
        // TODO:  should add a proper put/check in pavement.tmpl as this is misleading and will break on runestone
        this.div2id = {};
        if (this.stdin) {
            this.createInputElement();
        }
        this.createErrorOutput();
    }
    outputfun(a) {}
    createInputElement() {
        var label = document.createElement("label");
        label.for = this.divid + "_stdin";
        $(label).text($.i18n("msg_activecode_input_prg"));
        var input = document.createElement("input");
        input.id = this.divid + "_stdin";
        input.type = "text";
        input.size = "35";
        input.value = this.stdin;
        this.outerDiv.appendChild(label);
        this.outerDiv.appendChild(input);
        this.stdin_el = input;
    }
    createErrorOutput() {}

    /*  Main runProg method for livecode
     *
     */
    async runProg() {
        await this.runSetup();
        try {
            let res = await this.submitToJobe();
            if (!res.ok) {
                this.addJobeErrorMessage(
                    $.i18n(`Server Error: ${res.statusText}`)
                );
                $(this.runButton).removeAttr("disabled");
                return "fail";
            }
            let runResults = await res.json();
            this.processJobeResponse(runResults);
        } catch (e) {
            this.addJobeErrorMessage(
                $.i18n("msg_activecode_server_comm_err") + e.toString()
            );
            $(this.runButton).removeAttr("disabled");
            return `fail: ${e}`;
        }
        return "success";
    }
    /**
     * Note:
     * In order to check for supplemental files in java and deal with asynchronicity
     * I split the original runProg into two functions: runProg and runProg_callback
     */
    async runSetup() {
        var stdin;
        var source;
        var saveCode = "True";
        var sfilemap = {
            java: "",
            cpp: "test.cpp",
            c: "test.c",
            python3: "test.py",
            python2: "test.py",
            octave: "octatest.m",
        };
        var sourcefilename = "";
        var testdrivername = "";
        var file_checkp;

        // extract the class names so files can be named properly
        if (this.suffix && this.language == "java") {
            let classMatch = new RegExp(/public class\s+(\w+)[\s+{]/);
            source = await this.buildProg(false);
            let m = source.match(classMatch);
            if (m) {
                sourcefilename = m[1] + ".java";
            }
            // this will be unit test code
            m = this.suffix.match(classMatch);
            if (m) {
                testdrivername = m[1] + ".java";
            }
        } else {
            source = await this.buildProg(true);
        }
        // Validate the data is convertible to Base64. If not then error out now
        try {
            btoa(source);
        } catch (e) {
            alert(
                "Error: Bad Characters in the activecode window. Likely a quote character that has been copy/pasted. 🙁"
            );
            return;
        }

        this.saveCode = await this.manage_scrubber(saveCode);

        // assemble parameters for JOBE
        var paramlist = [
            "compileargs",
            "linkargs",
            "runargs",
            "interpreterargs",
            "memorylimit",
        ];
        var paramobj = {};
        for (let param of paramlist) {
            if (this[param]) {
                paramobj[param] = eval(this[param]); // needs a list
            }
        }
        if (this.language === "octave") {
            paramobj.memorylimit = 200000;
        }

        if (this.stdin) {
            stdin = $(this.stdin_el).val();
        }
        if (!this.sourcefile) {
            this.sourcefile = sfilemap[this.language];
        }

        $(this.output).html($.i18n("msg_activecode_compiling_running"));
        var files = [];
        var content, base64;
        if (this.datafile != undefined) {
            var ids = this.datafile.split(",");
            for (var i = 0; i < ids.length; i++) {
                let fileName = ids[i].trim();
                let file = document.getElementById(fileName);
                let fileExtension = fileName.substring(
                    fileName.lastIndexOf(".") + 1
                );
                if (file === null || file === undefined) {
                    // console.log("No file with given id");
                    // check to see if file is in db
                    content = this.fileReader(fileName);
                } else {
                    content = file.textContent;
                    // may be undefined at this point if file is an image
                }
                if (fileExtension === "jar") {
                    files = files.concat(this.parseJavaClasses(content));
                } else if (["jpg", "png", "gif"].indexOf(fileExtension) > -1) {
                    if (file) {
                        if (file.toDataURL) {
                            base64 = file.toDataURL("image/" + fileExtension);
                            base64 = base64.substring(base64.indexOf(",") + 1);
                        } else {
                            base64 = file.src.substring(
                                file.src.indexOf(",") + 1
                            );
                        }
                    } else {
                        base64 = content;
                    }
                    files.push({ name: fileName, content: base64 });
                } else {
                    // if no className or un recognized className it is treated as an individual file
                    // this could be any type of file, .txt, .java, .csv, etc
                    files.push({ name: fileName, content: content });
                }
            }
        }
        // If we are running unit tests we need to substitute the test driver for the student
        // code and send the student code as a file.  We'll do that here.
        this.junitDriverCode = `
        import org.junit.runner.JUnitCore;
        import org.junit.runner.Result;
        import org.junit.runner.notification.Failure;

        public class TestRunner {
            public static void main(String[] args) {
                CodeTestHelper.resetFinalResults();
                Result result = JUnitCore.runClasses(${testdrivername.replace(
                    ".java",
                    ".class"
                )});
                System.out.println(CodeTestHelper.getFinalResults());

                int total = result.getRunCount();
                int fails = result.getFailureCount();
                int corr  = total - fails;
                System.out.println("You got " + corr + " out of " + total + " correct. " + String.format("%.2f", (100.0 * corr / total)) + "%");
            }
        }
        `;
        if (this.suffix && this.language == "java") {
            files.push({ name: sourcefilename, content: source });
            files.push({ name: testdrivername, content: this.suffix });
            source = this.junitDriverCode;
            if (paramobj.compileargs) {
                paramobj.compileargs.push(sourcefilename);
            } else {
                paramobj.compileargs = [sourcefilename];
            }
        }
        let runspec = {
            language_id: this.language,
            sourcecode: source,
            parameters: paramobj,
            sourcefilename: this.sourcefile,
        };

        if (stdin) {
            runspec.input = stdin;
        }
        if (files.length === 0) {
            this.json_runspec = JSON.stringify({ run_spec: runspec });
            file_checkp = Promise.resolve("ready");
        } else {
            runspec["file_list"] = [];
            var promises = [];
            var instance = this;

            for (let i = 0; i < files.length; i++) {
                var fileName = files[i].name;
                var fileContent = files[i].content;
                instance.div2id[fileName] =
                    "runestone" + Object(_md5_js__WEBPACK_IMPORTED_MODULE_1__["default"])(fileName + fileContent);
                runspec["file_list"].push([
                    instance.div2id[fileName],
                    fileName,
                ]);
                promises.push(
                    new Promise((resolve, reject) => {
                        instance.checkFile(files[i], resolve, reject);
                    })
                );
            }
            this.json_runspec = JSON.stringify({ run_spec: runspec });
            this.div2id = instance.div2id;
            file_checkp = Promise.all(promises).catch(function (err) {
                console.log("Error: " + err);
            });
        }
        return file_checkp;
    }

    /* Submit the assembled job to the JOBE server and await the results.
     *
     */
    async submitToJobe() {
        var data = this.json_runspec;
        let host = this.JOBE_SERVER + this.resource;
        $(this.runButton).attr("disabled", "disabled");
        $(this.outDiv).show({ duration: 700, queue: false });
        $(this.errDiv).remove();
        $(this.output).css("visibility", "visible");

        let headers = new Headers({
            "Content-type": "application/json; charset=utf-8",
            Accept: "application/json",
            "X-API-KEY": this.API_KEY,
        });
        let request = new Request(host, {
            method: "POST",
            headers: headers,
            body: data,
        });
        return fetch(request);

        ///$("#" + divid + "_errinfo").remove();
    }

    processJobeResponse(result) {
        var logresult;
        var odiv = this.output;
        this.parsedOutput = {};
        $(this.runButton).removeAttr("disabled");
        if (result.outcome === 15) {
            logresult = "success";
        } else {
            logresult = result.outcome;
        }
        this.errinfo = logresult;
        switch (result.outcome) {
            case 15: {
                this.parsedOutput = new _extractUnitResults_js__WEBPACK_IMPORTED_MODULE_2__["default"](
                    result.stdout,
                    this.divid
                );
                $(odiv).html(this.parsedOutput.stdout);
                if (this.suffix) {
                    if (this.parsedOutput.pct === undefined) {
                        this.parsedOutput.pct = this.parsedOutput.passed = this.parsedOutput.failed = 0;
                    }
                    this.unit_results = `percent:${this.parsedOutput.pct}:passed:${this.parsedOutput.passed}:failed:${this.parsedOutput.failed}`;
                }
                break;
            }
            case 11: // compiler error
                $(odiv).html($.i18n("msg_activecode_were_compiling_err"));
                this.addJobeErrorMessage(result.cmpinfo);
                break;
            case 12: // run time error
                $(odiv).html(result.stdout.replace(/\n/g, "<br>"));
                if (result.stderr) {
                    this.addJobeErrorMessage(result.stderr);
                }
                break;
            case 13: // time limit
                $(odiv).html(result.stdout.replace(/\n/g, "<br>"));
                this.addJobeErrorMessage(
                    $.i18n("msg_activecode_time_limit_exc")
                );
                break;
            default:
                if (result.stderr) {
                    $(odiv).html(result.stderr.replace(/\n/g, "<br>"));
                } else {
                    this.addJobeErrorMessage(
                        $.i18n("msg_activecode_server_err")
                    );
                }
        }
        // todo: handle server busy and timeout errors too
    }

    renderFeedback() {
        let rdiv = document.getElementById(`${this.divid}_unit_results`);
        if (rdiv) {
            rdiv.remove();
        }
        if (this.parsedOutput && this.parsedOutput.table) {
            this.outDiv.appendChild(this.parsedOutput.table);
        }
        rdiv = document.getElementById(`${this.divid}_unit_results`);
        if (rdiv) {
            rdiv.appendChild(this.parsedOutput.pctString);
        }
    }

    addJobeErrorMessage(err) {
        var errHead = $("<h3>").html("Error");
        var eContainer = this.outerDiv.appendChild(
            document.createElement("div")
        );
        this.errDiv = eContainer;
        eContainer.className = "error alert alert-danger";
        eContainer.id = this.divid + "_errinfo";
        eContainer.appendChild(errHead[0]);
        var errText = eContainer.appendChild(document.createElement("pre"));
        errText.innerHTML = err;
    }
    /**
     * Checks to see if file is on server
     * Places it on server if it is not on server
     * @param  {object{name, contents}} file    File to place on server
     * @param  {function} resolve promise resolve function
     * @param  {function} reject  promise reject function
     */
    checkFile(file, resolve, reject) {
        var file_id = this.div2id[file.name];
        var resource = this.jobeCheckFiles + file_id;
        var host = this.JOBE_SERVER + resource;
        var xhr = new XMLHttpRequest();
        xhr.open("HEAD", host, true);
        xhr.setRequestHeader("Content-type", "application/json");
        xhr.setRequestHeader("Accept", "text/plain");
        xhr.setRequestHeader("X-API-KEY", this.API_KEY);
        xhr.onerror = function () {
            // console.log("error sending file" + xhr.responseText);
        };
        xhr.onload = function () {
            switch (xhr.status) {
                case 208:
                case 404:
                    // console.log("File not on Server");
                    this.pushDataFile(file, resolve, reject);
                    break;
                case 400:
                    // console.log("Bad Request");
                    reject();
                    break;
                case 204:
                    // console.log("File already on Server");
                    resolve();
                    break;
                default:
                    //console.log("This case should never happen");
                    reject();
            }
        }.bind(this);
        xhr.send();
    }
    /**
     * Places a file on a server
     */
    pushDataFile(file, resolve, reject) {
        var fileName = file.name;
        var extension = fileName.substring(fileName.indexOf(".") + 1);
        var file_id = this.div2id[fileName];
        var contents = file.content;
        // File types being uploaded that come in already in base64 format
        var extensions = ["jar", "zip", "png", "jpg", "jpeg"];
        var contentsb64;
        if (extensions.indexOf(extension) === -1) {
            contentsb64 = btoa(contents);
        } else {
            contentsb64 = contents;
        }
        var data = JSON.stringify({ file_contents: contentsb64 });
        var resource = this.jobePutFiles + file_id;
        var host = this.JOBE_SERVER + resource;
        var xhr = new XMLHttpRequest();
        xhr.open("PUT", host, true);
        xhr.setRequestHeader("Content-type", "application/json");
        xhr.setRequestHeader("Accept", "text/plain");
        xhr.setRequestHeader("X-API-KEY", this.API_KEY);
        xhr.onload = function () {
            switch (xhr.status) {
                case 403:
                    // console.log("Forbidden");
                    reject();
                    break;
                case 400:
                    // console.log("Bad Request");
                    reject();
                    break;
                case 204:
                    //console.log("successfully sent file " + xhr.responseText);
                    //console.log("File " + fileName +", " + file_id +" placed on server");
                    resolve();
                    break;
                default:
                    // console.log("This case should never happen");
                    reject();
            }
        }.bind(this);
        xhr.onerror = function () {
            // console.log("error sending file" + xhr.responseText);
            reject();
        };
        xhr.send(data);
    }

    async showCodelens() {
        let clMess = "";
        if (this.codelens.style.display == "none") {
            this.codelens.style.display = "block";
            clMess = "Building your visualization";
            this.codelens.innerHTML = clMess;
            this.clButton.innerText = $.i18n("msg_activecode_hide_codelens");
        } else {
            this.codelens.style.display = "none";
            this.clButton.innerText = $.i18n("msg_activecode_show_in_codelens");
            return;
        }
        var cl = this.codelens.firstChild;
        if (cl) {
            this.codelens.removeChild(cl);
            this.codelens.innerHTML = clMess;
        }
        var code = await this.buildProg(false);
        if (code.match(/System.exit/)) {
            alert(
                "Sorry... System.exit breaks the visualizer temporarily removing"
            );
            code = code.replace(/System.exit\(\d+\);/, "");
        }
        var myVars = {};
        myVars.code = code;
        myVars.lang = this.language;
        if (this.stdin) {
            myVars.stdin = $(this.stdin_el).val();
        }
        var targetDiv = this.codelens.id;

        let request = new Request("/runestone/proxy/pytutor_trace", {
            method: "POST",
            body: JSON.stringify(myVars),
            headers: this.jsonHeaders,
        });
        try {
            let response = await fetch(request);
            let data = await response.json();
            let vis = addVisualizerToPage(data, targetDiv, {
                startingInstruction: 0,
                editCodeBaseURL: null,
                hideCode: false,
                lang: myVars.lang,
            });
        } catch (error) {
            let targetDivError = document.getElementById(targetDiv);
            targetDivError.innerHTML =
                "Sorry, an error occurred while creating your visualization.";
            console.log("Get Trace Failed -- ");
            console.log(error);
        }

        this.logBookEvent({
            event: "codelens",
            act: "view",
            div_id: this.divid,
        });
    }

    /**
     * Seperates text into multiple .java files
     * @param  {String} text String with muliple java classes needed to be seperated
     * @return {array of objects}  .name gives the name of the java file with .java extension
     *                   .content gives the contents of the file
     */
    parseJavaClasses(text) {
        text = text.trim();
        var found = false;
        var stack = 0;
        var startIndex = 0;
        var classes = [];
        var importIndex = 0;
        var endOfLastCommentBeforeClassBegins = 0;
        for (var i = 0; i < text.length; i++) {
            var char = text.charAt(i);
            if (char === "/") {
                i++;
                if (text.charAt(i) === "/") {
                    i++;
                    while (text.charAt(i) !== "\n" && i < text.length) {
                        i++;
                    }
                    if (!found) {
                        endOfLastCommentBeforeClassBegins = i;
                    }
                } else if (text.charAt(i) == "*") {
                    i++;
                    while (
                        (text.charAt(i) !== "*" ||
                            text.charAt(i + 1) !== "/") &&
                        i + 1 < text.length
                    ) {
                        i++;
                    }
                    if (!found) {
                        endOfLastCommentBeforeClassBegins = i;
                    }
                }
            } else if (char === '"') {
                i++;
                while (text.charAt(i) !== '"' && i < text.length) {
                    i++;
                }
            } else if (char === "'") {
                while (text.charAt(i) !== "'" && i < text.length) {
                    i++;
                }
            } else if (char === "(") {
                var pCount = 1;
                i++;
                while (pCount > 0 && i < text.length) {
                    if (text.charAt(i) === "(") {
                        pCount++;
                    } else if (text.charAt(i) === ")") {
                        pCount--;
                    }
                    i++;
                }
            }
            if (!found && text.charAt(i) === "{") {
                startIndex = i;
                found = true;
                stack = 1;
            } else if (found) {
                if (text.charAt(i) === "{") {
                    stack++;
                }
                if (text.charAt(i) === "}") {
                    stack--;
                }
            }
            if (found && stack === 0) {
                let endIndex = i + 1;
                var words = text
                    .substring(endOfLastCommentBeforeClassBegins, startIndex)
                    .trim()
                    .split(" ");
                var className = "";
                for (var w = 0; w < words.length; w++) {
                    className = words[w];
                    if (words[w] === "extends" || words[w] === "implements") {
                        className = words[w - 1];
                        w = words.length;
                    }
                }
                className = className.trim() + ".java";
                classes.push({
                    name: className,
                    content: text.substring(importIndex, endIndex),
                });
                found = false;
                importIndex = endIndex;
                endOfLastCommentBeforeClassBegins = endIndex;
            }
        }
        return classes;
    }
}
function unescapeHtml(safe) {
    if (safe) {
        return safe
            .replace(/&amp;/g, "&")
            .replace(/&lt;/g, "<")
            .replace(/&gt;/g, ">")
            .replace(/&quot;/g, '"')
            .replace(/&#x27;/g, "'");
    }
}


/***/ }),

/***/ "./runestone/activecode/js/md5.js":
/*!****************************************!*\
  !*** ./runestone/activecode/js/md5.js ***!
  \****************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return MD5; });
/**
 *
 *  MD5 (Message-Digest Algorithm)
 *  http://www.webtoolkit.info/
 *
 **/

function MD5(string) {
    function RotateLeft(lValue, iShiftBits) {
        return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
    }

    function AddUnsigned(lX, lY) {
        var lX4, lY4, lX8, lY8, lResult;
        lX8 = lX & 0x80000000;
        lY8 = lY & 0x80000000;
        lX4 = lX & 0x40000000;
        lY4 = lY & 0x40000000;
        lResult = (lX & 0x3fffffff) + (lY & 0x3fffffff);
        if (lX4 & lY4) {
            return lResult ^ 0x80000000 ^ lX8 ^ lY8;
        }
        if (lX4 | lY4) {
            if (lResult & 0x40000000) {
                return lResult ^ 0xc0000000 ^ lX8 ^ lY8;
            } else {
                return lResult ^ 0x40000000 ^ lX8 ^ lY8;
            }
        } else {
            return lResult ^ lX8 ^ lY8;
        }
    }

    function F(x, y, z) {
        return (x & y) | (~x & z);
    }

    function G(x, y, z) {
        return (x & z) | (y & ~z);
    }

    function H(x, y, z) {
        return x ^ y ^ z;
    }

    function I(x, y, z) {
        return y ^ (x | ~z);
    }

    function FF(a, b, c, d, x, s, ac) {
        a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac));
        return AddUnsigned(RotateLeft(a, s), b);
    }

    function GG(a, b, c, d, x, s, ac) {
        a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac));
        return AddUnsigned(RotateLeft(a, s), b);
    }

    function HH(a, b, c, d, x, s, ac) {
        a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac));
        return AddUnsigned(RotateLeft(a, s), b);
    }

    function II(a, b, c, d, x, s, ac) {
        a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac));
        return AddUnsigned(RotateLeft(a, s), b);
    }

    function ConvertToWordArray(string) {
        var lWordCount;
        var lMessageLength = string.length;
        var lNumberOfWords_temp1 = lMessageLength + 8;
        var lNumberOfWords_temp2 =
            (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64;
        var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16;
        var lWordArray = Array(lNumberOfWords - 1);
        var lBytePosition = 0;
        var lByteCount = 0;
        while (lByteCount < lMessageLength) {
            lWordCount = (lByteCount - (lByteCount % 4)) / 4;
            lBytePosition = (lByteCount % 4) * 8;
            lWordArray[lWordCount] =
                lWordArray[lWordCount] |
                (string.charCodeAt(lByteCount) << lBytePosition);
            lByteCount++;
        }
        lWordCount = (lByteCount - (lByteCount % 4)) / 4;
        lBytePosition = (lByteCount % 4) * 8;
        lWordArray[lWordCount] =
            lWordArray[lWordCount] | (0x80 << lBytePosition);
        lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
        lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
        return lWordArray;
    }

    function WordToHex(lValue) {
        var WordToHexValue = "",
            WordToHexValue_temp = "",
            lByte,
            lCount;
        for (lCount = 0; lCount <= 3; lCount++) {
            lByte = (lValue >>> (lCount * 8)) & 255;
            WordToHexValue_temp = "0" + lByte.toString(16);
            WordToHexValue =
                WordToHexValue +
                WordToHexValue_temp.substr(WordToHexValue_temp.length - 2, 2);
        }
        return WordToHexValue;
    }

    function Utf8Encode(string) {
        string = string.replace(/\r\n/g, "\n");
        var utftext = "";

        for (var n = 0; n < string.length; n++) {
            var c = string.charCodeAt(n);

            if (c < 128) {
                utftext += String.fromCharCode(c);
            } else if (c > 127 && c < 2048) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            } else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }
        }

        return utftext;
    }

    var x = Array();
    var k, AA, BB, CC, DD, a, b, c, d;
    var S11 = 7,
        S12 = 12,
        S13 = 17,
        S14 = 22;
    var S21 = 5,
        S22 = 9,
        S23 = 14,
        S24 = 20;
    var S31 = 4,
        S32 = 11,
        S33 = 16,
        S34 = 23;
    var S41 = 6,
        S42 = 10,
        S43 = 15,
        S44 = 21;

    string = Utf8Encode(string);

    x = ConvertToWordArray(string);

    a = 0x67452301;
    b = 0xefcdab89;
    c = 0x98badcfe;
    d = 0x10325476;

    for (k = 0; k < x.length; k += 16) {
        AA = a;
        BB = b;
        CC = c;
        DD = d;
        a = FF(a, b, c, d, x[k + 0], S11, 0xd76aa478);
        d = FF(d, a, b, c, x[k + 1], S12, 0xe8c7b756);
        c = FF(c, d, a, b, x[k + 2], S13, 0x242070db);
        b = FF(b, c, d, a, x[k + 3], S14, 0xc1bdceee);
        a = FF(a, b, c, d, x[k + 4], S11, 0xf57c0faf);
        d = FF(d, a, b, c, x[k + 5], S12, 0x4787c62a);
        c = FF(c, d, a, b, x[k + 6], S13, 0xa8304613);
        b = FF(b, c, d, a, x[k + 7], S14, 0xfd469501);
        a = FF(a, b, c, d, x[k + 8], S11, 0x698098d8);
        d = FF(d, a, b, c, x[k + 9], S12, 0x8b44f7af);
        c = FF(c, d, a, b, x[k + 10], S13, 0xffff5bb1);
        b = FF(b, c, d, a, x[k + 11], S14, 0x895cd7be);
        a = FF(a, b, c, d, x[k + 12], S11, 0x6b901122);
        d = FF(d, a, b, c, x[k + 13], S12, 0xfd987193);
        c = FF(c, d, a, b, x[k + 14], S13, 0xa679438e);
        b = FF(b, c, d, a, x[k + 15], S14, 0x49b40821);
        a = GG(a, b, c, d, x[k + 1], S21, 0xf61e2562);
        d = GG(d, a, b, c, x[k + 6], S22, 0xc040b340);
        c = GG(c, d, a, b, x[k + 11], S23, 0x265e5a51);
        b = GG(b, c, d, a, x[k + 0], S24, 0xe9b6c7aa);
        a = GG(a, b, c, d, x[k + 5], S21, 0xd62f105d);
        d = GG(d, a, b, c, x[k + 10], S22, 0x2441453);
        c = GG(c, d, a, b, x[k + 15], S23, 0xd8a1e681);
        b = GG(b, c, d, a, x[k + 4], S24, 0xe7d3fbc8);
        a = GG(a, b, c, d, x[k + 9], S21, 0x21e1cde6);
        d = GG(d, a, b, c, x[k + 14], S22, 0xc33707d6);
        c = GG(c, d, a, b, x[k + 3], S23, 0xf4d50d87);
        b = GG(b, c, d, a, x[k + 8], S24, 0x455a14ed);
        a = GG(a, b, c, d, x[k + 13], S21, 0xa9e3e905);
        d = GG(d, a, b, c, x[k + 2], S22, 0xfcefa3f8);
        c = GG(c, d, a, b, x[k + 7], S23, 0x676f02d9);
        b = GG(b, c, d, a, x[k + 12], S24, 0x8d2a4c8a);
        a = HH(a, b, c, d, x[k + 5], S31, 0xfffa3942);
        d = HH(d, a, b, c, x[k + 8], S32, 0x8771f681);
        c = HH(c, d, a, b, x[k + 11], S33, 0x6d9d6122);
        b = HH(b, c, d, a, x[k + 14], S34, 0xfde5380c);
        a = HH(a, b, c, d, x[k + 1], S31, 0xa4beea44);
        d = HH(d, a, b, c, x[k + 4], S32, 0x4bdecfa9);
        c = HH(c, d, a, b, x[k + 7], S33, 0xf6bb4b60);
        b = HH(b, c, d, a, x[k + 10], S34, 0xbebfbc70);
        a = HH(a, b, c, d, x[k + 13], S31, 0x289b7ec6);
        d = HH(d, a, b, c, x[k + 0], S32, 0xeaa127fa);
        c = HH(c, d, a, b, x[k + 3], S33, 0xd4ef3085);
        b = HH(b, c, d, a, x[k + 6], S34, 0x4881d05);
        a = HH(a, b, c, d, x[k + 9], S31, 0xd9d4d039);
        d = HH(d, a, b, c, x[k + 12], S32, 0xe6db99e5);
        c = HH(c, d, a, b, x[k + 15], S33, 0x1fa27cf8);
        b = HH(b, c, d, a, x[k + 2], S34, 0xc4ac5665);
        a = II(a, b, c, d, x[k + 0], S41, 0xf4292244);
        d = II(d, a, b, c, x[k + 7], S42, 0x432aff97);
        c = II(c, d, a, b, x[k + 14], S43, 0xab9423a7);
        b = II(b, c, d, a, x[k + 5], S44, 0xfc93a039);
        a = II(a, b, c, d, x[k + 12], S41, 0x655b59c3);
        d = II(d, a, b, c, x[k + 3], S42, 0x8f0ccc92);
        c = II(c, d, a, b, x[k + 10], S43, 0xffeff47d);
        b = II(b, c, d, a, x[k + 1], S44, 0x85845dd1);
        a = II(a, b, c, d, x[k + 8], S41, 0x6fa87e4f);
        d = II(d, a, b, c, x[k + 15], S42, 0xfe2ce6e0);
        c = II(c, d, a, b, x[k + 6], S43, 0xa3014314);
        b = II(b, c, d, a, x[k + 13], S44, 0x4e0811a1);
        a = II(a, b, c, d, x[k + 4], S41, 0xf7537e82);
        d = II(d, a, b, c, x[k + 11], S42, 0xbd3af235);
        c = II(c, d, a, b, x[k + 2], S43, 0x2ad7d2bb);
        b = II(b, c, d, a, x[k + 9], S44, 0xeb86d391);
        a = AddUnsigned(a, AA);
        b = AddUnsigned(b, BB);
        c = AddUnsigned(c, CC);
        d = AddUnsigned(d, DD);
    }

    var temp = WordToHex(a) + WordToHex(b) + WordToHex(c) + WordToHex(d);

    return temp.toLowerCase();
}


/***/ }),

/***/ "./runestone/activecode/js/skulpt-stdlib.js":
/*!**************************************************!*\
  !*** ./runestone/activecode/js/skulpt-stdlib.js ***!
  \**************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

Sk.builtinFiles={"files":{"src/builtin/sys.js":"var $builtinmodule=function(){var b,a=Math.pow,c={},d=[],e=Sk.getSysArgv();for(b=0;b<e.length;++b)d.push(new Sk.builtin.str(e[b]));return c.argv=new Sk.builtins.list(d),c.copyright=new Sk.builtin.str(\"Copyright 2009-2010 Scott Graham.\\nAll Rights Reserved.\\n\"),Sk.__future__.python3?(c.version=\"3.7(ish) [Skulpt]\",c.version_info=new Sk.builtin.tuple([new Sk.builtin.int_(3),new Sk.builtin.int_(7)])):(c.version=\"2.7(ish) [Skulpt]\",c.version_info=new Sk.builtin.tuple([new Sk.builtin.int_(2),new Sk.builtin.int_(7)])),c.maxint=new Sk.builtin.int_(a(2,53)-1),c.maxsize=new Sk.builtin.int_(a(2,53)-1),c.modules=Sk.sysmodules,c.path=Sk.realsyspath,c.getExecutionLimit=new Sk.builtin.func(function(){return null===Sk.execLimit?Sk.builtin.none.none$:new Sk.builtin.int_(Sk.execLimit)}),c.setExecutionLimit=new Sk.builtin.func(function(a){if(null===Sk.execLimit)throw new Sk.builtin.NotImplementedError(\"Execution limiting is not enabled\");void 0!==a&&(Sk.execLimit=Sk.builtin.asnum$(a))}),c.resetTimeout=new Sk.builtin.func(function(){Sk.execStart=new Date}),c.getYieldLimit=new Sk.builtin.func(function(){return null===Sk.yieldLimit?Sk.builtin.none.none$:new Sk.builtin.int_(Sk.yieldLimit)}),c.setYieldLimit=new Sk.builtin.func(function(a){if(null===Sk.yieldLimit)throw new Sk.builtin.NotImplementedError(\"Yielding is not enabled\");void 0!==a&&(Sk.yieldLimit=Sk.builtin.asnum$(a))}),c.debug=new Sk.builtin.func(function(){return Sk.builtin.none.none$}),c.__stdout__=new Sk.builtin.file(new Sk.builtin.str(\"/dev/stdout\"),new Sk.builtin.str(\"w\")),c.__stdin__=new Sk.builtin.file(new Sk.builtin.str(\"/dev/stdin\"),new Sk.builtin.str(\"r\")),c.stdout=c.__stdout__,c.stdin=c.__stdin__,c};","src/builtin/this.py":"s = \"\"\"Gur Mra bs Clguba, ol Gvz Crgref\n\nOrnhgvshy vf orggre guna htyl.\nRkcyvpvg vf orggre guna vzcyvpvg.\nFvzcyr vf orggre guna pbzcyrk.\nPbzcyrk vf orggre guna pbzcyvpngrq.\nSyng vf orggre guna arfgrq.\nFcnefr vf orggre guna qrafr.\nErnqnovyvgl pbhagf.\nFcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.\nNygubhtu cenpgvpnyvgl orngf chevgl.\nReebef fubhyq arire cnff fvyragyl.\nHayrff rkcyvpvgyl fvyraprq.\nVa gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.\nGurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.\nNygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu.\nAbj vf orggre guna arire.\nNygubhtu arire vf bsgra orggre guna *evtug* abj.\nVs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn.\nVs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.\nAnzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!\"\"\"\n\nd = {}\nfor c in (65, 97):\n    for i in range(26):\n        d[chr(i+c)] = chr((i+13) % 26 + c)\n\nprint(\"\".join([d.get(c, c) for c in s]))\n","src/lib/BaseHTTPServer.py":"raise NotImplementedError(\"BaseHTTPServer is not yet implemented in Skulpt\")\n","src/lib/Bastion.py":"raise NotImplementedError(\"Bastion is not yet implemented in Skulpt\")\n","src/lib/CGIHTTPServer.py":"raise NotImplementedError(\"CGIHTTPServer is not yet implemented in Skulpt\")\n","src/lib/ConfigParser.py":"raise NotImplementedError(\"ConfigParser is not yet implemented in Skulpt\")\n","src/lib/Cookie.py":"raise NotImplementedError(\"Cookie is not yet implemented in Skulpt\")\n","src/lib/DocXMLRPCServer.py":"raise NotImplementedError(\"DocXMLRPCServer is not yet implemented in Skulpt\")\n","src/lib/HTMLParser.py":"raise NotImplementedError(\"HTMLParser is not yet implemented in Skulpt\")\n","src/lib/MimeWriter.py":"raise NotImplementedError(\"MimeWriter is not yet implemented in Skulpt\")\n","src/lib/Queue.py":"raise NotImplementedError(\"Queue is not yet implemented in Skulpt\")\n","src/lib/SimpleHTTPServer.py":"raise NotImplementedError(\"SimpleHTTPServer is not yet implemented in Skulpt\")\n","src/lib/SimpleXMLRPCServer.py":"raise NotImplementedError(\"SimpleXMLRPCServer is not yet implemented in Skulpt\")\n","src/lib/SocketServer.py":"raise NotImplementedError(\"SocketServer is not yet implemented in Skulpt\")\n","src/lib/StringIO.py":"r\"\"\"File-like objects that read from or write to a string buffer.\n\nThis implements (nearly) all stdio methods.\n\nf = StringIO()      # ready for writing\nf = StringIO(buf)   # ready for reading\nf.close()           # explicitly release resources held\nflag = f.isatty()   # always false\npos = f.tell()      # get current position\nf.seek(pos)         # set current position\nf.seek(pos, mode)   # mode 0: absolute; 1: relative; 2: relative to EOF\nbuf = f.read()      # read until EOF\nbuf = f.read(n)     # read up to n bytes\nbuf = f.readline()  # read until end of line ('\\n') or EOF\nlist = f.readlines()# list of f.readline() results until EOF\nf.truncate([size])  # truncate file at to at most size (default: current pos)\nf.write(buf)        # write at current position\nf.writelines(list)  # for line in list: f.write(line)\nf.getvalue()        # return whole file's contents as a string\n\nNotes:\n- Using a real file is often faster (but less convenient).\n- There's also a much faster implementation in C, called cStringIO, but\n  it's not subclassable.\n- fileno() is left unimplemented so that code which uses it triggers\n  an exception early.\n- Seeking far beyond EOF and then writing will insert real null\n  bytes that occupy space in the buffer.\n- There's a simple test set (see end of this file).\n\"\"\"\n\n__all__ = [\"StringIO\"]\n\ndef _complain_ifclosed(closed):\n    if closed:\n        raise ValueError(\"I/O operation on closed file\")\n\nclass StringIO:\n    \"\"\"class StringIO([buffer])\n\n    When a StringIO object is created, it can be initialized to an existing\n    string by passing the string to the constructor. If no string is given,\n    the StringIO will start empty.\n\n    The StringIO object can accept either Unicode or 8-bit strings, but\n    mixing the two may take some care. If both are used, 8-bit strings that\n    cannot be interpreted as 7-bit ASCII (that use the 8th bit) will cause\n    a UnicodeError to be raised when getvalue() is called.\n    \"\"\"\n    def __init__(self, buf = ''):\n        # Force self.buf to be a string or unicode\n        if not isinstance(buf, str):\n            buf = str(buf)\n        self.buf = buf\n        self.len = len(buf)\n        self.buflist = []\n        self.pos = 0\n        self.closed = False\n        self.softspace = 0\n\n    def __iter__(self):\n        return self\n\n    def next(self):\n        \"\"\"A file object is its own iterator, for example iter(f) returns f\n        (unless f is closed). When a file is used as an iterator, typically\n        in a for loop (for example, for line in f: print line), the next()\n        method is called repeatedly. This method returns the next input line,\n        or raises StopIteration when EOF is hit.\n        \"\"\"\n        _complain_ifclosed(self.closed)\n        r = self.readline()\n        if not r:\n            raise StopIteration\n        return r\n\n    def close(self):\n        \"\"\"Free the memory buffer.\n        \"\"\"\n        if not self.closed:\n            self.closed = True\n            self.buf = None\n            self.pos = None\n\n    def isatty(self):\n        \"\"\"Returns False because StringIO objects are not connected to a\n        tty-like device.\n        \"\"\"\n        _complain_ifclosed(self.closed)\n        return False\n\n    def seek(self, pos, mode = 0):\n        \"\"\"Set the file's current position.\n\n        The mode argument is optional and defaults to 0 (absolute file\n        positioning); other values are 1 (seek relative to the current\n        position) and 2 (seek relative to the file's end).\n\n        There is no return value.\n        \"\"\"\n        _complain_ifclosed(self.closed)\n        if self.buflist:\n            self.buf += ''.join(self.buflist)\n            self.buflist = []\n        if mode == 1:\n            pos += self.pos\n        elif mode == 2:\n            pos += self.len\n        self.pos = max(0, pos)\n\n    def tell(self):\n        \"\"\"Return the file's current position.\"\"\"\n        _complain_ifclosed(self.closed)\n        return self.pos\n\n    def read(self, n = -1):\n        \"\"\"Read at most size bytes from the file\n        (less if the read hits EOF before obtaining size bytes).\n\n        If the size argument is negative or omitted, read all data until EOF\n        is reached. The bytes are returned as a string object. An empty\n        string is returned when EOF is encountered immediately.\n        \"\"\"\n        _complain_ifclosed(self.closed)\n        if self.buflist:\n            self.buf += ''.join(self.buflist)\n            self.buflist = []\n        if n is None or n < 0:\n            newpos = self.len\n        else:\n            newpos = min(self.pos+n, self.len)\n        r = self.buf[self.pos:newpos]\n        self.pos = newpos\n        return r\n\n    def readline(self, length=None):\n        r\"\"\"Read one entire line from the file.\n\n        A trailing newline character is kept in the string (but may be absent\n        when a file ends with an incomplete line). If the size argument is\n        present and non-negative, it is a maximum byte count (including the\n        trailing newline) and an incomplete line may be returned.\n\n        An empty string is returned only when EOF is encountered immediately.\n\n        Note: Unlike stdio's fgets(), the returned string contains null\n        characters ('\\0') if they occurred in the input.\n        \"\"\"\n        _complain_ifclosed(self.closed)\n        if self.buflist:\n            self.buf += ''.join(self.buflist)\n            self.buflist = []\n        i = self.buf.find('\\n', self.pos)\n        if i < 0:\n            newpos = self.len\n        else:\n            newpos = i+1\n        if length is not None and length >= 0:\n            if self.pos + length < newpos:\n                newpos = self.pos + length\n        r = self.buf[self.pos:newpos]\n        self.pos = newpos\n        return r\n\n    def readlines(self, sizehint = 0):\n        \"\"\"Read until EOF using readline() and return a list containing the\n        lines thus read.\n\n        If the optional sizehint argument is present, instead of reading up\n        to EOF, whole lines totalling approximately sizehint bytes (or more\n        to accommodate a final whole line).\n        \"\"\"\n        total = 0\n        lines = []\n        line = self.readline()\n        while line:\n            lines.append(line)\n            total += len(line)\n            if 0 < sizehint <= total:\n                break\n            line = self.readline()\n        return lines\n\n    def truncate(self, size=None):\n        \"\"\"Truncate the file's size.\n\n        If the optional size argument is present, the file is truncated to\n        (at most) that size. The size defaults to the current position.\n        The current file position is not changed unless the position\n        is beyond the new file size.\n\n        If the specified size exceeds the file's current size, the\n        file remains unchanged.\n        \"\"\"\n        _complain_ifclosed(self.closed)\n        if size is None:\n            size = self.pos\n        elif size < 0:\n            raise IOError(22, \"Negative size not allowed\")\n        elif size < self.pos:\n            self.pos = size\n        self.buf = self.getvalue()[:size]\n        self.len = size\n\n    def write(self, s):\n        \"\"\"Write a string to the file.\n\n        There is no return value.\n        \"\"\"\n        _complain_ifclosed(self.closed)\n        if not s: return\n        # Force s to be a string or unicode\n        if not isinstance(s, str):\n            s = str(s)\n        spos = self.pos\n        slen = self.len\n        if spos == slen:\n            self.buflist.append(s)\n            self.len = self.pos = spos + len(s)\n            return\n        if spos > slen:\n            self.buflist.append('\\0'*(spos - slen))\n            slen = spos\n        newpos = spos + len(s)\n        if spos < slen:\n            if self.buflist:\n                self.buf += ''.join(self.buflist)\n            self.buflist = [self.buf[:spos], s, self.buf[newpos:]]\n            self.buf = ''\n            if newpos > slen:\n                slen = newpos\n        else:\n            self.buflist.append(s)\n            slen = newpos\n        self.len = slen\n        self.pos = newpos\n\n    def writelines(self, iterable):\n        \"\"\"Write a sequence of strings to the file. The sequence can be any\n        iterable object producing strings, typically a list of strings. There\n        is no return value.\n\n        (The name is intended to match readlines(); writelines() does not add\n        line separators.)\n        \"\"\"\n        write = self.write\n        for line in iterable:\n            write(line)\n\n    def flush(self):\n        \"\"\"Flush the internal buffer\n        \"\"\"\n        _complain_ifclosed(self.closed)\n\n    def getvalue(self):\n        \"\"\"\n        Retrieve the entire contents of the \"file\" at any time before\n        the StringIO object's close() method is called.\n\n        The StringIO object can accept either Unicode or 8-bit strings,\n        but mixing the two may take some care. If both are used, 8-bit\n        strings that cannot be interpreted as 7-bit ASCII (that use the\n        8th bit) will cause a UnicodeError to be raised when getvalue()\n        is called.\n        \"\"\"\n        _complain_ifclosed(self.closed)\n        if self.buflist:\n            self.buf += ''.join(self.buflist)\n            self.buflist = []\n        return self.buf\n","src/lib/UserDict.py":"raise NotImplementedError(\"UserDict is not yet implemented in Skulpt\")\n","src/lib/UserList.py":"raise NotImplementedError(\"UserList is not yet implemented in Skulpt\")\n","src/lib/UserString.py":"raise NotImplementedError(\"UserString is not yet implemented in Skulpt\")\n","src/lib/_LWPCookieJar.py":"raise NotImplementedError(\"_LWPCookieJar is not yet implemented in Skulpt\")\n","src/lib/_MozillaCookieJar.py":"raise NotImplementedError(\"_MozillaCookieJar is not yet implemented in Skulpt\")\n","src/lib/__future__.py":"raise NotImplementedError(\"__future__ is not yet implemented in Skulpt\")\n","src/lib/__phello__.foo.py":"raise NotImplementedError(\"__phello__.foo is not yet implemented in Skulpt\")\n","src/lib/_abcoll.py":"raise NotImplementedError(\"_abcoll is not yet implemented in Skulpt\")\n","src/lib/_strptime.py":"raise NotImplementedError(\"_strptime is not yet implemented in Skulpt\")\n","src/lib/_threading_local.py":"raise NotImplementedError(\"_threading_local is not yet implemented in Skulpt\")\n","src/lib/abc.py":"raise NotImplementedError(\"abc is not yet implemented in Skulpt\")\n","src/lib/aifc.py":"raise NotImplementedError(\"aifc is not yet implemented in Skulpt\")\n","src/lib/altair/__init__.py":"import random\n\ntry:\n    from vega_caller import render_graph\nexcept:\n    print(\"Mock render\")\n\n    def render_graph(js):\n        print(js)\n\n\nclass Chart:\n    # TODO allow data to be specified as a URL to a CSV or DB or json\n    def __init__(self, data, title=None):\n        self.title = title\n        self.data_id = random.randrange(1000000, 5000000)\n        self.dsname = \"data-{}\".format(self.data_id)\n        if isinstance(data, dict):\n            self.data = Data(**data)\n        else:\n            self.data = data\n\n        self.json = {}\n        self.json[\"$schema\"] = \"https://vega.github.io/schema/vega-lite/v2.5.json\"\n        self.json[\"data\"] = {}\n        if self.title:\n            self.json[\"title\"] = self.title\n\n        self.json[\"data\"] = {\"values\": self.data.vals}\n        self.is_composite = False\n\n    # mark can be a simple string or a dictionary\n    # \"mark\": {\"color\": \"green\", \"opacity\": 0.2, \"type\": \"rect\"}\n\n    def mark_json(self, mark_type, **kwargs):\n        if kwargs:\n            self.json[\"mark\"] = {}\n            self.json[\"mark\"][\"type\"] = mark_type\n            self.json[\"mark\"].update(kwargs)\n        else:\n            self.json[\"mark\"] = mark_type\n\n        return self\n\n    def mark_bar(self, **kwargs):\n        return self.mark_json(\"bar\", **kwargs)\n\n    def mark_point(self, **kwargs):\n        return self.mark_json(\"point\", **kwargs)\n\n    def mark_line(self, **kwargs):\n        return self.mark_json(\"line\", **kwargs)\n\n    def mark_rect(self, **kwargs):\n        return self.mark_json(\"rect\", **kwargs)\n\n    def mark_area(self, **kwargs):\n        return self.mark_json(\"area\", **kwargs)\n\n    def mark_tick(self, **kwargs):\n        return self.mark_json(\"tick\", **kwargs)\n\n    def encode(self, x=\"\", y=\"\", color=\"\", size=\"\", tooltip=\"\"):\n        self.encoding = {}\n        if x:\n            if isinstance(x, Axis):\n                self.encoding[\"x\"] = x.toJson()\n            else:\n                self.encoding[\"x\"] = Axis(x).toJson()\n        if y:\n            if isinstance(y, Axis):\n                self.encoding[\"y\"] = y.toJson()\n            else:\n                self.encoding[\"y\"] = Axis(y).toJson()\n\n        # \"color\": {\"type\": \"nominal\", \"field\": \"species\"}\n        if color:\n            field, tp = _get_name_type(color)\n            self.encoding[\"color\"] = dict(type=tp, field=field)\n\n        # \"size\": {\"type\": \"quantitative\", \"field\": \"Acceleration\"}\n        if size:\n            field, tp = _get_name_type(size)\n            self.encoding[\"size\"] = dict(type=tp, field=field)\n\n        if tooltip:\n            field, tp = _get_name_type(tooltip)\n            self.encoding[\"tooltip\"] = dict(type=tp, field=field)\n\n        self.json[\"encoding\"] = self.encoding\n        return self\n\n    def interactive(self):\n        iconfig = {\n            \"selector002\": {\n                \"type\": \"interval\",\n                \"bind\": \"scales\",\n                \"encodings\": [\"x\", \"y\"],\n                \"on\": \"[mousedown, window:mouseup] > window:mousemove!\",\n                \"translate\": \"[mousedown, window:mouseup] > window:mousemove!\",\n                \"zoom\": \"wheel!\",\n                \"mark\": {\"fill\": \"#333\", \"fillOpacity\": 0.125, \"stroke\": \"white\"},\n                \"resolve\": \"global\",\n            }\n        }\n        self.json[\"selection\"] = iconfig\n        return self\n\n    def display(self):\n        render_graph(self.json)\n\n    def __add__(self, other):\n        return self.add_layer(other, \"layer\")\n\n    def __and__(self, other):\n        return self.add_layer(other, \"vconcat\")\n\n    def __or__(self, other):\n        return self.add_layer(other, \"hconcat\")\n\n    def add_layer(self, other, lname):\n        # multle layers are added by concatenating the new element to the\n        # existing layer.  If the layer does not exist then initialize\n        # it from self.\n        if lname not in self.json:\n            self.json[lname] = []\n            self.json[\"datasets\"] = {}\n            temp = {}\n            temp[\"data\"] = {\"name\": self.dsname}\n            self.json[\"datasets\"][self.dsname] = self.json[\"data\"][\"values\"]\n            del self.json[\"data\"]\n            temp[\"encoding\"] = self.json[\"encoding\"]\n            del self.json[\"encoding\"]\n            temp[\"mark\"] = self.json[\"mark\"]\n            del self.json[\"mark\"]\n            self.json[lname].append(temp)\n        temp = {}\n        self.json[\"datasets\"][other.dsname] = other.json[\"data\"][\"values\"]\n        temp[\"data\"] = {\"name\": other.dsname}\n        temp[\"encoding\"] = other.json[\"encoding\"]\n        temp[\"mark\"] = other.json[\"mark\"]\n        self.json[lname].append(temp)\n        return self\n\n\n#   \"datasets\": {\n#     \"data-e56e43354d9b8f2ab0d1e90cf27cd47f\": [\n#       {\"a\": 4, \"b\": 1, \"c\": \"r\"},\n#       {\"a\": 5, \"b\": 2, \"c\": \"g\"},\n#       {\"a\": 6, \"b\": 3, \"c\": \"b\"}\n#     ],\n#     \"data-a4c3047a15bf9380c2e615cf87648369\": [\n#       {\"a\": 1, \"b\": 4, \"c\": \"r\"},\n#       {\"a\": 2, \"b\": 5, \"c\": \"g\"},\n#       {\"a\": 3, \"b\": 6, \"c\": \"b\"}\n#     ]\n#   }\n\n# TODO:  add a rect mark type for a heat map\n\n\ndef _get_name_type(name):\n    typed = {\"Q\": \"quantitative\", \"O\": \"ordinal\", \"N\": \"nominal\", \"T\": \"temporal\"}\n    nt = name.split(\":\")\n    tstring = \"quantitative\"\n    if len(nt) == 2:\n        tstring = typed[nt[1]]\n    return nt[0], tstring\n\n\nclass Axis:\n    def __init__(self, name, bin=False):\n        self.aggregate = None\n        self.name, self.type = _get_name_type(name)\n        if \"()\" in self.name:  # aggregate function\n            self.aggregate = self.name.replace(\"()\", \"\")\n            self.name = None\n        self.bin = bin\n\n    def toJson(self):\n        json = {}\n        if self.name:\n            json[\"field\"] = self.name\n        if self.type:\n            json[\"type\"] = self.type\n        if self.bin:\n            json[\"bin\"] = True\n        if self.aggregate:\n            json[\"aggregate\"] = self.aggregate\n\n        return json\n\n\nclass X(Axis):\n    pass\n\n\nclass Y(Axis):\n    pass\n\n\nclass Data:\n    def __init__(self, **kwargs):\n        \"\"\"\n        input can be the form of a series of keyword args where\n        the keyword is the column name, or a single keyword data\n        that is a list of json style records as rows.\n        could also accept a csv file? or keywords columns and rows\n        This accepts a lot more options that \"real altair\" to make it easier\n        to work with non-DataFrame data sets.  Internally:\n        self.keys contains the list of column names\n        self.vals is a list of dictionaries of the form [{col1:val1, col2:val2, ... coln:valn},\n                                                         {col1:val3, col2:val4, ... coln:valn},\n                                                         {col1:val5, col2:val6, ... coln:valn}]\n        \"\"\"\n        # todo: accept a URL as the argument to read a CSV or JSON file\n        if len(kwargs) == 1 and \"data\" in kwargs:\n            self.data = kwargs[\"data\"]\n            self.keys = self.data[0].keys()\n            self.vals = self.data\n        elif len(kwargs) == 2 and (\"columns\" in kwargs) and (\"rows\" in kwargs):\n            self.keys = kwargs[\"columns\"]\n            self.vals = []\n            for r in kwargs[\"rows\"]:\n                self.vals.append(dict(zip(self.keys, r)))\n        else:\n            keys = kwargs.keys()\n            vals = []\n            primary_key = list(keys)[0]\n            for ix in range(len(kwargs[primary_key])):\n                d = {}\n                for key in keys:\n                    d[key] = kwargs[key][ix]\n                vals.append(d)\n            self.vals = vals\n            self.keys = keys\n\n    def __str__(self):\n        # header = \"\\t\".join(self.keys) + \"\\n\"\n        header = \"\"\n        for key in self.keys:\n            header += key.center(10) + \" \"\n        header += \"\\n\"\n        template = \"\"\n        for key in self.keys:\n            template += \"{\" + key + \": <10} \"\n        template += \"\\n\"\n        res = \"\"\n        for row in self.vals:\n            res += template.format(**row)\n\n        return header + res\n\n\nif __name__ == \"__main__\":\n    d = Data(a=[3, 4, 5], b=[1, 2, 3], c=[\"r\", \"g\", \"b\"])\n    print(d)\n    print(\n        type(\n            Chart({\"a\": list(\"abc\"), \"b\": [1, 2, 3]}).mark_bar().encode(x=\"a:N\", y=\"b\")\n        )\n    )\n    aa = (\n        Chart(Data(a=[3, 4, 5], b=[1, 2, 3], c=[\"r\", \"g\", \"b\"]))\n        .mark_point(color=\"red\")\n        .encode(x=\"b\", y=\"a\", color=\"c:O\")\n    )\n    print(\"aa = \", aa)\n    aa.display()\n\n    bb = (\n        Chart(Data(a=[1, 2, 3], b=[4, 5, 6], c=[\"r\", \"g\", \"b\"]))\n        .mark_line()\n        .encode(x=\"b\", y=\"a\", color=\"c:O\")\n    )\n    print(\"bb = \", bb)\n    bb.display()\n    print(\"aa+bb\", (aa + bb).display())\n    # Chart(Data(a=[1,2,3,2,2,4,5,5,6,7,8,8,8,8,8,9,0,0])).mark_bar().encode(Axis('a:Q', bin=True),y='count()')\n\n\n# todo: see if I can work with the repr of an object to get the behavior that altair gets in notebooks\n# todo: implement a Bin object to specifiy maxbins\n","src/lib/altair/vega_caller.js":"var $builtinmodule=function(){var a={__file__:\"/src/lib/vega_caller.js\",__package__:Sk.builtin.none.none$,render_graph:new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgs(\"render_graph\",arguments,1,1);let b=\"#\"+Sk.divid+\"_graphics\";try{vegaEmbed(b,Sk.ffi.remapToJs(a),{actions:!0})}catch(a){throw new Sk.builtin.Exception(\"Error in graph specification unable to render the graph\\n\"+a.message)}})};return a};","src/lib/antigravity.py":"import webbrowser\n\nwebbrowser.open(\"https://xkcd.com/353/\")\n","src/lib/anydbm.py":"raise NotImplementedError(\"anydbm is not yet implemented in Skulpt\")\n","src/lib/array.js":"$builtinmodule=function(){var a={},b=[\"c\",\"b\",\"B\",\"u\",\"h\",\"H\",\"i\",\"I\",\"l\",\"L\",\"f\",\"d\"];return a.__name__=new Sk.builtin.str(\"array\"),a.array=Sk.misceval.buildClass(a,function(a,c){c.__init__=new Sk.builtin.func(function(a,c,d){if(Sk.builtin.pyCheckArgsLen(\"__init__\",arguments.length,2,3),-1==b.indexOf(Sk.ffi.remapToJs(c)))throw new Sk.builtin.ValueError(\"bad typecode (must be c, b, B, u, h, H, i, I, l, L, f or d)\");if(d&&!Sk.builtin.checkIterable(d))throw new Sk.builtin.TypeError(\"iteration over non-sequence\");if(a.$d.mp$ass_subscript(new Sk.builtin.str(\"typecode\"),c),a.$d.mp$ass_subscript(new Sk.builtin.str(\"__module__\"),new Sk.builtin.str(\"array\")),a.typecode=c,void 0===d)a.internalIterable=new Sk.builtin.list;else if(d instanceof Sk.builtin.list)a.internalIterable=d;else for(a.internalIterable=new Sk.builtin.list,iter=Sk.abstr.iter(d),item=iter.tp$iternext();void 0!==item;item=iter.tp$iternext())Sk.misceval.callsimArray(a.internalIterable.append,[a.internalIterable,item])}),c.__repr__=new Sk.builtin.func(function(a){var b=Sk.ffi.remapToJs(a.typecode),c=\"\";return Sk.ffi.remapToJs(a.internalIterable).length&&(\"c\"==Sk.ffi.remapToJs(a.typecode)?c=\", '\"+Sk.ffi.remapToJs(a.internalIterable).join(\"\")+\"'\":c=\", \"+Sk.ffi.remapToJs(Sk.misceval.callsimArray(a.internalIterable.__repr__,[a.internalIterable]))),new Sk.builtin.str(\"array('\"+b+\"'\"+c+\")\")}),c.__str__=c.__repr__,c.__getattribute__=new Sk.builtin.func(function(a,b){return a.tp$getattr(b)}),c.append=new Sk.builtin.func(function(a,b){return Sk.misceval.callsimArray(a.internalIterable.append,[a.internalIterable,b]),Sk.builtin.none.none$}),c.extend=new Sk.builtin.func(function(a,b){if(Sk.builtin.pyCheckArgsLen(\"__init__\",arguments.length,2,2),!Sk.builtin.checkIterable(b))throw new Sk.builtin.TypeError(\"iteration over non-sequence\");for(iter=Sk.abstr.iter(b),item=iter.tp$iternext();void 0!==item;item=iter.tp$iternext())Sk.misceval.callsimArray(a.internalIterable.append,[a.internalIterable,item])})},\"array\",[]),a};","src/lib/ast.py":"raise NotImplementedError(\"ast is not yet implemented in Skulpt\")\n","src/lib/asynchat.py":"raise NotImplementedError(\"asynchat is not yet implemented in Skulpt\")\n","src/lib/asyncore.py":"raise NotImplementedError(\"asyncore is not yet implemented in Skulpt\")\n","src/lib/atexit.py":"raise NotImplementedError(\"atexit is not yet implemented in Skulpt\")\n","src/lib/audiodev.py":"raise NotImplementedError(\"audiodev is not yet implemented in Skulpt\")\n","src/lib/base64.py":"raise NotImplementedError(\"base64 is not yet implemented in Skulpt\")\n","src/lib/bdb.py":"raise NotImplementedError(\"bdb is not yet implemented in Skulpt\")\n","src/lib/binhex.py":"raise NotImplementedError(\"binhex is not yet implemented in Skulpt\")\n","src/lib/bisect.py":"\"\"\"Bisection algorithms.\"\"\"\n\ndef insort_right(a, x, lo=0, hi=None):\n    \"\"\"Insert item x in list a, and keep it sorted assuming a is sorted.\n\n    If x is already in a, insert it to the right of the rightmost x.\n\n    Optional args lo (default 0) and hi (default len(a)) bound the\n    slice of a to be searched.\n    \"\"\"\n\n    if lo < 0:\n        raise ValueError('lo must be non-negative')\n    if hi is None:\n        hi = len(a)\n    while lo < hi:\n        mid = (lo+hi)//2\n        if x < a[mid]: hi = mid\n        else: lo = mid+1\n    a.insert(lo, x)\n\ndef bisect_right(a, x, lo=0, hi=None):\n    \"\"\"Return the index where to insert item x in list a, assuming a is sorted.\n\n    The return value i is such that all e in a[:i] have e <= x, and all e in\n    a[i:] have e > x.  So if x already appears in the list, a.insert(x) will\n    insert just after the rightmost x already there.\n\n    Optional args lo (default 0) and hi (default len(a)) bound the\n    slice of a to be searched.\n    \"\"\"\n\n    if lo < 0:\n        raise ValueError('lo must be non-negative')\n    if hi is None:\n        hi = len(a)\n    while lo < hi:\n        mid = (lo+hi)//2\n        if x < a[mid]: hi = mid\n        else: lo = mid+1\n    return lo\n\ndef insort_left(a, x, lo=0, hi=None):\n    \"\"\"Insert item x in list a, and keep it sorted assuming a is sorted.\n\n    If x is already in a, insert it to the left of the leftmost x.\n\n    Optional args lo (default 0) and hi (default len(a)) bound the\n    slice of a to be searched.\n    \"\"\"\n\n    if lo < 0:\n        raise ValueError('lo must be non-negative')\n    if hi is None:\n        hi = len(a)\n    while lo < hi:\n        mid = (lo+hi)//2\n        if a[mid] < x: lo = mid+1\n        else: hi = mid\n    a.insert(lo, x)\n\n\ndef bisect_left(a, x, lo=0, hi=None):\n    \"\"\"Return the index where to insert item x in list a, assuming a is sorted.\n\n    The return value i is such that all e in a[:i] have e < x, and all e in\n    a[i:] have e >= x.  So if x already appears in the list, a.insert(x) will\n    insert just before the leftmost x already there.\n\n    Optional args lo (default 0) and hi (default len(a)) bound the\n    slice of a to be searched.\n    \"\"\"\n\n    if lo < 0:\n        raise ValueError('lo must be non-negative')\n    if hi is None:\n        hi = len(a)\n    while lo < hi:\n        mid = (lo+hi)//2\n        if a[mid] < x: lo = mid+1\n        else: hi = mid\n    return lo\n\n# Overwrite above definitions with a fast C implementation\ntry:\n    from _bisect import *\nexcept ImportError:\n    pass\n\n# Create aliases\nbisect = bisect_right\ninsort = insort_right\n","src/lib/bsddb/__init__.py":"raise NotImplementedError(\"bsddb is not yet implemented in Skulpt\")\n","src/lib/cProfile.py":"raise NotImplementedError(\"cProfile is not yet implemented in Skulpt\")\n","src/lib/calendar.py":"raise NotImplementedError(\"calendar is not yet implemented in Skulpt\")\n","src/lib/cgi.py":"raise NotImplementedError(\"cgi is not yet implemented in Skulpt\")\n","src/lib/cgitb.py":"raise NotImplementedError(\"cgitb is not yet implemented in Skulpt\")\n","src/lib/chunk.py":"raise NotImplementedError(\"chunk is not yet implemented in Skulpt\")\n","src/lib/cmd.py":"raise NotImplementedError(\"cmd is not yet implemented in Skulpt\")\n","src/lib/code.py":"raise NotImplementedError(\"code is not yet implemented in Skulpt\")\n","src/lib/codecs.py":"raise NotImplementedError(\"codecs is not yet implemented in Skulpt\")\n","src/lib/codeop.py":"raise NotImplementedError(\"codeop is not yet implemented in Skulpt\")\n","src/lib/collections.js":"var $builtinmodule=function(){return Sk.misceval.chain(Sk.importModule(\"keyword\",!1,!0),function(a){var b=Number.MAX_SAFE_INTEGER,c=Number.isInteger,d={};d.defaultdict=function defaultdict(a,b){if(!(this instanceof d.defaultdict))return new d.defaultdict(a,b);if(Sk.abstr.superConstructor(d.defaultdict,this,b),void 0===a)this.default_factory=Sk.builtin.none.none$;else{if(!Sk.builtin.checkCallable(a)&&a!==Sk.builtin.none.none$)throw new Sk.builtin.TypeError(\"first argument must be callable\");this.default_factory=a}return this.$d?this.$d.default_factory=this.default_factory:this.$d={default_factory:this.default_factory},this},Sk.abstr.setUpInheritance(\"defaultdict\",d.defaultdict,Sk.builtin.dict),d.defaultdict.prototype.$r=function(){var a=Sk.misceval.objectRepr(this.default_factory).v,b=Sk.builtin.dict.prototype.$r.call(this).v;return new Sk.builtin.str(\"defaultdict(\"+a+\", \"+b+\")\")},d.defaultdict.prototype.__copy__=function(a){var b,c,e,f=[];for(c=Sk.abstr.iter(a),e=c.tp$iternext();void 0!==e;e=c.tp$iternext())b=a.mp$subscript(e),f.push(e),f.push(b);return new d.defaultdict(a.$d.default_factory,f)},d.defaultdict.prototype.__missing__=function(a){if(Sk.builtin.pyCheckArgsLen(\"__missing__\",arguments.length,0,1),a)throw new Sk.builtin.KeyError(Sk.misceval.objectRepr(a));else return Sk.misceval.callsimArray(this.default_factory)},d.defaultdict.prototype.mp$subscript=function(a){try{return Sk.builtin.dict.prototype.mp$subscript.call(this,a)}catch(b){return this.default_factory===Sk.builtin.none.none$?this.__missing__(a):(ret=this.__missing__(),this.mp$ass_subscript(a,ret),ret)}},d.Counter=function Counter(a){if(!(this instanceof d.Counter))return new d.Counter(a);if(a instanceof Sk.builtin.dict||void 0===a)Sk.abstr.superConstructor(d.Counter,this,a);else{if(!Sk.builtin.checkIterable(a))throw new Sk.builtin.TypeError(\"'\"+Sk.abstr.typeName(a)+\"' object is not iterable\");Sk.abstr.superConstructor(d.Counter,this);for(var b,c=new Sk.builtin.int_(1),e=a.tp$iter(),f=e.tp$iternext();void 0!==f;f=e.tp$iternext())b=this.mp$subscript(f),b=b.nb$add(c),this.mp$ass_subscript(f,b)}return this},Sk.abstr.setUpInheritance(\"Counter\",d.Counter,Sk.builtin.dict),d.Counter.prototype.$r=function(){var a=0<this.size?Sk.builtin.dict.prototype.$r.call(this).v:\"\";return new Sk.builtin.str(\"Counter(\"+a+\")\")},d.Counter.prototype.mp$subscript=function(a){try{return Sk.builtin.dict.prototype.mp$subscript.call(this,a)}catch(a){return new Sk.builtin.int_(0)}},d.Counter.prototype.elements=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen(\"elements\",arguments.length,1,1);for(var b=[],c=a.tp$iter(),d=c.tp$iternext();void 0!==d;d=c.tp$iternext())for(var e=0;e<a.mp$subscript(d).v;e++)b.push(d);var f={tp$iter:function(){return f},$obj:this,$index:0,$elem:b,tp$iternext:function(){return f.$index>=f.$elem.length?void 0:f.$elem[f.$index++]}};return f}),d.Counter.prototype.most_common=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen(\"most_common\",arguments.length,1,2);var c=a.mp$length();if(void 0===b)b=c;else{if(!Sk.builtin.checkInt(b))if(b instanceof Sk.builtin.float_)throw new Sk.builtin.TypeError(\"integer argument expected, got float\");else throw new Sk.builtin.TypeError(\"an integer is required\");b=Sk.builtin.asnum$(b),b=b<=c?b:c,b=0<=b?b:0}for(var d=[],e=a.tp$iter(),f=e.tp$iternext();void 0!==f;f=e.tp$iternext())d.push([f,a.mp$subscript(f)]);d=d.sort(function(c,a){return c[1].v<a[1].v?1:c[1].v>a[1].v?-1:0});for(var g=[],h=0;h<b;h++)g.push(new Sk.builtin.tuple(d.shift()));return new Sk.builtin.list(g)}),d.Counter.prototype.update=new Sk.builtin.func(function(a,b){if(Sk.builtin.pyCheckArgsLen(\"update\",arguments.length,1,2),b instanceof Sk.builtin.dict)for(var c,d=b.tp$iter(),e=d.tp$iternext();void 0!==e;e=d.tp$iternext())c=a.mp$subscript(e),a.mp$ass_subscript(e,c.nb$add(b.mp$subscript(e)));else if(void 0!==b){if(!Sk.builtin.checkIterable(b))throw new Sk.builtin.TypeError(\"'\"+Sk.abstr.typeName(b)+\"' object is not iterable\");for(var c,f=new Sk.builtin.int_(1),d=b.tp$iter(),e=d.tp$iternext();void 0!==e;e=d.tp$iternext())c=a.mp$subscript(e),a.mp$ass_subscript(e,c.nb$add(f))}}),d.Counter.prototype.subtract=new Sk.builtin.func(function(a,b){if(Sk.builtin.pyCheckArgsLen(\"subtract\",arguments.length,1,2),b instanceof Sk.builtin.dict)for(var c,d=b.tp$iter(),e=d.tp$iternext();void 0!==e;e=d.tp$iternext())c=a.mp$subscript(e),a.mp$ass_subscript(e,c.nb$subtract(b.mp$subscript(e)));else if(void 0!==b){if(!Sk.builtin.checkIterable(b))throw new Sk.builtin.TypeError(\"'\"+Sk.abstr.typeName(b)+\"' object is not iterable\");for(var c,f=new Sk.builtin.int_(1),d=b.tp$iter(),e=d.tp$iternext();void 0!==e;e=d.tp$iternext())c=a.mp$subscript(e),a.mp$ass_subscript(e,c.nb$subtract(f))}}),d.OrderedDict=function OrderedDict(a){return this instanceof d.OrderedDict?(this.orderedkeys=[],Sk.abstr.superConstructor(d.OrderedDict,this,a),this):new d.OrderedDict(a)},Sk.abstr.setUpInheritance(\"OrderedDict\",d.OrderedDict,Sk.builtin.dict),d.OrderedDict.prototype.$r=function(){var a,b,c,d,e=[];for(b=this.tp$iter(),c=b.tp$iternext();void 0!==c;c=b.tp$iternext())a=this.mp$subscript(c),void 0===a&&(a=null),e.push(\"(\"+Sk.misceval.objectRepr(c).v+\", \"+Sk.misceval.objectRepr(a).v+\")\");return d=e.join(\", \"),0<e.length&&(d=\"[\"+d+\"]\"),new Sk.builtin.str(\"OrderedDict(\"+d+\")\")},d.OrderedDict.prototype.mp$ass_subscript=function(a,b){var c=this.orderedkeys.indexOf(a);return-1==c&&this.orderedkeys.push(a),Sk.builtin.dict.prototype.mp$ass_subscript.call(this,a,b)},d.OrderedDict.prototype.mp$del_subscript=function(a){var b=this.orderedkeys.indexOf(a);return-1!=b&&this.orderedkeys.splice(b,1),Sk.builtin.dict.prototype.mp$del_subscript.call(this,a)},d.OrderedDict.prototype.__iter__=new Sk.builtin.func(function(a){return Sk.builtin.pyCheckArgsLen(\"__iter__\",arguments.length,0,0,!1,!0),d.OrderedDict.prototype.tp$iter.call(a)}),d.OrderedDict.prototype.tp$iter=function(){var a;return a={tp$iter:function(){return a},$obj:this,$index:0,$keys:this.orderedkeys.slice(0),tp$iternext:function(){return a.$index>=a.$keys.length?void 0:a.$keys[a.$index++]}},a},d.OrderedDict.prototype.ob$eq=function(a){var b,c,e,f,g;if(!(a instanceof d.OrderedDict))return Sk.builtin.dict.prototype.ob$eq.call(this,a);if(b=this.mp$length(),c=a.mp$length(),b!==c)return Sk.builtin.bool.false$;for(e=this.tp$iter(),otheriter=a.tp$iter(),f=e.tp$iternext(),otherk=otheriter.tp$iternext();void 0!==f;f=e.tp$iternext(),otherk=otheriter.tp$iternext()){if(!Sk.misceval.isTrue(Sk.misceval.richCompareBool(f,otherk,\"Eq\")))return Sk.builtin.bool.false$;if(g=this.mp$subscript(f),otherv=a.mp$subscript(otherk),!Sk.misceval.isTrue(Sk.misceval.richCompareBool(g,otherv,\"Eq\")))return Sk.builtin.bool.false$}return Sk.builtin.bool.true$},d.OrderedDict.prototype.ob$ne=function(a){var b,c,e,f,g;if(!(a instanceof d.OrderedDict))return Sk.builtin.dict.prototype.ob$ne.call(this,a);if(b=this.size,c=a.size,b!==c)return Sk.builtin.bool.true$;for(e=this.tp$iter(),otheriter=a.tp$iter(),f=e.tp$iternext(),otherk=otheriter.tp$iternext();void 0!==f;f=e.tp$iternext(),otherk=otheriter.tp$iternext()){if(!Sk.misceval.isTrue(Sk.misceval.richCompareBool(f,otherk,\"Eq\")))return Sk.builtin.bool.true$;if(g=this.mp$subscript(f),otherv=a.mp$subscript(otherk),!Sk.misceval.isTrue(Sk.misceval.richCompareBool(g,otherv,\"Eq\")))return Sk.builtin.bool.true$}return Sk.builtin.bool.false$},d.OrderedDict.prototype.pop=new Sk.builtin.func(function(a,b,c){var d;return Sk.builtin.pyCheckArgsLen(\"pop\",arguments.length,2,3),d=a.orderedkeys.indexOf(b),-1!=d&&a.orderedkeys.splice(d,1),Sk.misceval.callsimArray(Sk.builtin.dict.prototype.pop,[a,b,c])}),d.OrderedDict.prototype.popitem=new Sk.builtin.func(function(a,b){var c,d,e;if(Sk.builtin.pyCheckArgsLen(\"popitem\",arguments.length,1,2),0==a.orderedkeys.length)throw e=new Sk.builtin.str(\"dictionary is empty\"),new Sk.builtin.KeyError(e.v);return c=a.orderedkeys[0],(void 0===b||Sk.misceval.isTrue(b))&&(c=a.orderedkeys[a.orderedkeys.length-1]),d=Sk.misceval.callsimArray(a.pop,[a,c]),new Sk.builtin.tuple([c,d])}),d.deque=function(a,b){if(!(this instanceof d.deque))return new d.deque(a,b);if(this.$d?this.$d.maxlen=b?b:Sk.builtin.none.none$:this.$d={maxlen:b?b:Sk.builtin.none.none$},this.head=0,this.tail=0,this.mask=1,b&&!Sk.builtin.checkNone(b))if(b=Sk.builtin.asnum$(b),!c(b))throw new Sk.builtin.TypeError(\"an integer is required\");else if(0>b)throw new Sk.builtin.ValueError(\"maxlen must be non-negative\");else this.maxlen=b;if(void 0===a)this.v=[,,];else if(Sk.builtin.checkIterable(a))this.v=[,,],d.deque.prototype.extend.func_code(this,a);else throw new Sk.builtin.TypeError(\"'\"+Sk.abstr.typeName(a)+\"' object is not iterable\");return this.__class__=d.deque,this},d.deque.minArgs=1,d.deque.maxArgs=2,d.deque.co_varnames=[\"iterable\",\"maxlen\"],d.deque.co_name=new Sk.builtin.str(\"mod.deque\"),d.deque.co_argcount=2,d.deque.$defaults=[new Sk.builtin.tuple([]),Sk.builtin.none.none$],Sk.abstr.setUpInheritance(\"collections.deque\",d.deque,Sk.builtin.seqtype),Sk.abstr.markUnhashable(d.deque),d.deque.prototype.$init$=d.deque,d.deque.prototype.__init__=new Sk.builtin.func(function(a,b,c){a.$init$(b,c)}),d.deque.prototype.$resize=function(a,b){var c=this.head,d=this.mask;if(this.head=0,this.tail=a,this.mask=b-1,0===c)return void(this.v.length=b);const e=Array(b);for(let f=0;f<a;f++)e[f]=this.v[c+f&d];this.v=e},d.deque.prototype.$push=function(a){this.v[this.tail]=a,this.tail=this.tail+1&this.mask,this.head===this.tail&&this.$resize(this.v.length,this.v.length<<1);var b=this.tail-this.head&this.mask;return void 0!==this.maxlen&&b>this.maxlen&&d.deque.prototype.popleft.func_code(this),this},d.deque.prototype.$pushLeft=function(a){this.head=this.head-1&this.mask,this.v[this.head]=a,this.head===this.tail&&this.$resize(this.v.length,this.v.length<<1);var b=this.tail-this.head&this.mask;return void 0!==this.maxlen&&b>this.maxlen&&d.deque.prototype.pop.func_code(this),this},d.deque.prototype.append=new Sk.builtin.func(function(a,b){return Sk.builtin.pyCheckArgsLen(\"append\",arguments.length,1,1,!0,!1),a.$push(b),Sk.builtin.none.none$}),d.deque.prototype.appendleft=new Sk.builtin.func(function(a,b){return Sk.builtin.pyCheckArgsLen(\"appendleft\",arguments.length,1,1,!0,!1),a.$pushLeft(b),Sk.builtin.none.none$}),d.deque.prototype.extend=new Sk.builtin.func(function(a,b){if(Sk.builtin.pyCheckArgsLen(\"extend\",arguments.length,1,1,!0,!1),!Sk.builtin.checkIterable(b))throw new Sk.builtin.TypeError(\"'\"+Sk.abstr.typeName(b)+\"' object is not iterable\");for(it=Sk.abstr.iter(b),i=it.tp$iternext();void 0!==i;i=it.tp$iternext())a.$push(i);return Sk.builtin.none.none$}),d.deque.prototype.extendleft=new Sk.builtin.func(function(a,b){if(Sk.builtin.pyCheckArgsLen(\"extendleft\",arguments.length,1,1,!0,!1),!Sk.builtin.checkIterable(b))throw new Sk.builtin.TypeError(\"'\"+Sk.abstr.typeName(b)+\"' object is not iterable\");for(it=Sk.abstr.iter(b),i=it.tp$iternext();void 0!==i;i=it.tp$iternext())a.$pushLeft(i);return Sk.builtin.none.none$}),d.deque.prototype.clear=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen(\"clear\",arguments.length,0,0,!0,!1),a.head=0,a.tail=0,a.mask=1,a.maxlen=void 0,a.v=[,,]}),d.deque.prototype.insert=new Sk.builtin.func(function(a,b,d){if(Sk.builtin.pyCheckArgsLen(\"insert\",arguments.length,2,2,!0,!1),index=Sk.builtin.asnum$(b),!c(index))throw new Sk.builtin.TypeError(\"integer argument expected, got \"+Sk.abstr.typeName(b));var e=a.tail-a.head&a.mask;if(void 0!==a.maxlen&&e>=a.maxlen)throw new Sk.builtin.IndexError(\"deque already at its maximum size\");index>e&&(index=e),index<=-e&&(index=0);const f=(0<=index?a.head:a.tail)+index&a.mask;var g=a.tail;for(a.tail=a.tail+1&a.mask;g!==f;){const b=g-1&a.mask;a.v[g]=a.v[b],g=b}return a.v[f]=d,a.head===a.tail&&a.$resize(a.v.length,a.v.length<<1),Sk.builtin.none.none$}),d.deque.prototype.index=new Sk.builtin.func(function(a,b,d,e){Sk.builtin.pyCheckArgsLen(\"index\",arguments.length,1,3,!0,!1);var f=a.tail-a.head&a.mask;if(!d)var d=0;else if(d=Sk.builtin.asnum$(d),!c(d))throw new Sk.builtin.TypeError(\"slice indices must be integers or have an __index__ method\");if(!e)var e=f;else if(e=Sk.builtin.asnum$(e),!c(e))throw new Sk.builtin.TypeError(\"slice indices must be integers or have an __index__ method\");var g=a.head,h=a.mask,k=a.v;const l=0<=d?d:d<-f?0:f+d;e=0<=e?e:e<-f?0:f+e;for(var m=l;m<e;m++)if(k[g+m&h]===b)return m;throw new Sk.builtin.ValueError(Sk.ffi.remapToJs(b)+\" is not in deque\")}),d.deque.prototype.__delitem__=new Sk.builtin.func(function(a,b){var d=a.tail-a.head&a.mask;if(index=Sk.builtin.asnum$(b),!c(index))throw new Sk.builtin.TypeError(\"'\"+Sk.abstr.typeName(b)+\"' object cannot be interpreted as an integer\");if((0|index)!==index||index>=d||index<-d)throw new Sk.builtin.IndexError(\"deque index out of range\");const e=(0<=index?a.head:a.tail)+index&a.mask;for(var f=e;f!==a.tail;){const b=f+1&a.mask;a.v[f]=a.v[b],f=b}return a.tail=a.tail-1&a.mask,d<a.mask>>>1&&a.$resize(d,a.v.length>>>1),Sk.builtin.none.none$}),d.deque.prototype.mp$del_subscript=function(a){d.deque.prototype.__delitem__.func_code(this,a)},d.deque.prototype.count=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen(\"count\",arguments.length,1,1,!0,!1);var c=a.head,d=a.tail-a.head&a.mask,e=a.mask,f=a.v,g=0;for(var h=0;h<d;h++)f[c+h&e]==b&&g++;return new Sk.builtin.int_(g)}),d.deque.prototype.pop=new Sk.builtin.func(function(a){if(Sk.builtin.pyCheckArgsLen(\"pop\",arguments.length,0,0,!0,!1),a.head===a.tail)throw new Sk.builtin.IndexError(\"pop from an empty deque\");a.tail=a.tail-1&a.mask;const b=a.v[a.tail];a.v[a.tail]=void 0;var c=a.tail-a.head&a.mask;return c<a.mask>>>1&&a.$resize(c,a.v.length>>>1),b}),d.deque.prototype.popleft=new Sk.builtin.func(function(a){if(Sk.builtin.pyCheckArgsLen(\"popleft\",arguments.length,0,0,!0,!1),a.head===a.tail)throw new Sk.builtin.IndexError(\"pop from an empty deque\");const b=a.v[a.head];a.v[a.head]=void 0,a.head=a.head+1&a.mask;var c=a.tail-a.head&a.mask;return c<a.mask>>>1&&a.$resize(c,a.v.length>>>1),b}),d.deque.prototype.__iter__=new Sk.builtin.func(function(a){return Sk.builtin.pyCheckArgsLen(\"__iter__\",arguments.length,0,0,!0,!1),new d.deque.deque_iter_(a)}),d.deque.prototype.mp$subscript=function(a){if(index=Sk.builtin.asnum$(a),!c(index))throw new Sk.builtin.TypeError(\"sequence index must be integer, not '\"+Sk.abstr.typeName(a)+\"'\");var b=this.tail-this.head&this.mask;if((0|index)!==index||index>=b||index<-b)throw new Sk.builtin.IndexError(\"deque index out of range\");const d=(0<=index?this.head:this.tail)+index&this.mask;return this.v[d]},d.deque.prototype.mp$ass_subscript=function(a,b){if(index=Sk.builtin.asnum$(a),!c(index))throw new Sk.builtin.TypeError(\"sequence index must be integer, not '\"+Sk.abstr.typeName(a)+\"'\");var d=this.tail-this.head&this.mask;if((0|index)!==index||index>=d||index<-d)throw new Sk.builtin.IndexError(\"deque index out of range\");const e=(0<=index?this.head:this.tail)+index&this.mask;this.v[e]=b},d.deque.prototype.__reversed__=new Sk.builtin.func(function(a){var b=new d.deque(a);return d.deque.prototype.reverse.func_code(b),new d._deque_reverse_iterator(b)}),d.deque.prototype.tp$setattr=function(a){if(!(Sk.ffi.remapToJs(a)in this.$d))throw new Sk.builtin.AttributeError(\"'collections.deque' object has no attribute '\"+Sk.ffi.remapToJs(a)+\"'\");throw new Sk.builtin.AttributeError(\"attribute '\"+Sk.ffi.remapToJs(a)+\"' of 'collections.deque' objects is not writable\")},d.deque.__class__=d.deque,d.deque.deque_iter_=function(a){if(!(this instanceof d.deque.deque_iter_))return new d.deque.deque_iter_(a);this.$index=0,this.dq=a.v,this.sq$length=a.tail-a.head&a.mask,this.tp$iter=()=>this,this.$head=a.head,this.$tail=a.tail,this.$mask=a.mask;var b;return this.tp$iternext=function(){if(!(this.$index>=this.sq$length))return b=(0<=this.$index?this.$head:this.$tail)+this.$index&this.$mask,this.$index++,this.dq[b]},this.$r=function(){return new Sk.builtin.str(\"_collections._deque_iterator\")},this},Sk.abstr.setUpInheritance(\"_collections._deque_iterator\",d.deque.deque_iter_,Sk.builtin.object),d.deque.deque_iter_.prototype.__class__=d.deque.deque__iter_,d.deque.deque_iter_.prototype.__iter__=new Sk.builtin.func(function(a){return a}),d.deque.deque_iter_.prototype.next$=function(a){var b=a.tp$iternext();if(void 0===b)throw new Sk.builtin.StopIteration;return b},d.deque.prototype.tp$iter=function(){return new d.deque.deque_iter_(this)},d.deque.prototype.remove=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen(\"remove\",arguments.length,1,1,!0,!1);var c=d.deque.prototype.index.func_code(a,b);const e=a.head+c&a.mask;for(var f=e;f!==a.tail;){const b=f+1&a.mask;a.v[f]=a.v[b],f=b}a.tail=a.tail-1&a.mask;var g=a.tail-a.head&a.mask;g<a.mask>>>1&&a.$resize(g,a.v.length>>>1)}),d.deque.prototype.__add__=new Sk.builtin.func(function(a,b){if(\"collections.deque\"!=Sk.abstr.typeName(b))throw new Sk.builtin.TypeError(\"can only concatenate deque (not \\\"\"+Sk.abstr.typeName(b)+\"\\\") to deque\");for(var c=d.deque(a,a.maxlen),e=b.tp$iter(),f=e.tp$iternext();void 0!==f;f=e.tp$iternext())c.$push(f);return c}),d.deque.prototype.__iadd__=new Sk.builtin.func(function(a,b){if(!Sk.builtin.checkIterable(b))throw new Sk.builtin.TypeError(\"'\"+Sk.abstr.typeName(b)+\"' object is not iterable\");for(it=Sk.abstr.iter(b),i=it.tp$iternext();void 0!==i;i=it.tp$iternext())a.$push(i);return a}),d.deque.prototype.sq$repeat=function(a){var e;if(n=Sk.builtin.asnum$(a),!c(n))throw new Sk.builtin.TypeError(\"can't multiply sequence by non-int of type '\"+Sk.abstr.typeName(a)+\"'\");if(n>b)throw new Sk.builtin.OverflowError(\"Python int too large to convert to ssize_t\");e=[];var f,g=this.tail-this.head&this.mask;for(i=0;i<n;++i)for(j=0;j<g;++j)f=this.head+j&this.mask,e.push(this.v[f]);return void 0===this.maxlen?new d.deque(Sk.builtin.list(e)):new d.deque(Sk.builtin.list(e),Sk.builtin.int_(this.maxlen))},d.deque.prototype.reverse=new Sk.builtin.func(function(c){Sk.builtin.pyCheckArgsLen(\"reverse\",arguments.length,0,0,!0,!1);for(var d=c.head,e=c.tail,f=c.mask,g=c.tail-c.head&c.mask,h=0;h<~~(g/2);h++){const g=e-h-1&f,a=d+h&f,b=c.v[g];c.v[g]=c.v[a],c.v[a]=b}return Sk.builtin.none.none$}),d.deque.prototype.rotate=new Sk.builtin.func(function(d,e=Sk.builtin.int_(1)){if(Sk.builtin.pyCheckArgsLen(\"rotate\",arguments.length,0,1,!0,!1),n=Sk.builtin.asnum$(e),!c(n))throw new Sk.builtin.TypeError(\"'\"+Sk.abstr.typeName(e)+\"' object cannot be interpreted as an integer\");if(n>b)throw new Sk.builtin.OverflowError(\"Python int too large to convert to ssize_t\");var f=d.head,g=d.tail;if(0===n||f===g)return d;if(d.head=f-n&d.mask,d.tail=g-n&d.mask,0<n)for(var h=1;h<=n;h++){const c=f-h&d.mask,a=g-h&d.mask;d.v[c]=d.v[a],d.v[a]=void 0}else for(let c=0;c>n;c--){const e=g-c&d.mask,a=f-c&d.mask;d.v[e]=d.v[a],d.v[a]=void 0}return Sk.builtin.none.none$}),d.deque.prototype.sq$length=function(){return this.tail-this.head&this.mask},d.deque.prototype.sq$contains=function(a){var b,c;for(b=this.tp$iter(),c=b.tp$iternext();void 0!==c;c=b.tp$iternext())if(Sk.misceval.richCompareBool(c,a,\"Eq\"))return!0;return!1},d.deque.prototype.__contains__=new Sk.builtin.func(function(a,b){return Sk.builtin.pyCheckArgsLen(\"__contains__\",arguments.length-1,1,1),new Sk.builtin.bool(a.sq$contains(b))}),d.deque.prototype.$r=function(){for(var a=[],b=this.tail-this.head&this.mask,c=0;c<b;c++)this.v[this.head+c&this.mask]&&(this.v[this.head+c&this.mask]==this?a.push(\"[...]\"):a.push(Sk.misceval.objectRepr(this.v[this.head+c&this.mask]).v));return void 0===this.maxlen?new Sk.builtin.str(\"deque([\"+a.filter(Boolean).join(\", \")+\"])\"):new Sk.builtin.str(\"deque([\"+a.filter(Boolean).join(\", \")+\"], maxlen=\"+this.maxlen+\")\")},d.deque.prototype.__repr__=new Sk.builtin.func(function(a){return Sk.builtin.pyCheckArgsLen(\"__repr__\",arguments.length,0,0,!1,!0),d.deque.prototype.$r.call(a)}),d.deque.prototype.tp$richcompare=function(a,b){var c,e,f,g,h;if(this===a&&Sk.misceval.opAllowsEquality(b))return!0;if(!a.__class__||a.__class__!=d.deque)return\"Eq\"!==b&&\"NotEq\"===b;var l=a;for(h=this.v,a=a.v,g=this.tail-this.head&this.mask,f=l.tail-l.head&l.mask,e=0;e<g&&e<f&&(c=Sk.misceval.richCompareBool(h[this.head+e&this.mask],a[l.head+e&l.mask],\"Eq\"),!!c);++e);if(e>=g||e>=f)switch(b){case\"Lt\":return g<f;case\"LtE\":return g<=f;case\"Eq\":return g===f;case\"NotEq\":return g!==f;case\"Gt\":return g>f;case\"GtE\":return g>=f;default:Sk.asserts.fail();}return\"Eq\"!==b&&(\"NotEq\"===b||Sk.misceval.richCompareBool(h[this.head+e&this.mask],a[l.head+e&l.mask],b))},d._deque_reverse_iterator=function(a){this.v=a},Sk.abstr.setUpInheritance(\"_collections._deque_reverse_iterator\",d._deque_reverse_iterator,Sk.builtin.seqtype),d._deque_reverse_iterator._deque_reverse_iterator_iter_=function(a){if(!(this instanceof d._deque_reverse_iterator._deque_reverse_iterator_iter_))return new d._deque_reverse_iterator._deque_reverse_iterator_iter_(a);this.$index=0,this.dq=a.v.v,this.sq$length=this.dq.length,this.tp$iter=()=>this;var b;return this.tp$iternext=function(){if(!(this.$index>=this.sq$length))return b=(0<=this.$index?a.v.head:a.v.tail)+this.$index&a.v.mask,this.$index++,this.dq[b]},this.$r=function(){return new Sk.builtin.str(\"_collections._deque_reverse_iterator_iter_\")},this},d._deque_reverse_iterator.prototype.tp$iter=function(){return new d._deque_reverse_iterator._deque_reverse_iterator_iter_(this)},Sk.abstr.setUpInheritance(\"_deque_reverse_iterator_iterator\",d._deque_reverse_iterator._deque_reverse_iterator_iter_,Sk.builtin.object),d._deque_reverse_iterator._deque_reverse_iterator_iter_.prototype.__class__=d._deque_reverse_iterator._deque_reverse_iterator_iter_,d._deque_reverse_iterator._deque_reverse_iterator_iter_.prototype.__iter__=new Sk.builtin.func(function(a){return a}),d._deque_reverse_iterator._deque_reverse_iterator_iter_.prototype.next$=function(a){var b=a.tp$iternext();if(void 0===b)throw new Sk.builtin.StopIteration;return b},d._deque_reverse_iterator.prototype.$r=function(){return Sk.builtin.str(\"<_collections._deque_reverse_iterator object>\")},d.namedtuples={};var e=function(a,b){function tempCtor(){}tempCtor.prototype=b.prototype,a.superClass_=b.prototype,a.prototype=new tempCtor,a.prototype.constructor=a},f=function(b,c,f,g,h){if(Sk.ffi.remapToJs(Sk.misceval.callsimArray(a.$d.iskeyword,[b])))throw new Sk.builtin.ValueError(\"Type names and field names cannot be a keyword: '\"+b.v+\"'\");const k=b.$jsstr(),l=new RegExp(/^[0-9].*/),m=new RegExp(/^[0-9_].*/),o=new RegExp(/^\\w*$/);if(l.test(k)||!o.test(k)||!k)throw new Sk.builtin.ValueError(\"Type names and field names must be valid identifiers: '\"+k+\"'\");let p;if(!Sk.builtin.checkIterable(c))throw new Sk.builtin.TypeError(\"'\"+Sk.abstr.typeName(c)+\"' object is not iterable\");if(Sk.builtin.checkString(c))p=c.$jsstr(),p=p.replace(/,/g,\" \").split(/\\s+/),1==p.length&&\"\"===p[0]&&(p=[]);else for(p=[],iter=Sk.abstr.iter(c),i=iter.tp$iternext();void 0!==i;i=iter.tp$iternext())p.push(Sk.ffi.remapToJs(i));if(f=Sk.misceval.isTrue(Sk.builtin.bool(f)),f){let b=new Set;for(i=0;i<p.length;i++)(Sk.ffi.remapToJs(Sk.misceval.callsimArray(a.$d.iskeyword,[Sk.ffi.remapToPy(p[i])]))||m.test(p[i])||!o.test(p[i])||!p[i]||b.has(p[i]))&&(p[i]=\"_\"+i),b.add(p[i])}for(i=0;i<p.length;i++)if(Sk.ffi.remapToJs(Sk.misceval.callsimArray(a.$d.iskeyword,[Sk.ffi.remapToPy(p[i])])))throw new Sk.builtin.ValueError(\"Type names and field names cannot be a keyword: '\"+p[i]+\"'\");else if((m.test(p[i])||!p[i])&&!f)throw new Sk.builtin.ValueError(\"Field names cannot start with an underscore: '\"+p[i]+\"'\");else if(!o.test(p[i]))throw new Sk.builtin.ValueError(\"Type names and field names must be valid identifiers: '\"+p[i]+\"'\");let q=new Set;for(i=0;i<p.length;i++){if(q.has(p[i]))throw new Sk.builtin.ValueError(\"Encountered duplicate field name: '\"+p[i]+\"'\");q.add(p[i])}const r=[];if(!Sk.builtin.checkNone(g)){if(!Sk.builtin.checkIterable(g))throw new Sk.builtin.TypeError(\"'\"+Sk.abstr.typeName(g)+\"' object is not iterable\");g=Sk.abstr.iter(g);for(let a=g.tp$iternext();void 0!==a;a=g.tp$iternext())r.push(a)}if(r.length>p.length)throw new Sk.builtin.TypeError(\"Got more default values than field names\");var s=function nametuple_constructor(...a){return Sk.builtin.pyCheckArgsLen(\"__new__\",arguments.length,p.length,p.length),this instanceof d.namedtuples[k]?void(this.__class__=d.namedtuples[k],this.v=a):new d.namedtuples[k](...a)};s.co_name=new Sk.builtin.str(\"__new__\"),s.co_varnames=p,s.$defaults=r;const t=function(a,...b){if(Sk.builtin.pyCheckArgsLen(\"__new__\",arguments.length,p.length+1,p.length+1),Sk.builtin.pyCheckType(\"___new___(X): X\",\"type object\",Sk.builtin.checkClass(a)),Sk.builtin.issubclass(a,Sk.builtin.tuple)){let c;try{c=new a(...b)}catch(d){d instanceof Sk.builtin.TypeError&&(c=new a(b))}return c}throw new Sk.builtin.TypeError(a.tp$name+\" is not a subtype of tuple\")};t.co_name=new Sk.builtin.str(\"__new__\"),t.co_varnames=[\"cls\"].concat(p),t.$defaults=r,__new__=function(a){Sk.builtin.func.call(this,a),this.$d.__defaults__=Sk.builtin.checkNone(g)?g:new Sk.builtin.tuple(r)},__new__.prototype=Object.create(Sk.builtin.func.prototype),s.__new__=new __new__(t);for(let a=0;a<p.length;a++)fld=Sk.fixReserved(p[a]),s[fld]={},s[fld].tp$descr_set=function(){throw new Sk.builtin.AttributeError(\"can't set attribute\")},s[fld].tp$descr_get=function(b){return b.v[a]},s[fld].$r=function(){return new Sk.builtin.str(\"<property object>\")};d.namedtuples[k]=s,s.__class__=s,e(s,Sk.builtin.tuple),s.prototype.tp$name=k,s.prototype.ob$type=Sk.builtin.type.makeIntoTypeObj(k,d.namedtuples[k]),s.prototype.$r=function(){let a,b,c;if(0===this.v.length)return new Sk.builtin.str(k+\"()\");for(c=[],b=0;b<this.v.length;++b)c[b]=p[b]+\"=\"+Sk.misceval.objectRepr(this.v[b]).v;return a=c.join(\", \"),cls=Sk.abstr.typeName(this),new Sk.builtin.str(cls+\"(\"+a+\")\")},s.prototype._fields=s._fields=new Sk.builtin.tuple(p.map(a=>Sk.builtin.str(a)));const u=function(a){if(Sk.builtin.pyCheckArgsLen(\"_make\",arguments.length,1,1),!Sk.builtin.checkIterable(a))throw new Sk.builtin.TypeError(\"'\"+Sk.abstr.typeName(a)+\"' object is not iterable\");a=Sk.abstr.iter(a),values=[];for(let b=a.tp$iternext();void 0!==b;b=a.tp$iternext())values.push(b);return s(...values)};u.co_name=new Sk.builtin.str(\"_make\"),s._make=new Sk.builtin.func(u);const v=function(a){const b=[];for(let c=0;c<a._fields.v.length;c++)b.push(a._fields.v[c]),b.push(a.v[c]);return new Sk.builtin.dict(b)};v.co_name=new Sk.builtin.str(\"_asdict\"),s.prototype._asdict=new Sk.builtin.func(v);const w=[];for(let a=p.length-r.length;a<p.length;a++)w.push(Sk.builtin.str(p[a])),w.push(r[a-(p.length-r.length)]);s.prototype._field_defaults=s._field_defaults=new Sk.builtin.dict(w);const x=function(a,b){const c={};for(let d=0;d<a.length;d+=2)c[a[d].$jsstr()]=a[d+1];const d=[];for(let e=0;e<p.length;e++){const a=p[e],f=a in c?c[a]:b.v[e];d.push(f),delete c[a]}for(let d in c){const a=Object.keys(c).map(a=>\"'\"+a+\"'\");throw new Sk.builtin.ValueError(\"Got unexpectd field names: [\"+a+\"]\")}return s(...d)};x.co_name=new Sk.builtin.str(\"replace\"),x.co_kwargs=1,x.co_varnames=[\"_self\"],s.prototype._replace=new Sk.builtin.func(x),Sk.builtin.checkNone(h)&&(h=Sk.globals.__name__),s.__module__=h,s.__doc__=new Sk.builtin.str(k+\"(\"+p.join(\", \")+\")\"),s.__slots__=new Sk.builtin.tuple([]),s.prototype.__getnewargs__=new Sk.builtin.func(function(a){return new Sk.builtin.tuple(a.v)});const y=new Sk.builtin.tuple([s,Sk.builtin.tuple,Sk.builtin.object]);s.tp$mro=y;let z=[\"_make\",s._make,\"__bases__\",new Sk.builtin.tuple([Sk.builtin.tuple]),\"__mro__\",new Sk.builtin.tuple([s,Sk.builtin.tuple,Sk.builtin.object]),\"__new__\",s.__new__,\"__name__\",b];return z=z.map(a=>\"string\"==typeof a?Sk.builtin.str(a):a),s.$d=new Sk.builtin.dict(z),s};return f.co_name=new Sk.builtin.str(\"namedtuple\"),f.co_argcount=2,f.co_kwonlyargcount=3,f.$kwdefs=[Sk.builtin.bool.false$,Sk.builtin.none.none$,Sk.builtin.none.none$],f.co_varnames=[\"typename\",\"field_names\",\"rename\",\"defaults\",\"module\"],d.namedtuple=new Sk.builtin.func(f),d})};","src/lib/colorsys.py":"raise NotImplementedError(\"colorsys is not yet implemented in Skulpt\")\n","src/lib/commands.py":"raise NotImplementedError(\"commands is not yet implemented in Skulpt\")\n","src/lib/compileall.py":"raise NotImplementedError(\"compileall is not yet implemented in Skulpt\")\n","src/lib/compiler/__init__.py":"raise NotImplementedError(\"compiler is not yet implemented in Skulpt\")\n","src/lib/config/__init__.py":"raise NotImplementedError(\"config is not yet implemented in Skulpt\")\n","src/lib/contextlib.py":"raise NotImplementedError(\"contextlib is not yet implemented in Skulpt\")\n","src/lib/cookielib.py":"raise NotImplementedError(\"cookielib is not yet implemented in Skulpt\")\n","src/lib/copy.py":"\"\"\"\nThis file was modified from CPython.\nCopyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,\n2011, 2012, 2013, 2014, 2015 Python Software Foundation; All Rights Reserved\n\"\"\"\nimport types\nclass Error(Exception):\n    pass\nerror = Error \nclass _EmptyClass:\n    pass\n\ndef copy(x):\n    cls = type(x)\n    if callable(x):\n        return x\n    copier = getattr(cls, \"__copy__\", None)\n    if copier:\n        return copier(x)\n    if cls in (type(None), int, float, bool, long, str, tuple, type):\n        return x\n    if (cls == list) or (cls == dict) or (cls == set) or (cls == slice):\n        return cls(x)\n    try:\n        getstate = getattr(x, \"__getstate__\", None)\n        setstate = getattr(x, \"__setstate__\", None)\n        initargs = getattr(x, \"__getinitargs__\", None)\n    except:\n        reductor = False\n    if getstate or setstate or initargs:\n        raise NotImplementedError(\"Skulpt does not yet support copying with user-defined __getstate__, __setstate__ or __getinitargs__()\")\n    reductor = getattr(x, \"__reduce_ex__\", None)\n    if reductor:\n        rv = reductor(4)\n    else:\n        reductor = getattr(x, \"__reduce__\", None)\n        if reductor:\n            rv = reductor()\n        elif str(cls)[1:6] == \"class\":\n            copier = _copy_inst\n            return copier(x)\n        else:\n            raise Error(\"un(shallow)copyable object of type %s\" % cls)\n    if isinstance(rv, str):\n        return x\n    return _reconstruct(x, rv, 0)\n\ndef _copy_inst(x):\n    if hasattr(x, '__copy__'):\n        return x.__copy__()\n    if hasattr(x, '__getinitargs__'):\n        args = x.__getinitargs__()\n        y = x.__class__(*args)\n    else:\n        y = _EmptyClass()\n        y.__class__ = x.__class__\n    if hasattr(x, '__getstate__'):\n        state = x.__getstate__()\n    else:\n        state = x.__dict__\n    if hasattr(y, '__setstate__'):\n        y.__setstate__(state)\n    else:\n        y.__dict__.update(state)\n    return y\n\nd = _deepcopy_dispatch = {}\n\ndef deepcopy(x, memo=None, _nil=[]):\n    \"\"\"Deep copy operation on arbitrary Python objects.\n    See the module's __doc__ string for more info.\n    \"\"\"\n    if memo is None:\n        memo = {}\n    idx = id(x)\n    y = memo.get(idx, _nil)\n    if y is not _nil:\n        return y\n    cls = type(x)\n    try:\n        getstate = getattr(x, \"__getstate__\", None)\n        setstate = getattr(x, \"__setstate__\", None)\n        initargs = getattr(x, \"__getinitargs__\", None)\n    except:\n        reductor = False\n    if getstate or setstate or initargs:\n        raise NotImplementedError(\"Skulpt does not yet support copying with user-defined __getstate__, __setstate__ or __getinitargs__()\")\n    copier = _deepcopy_dispatch.get(cls)\n    if copier:\n        y = copier(x, memo)\n    elif str(cls)[1:6] == \"class\":\n        copier = _deepcopy_dispatch[\"InstanceType\"]\n        y = copier(x, memo)\n    else:\n        try:\n            issc = issubclass(cls, type)\n        except TypeError: # cls is not a class (old Boost; see SF #502085)\n            issc = 0\n        if issc:\n            y = _deepcopy_atomic(x, memo)\n        else:\n            copier = getattr(x, \"__deepcopy__\", None)\n            if copier:\n                y = copier(memo)\n            else:\n                reductor = getattr(x, \"__reduce_ex__\", None)\n                if reductor:\n                    rv = reductor(2)\n                else:\n                    reductor = getattr(x, \"__reduce__\", None)\n                    if reductor:\n                        rv = reductor()\n                    else:\n                        raise Error(\n                            \"un(deep)copyable object of type %s\" % cls)\n                y = _reconstruct(x, rv, 1, memo)\n    memo[idx] = y\n    _keep_alive(x, memo) # Make sure x lives at least as long as d\n    return y\n\ndef _deepcopy_atomic(x, memo):\n    return x\nd[type(None)] = _deepcopy_atomic\n# d[type(Ellipsis)] = _deepcopy_atomic\nd[type(NotImplemented)] = _deepcopy_atomic\nd[int] = _deepcopy_atomic\nd[float] = _deepcopy_atomic\nd[bool] = _deepcopy_atomic\nd[complex] = _deepcopy_atomic\n# d[bytes] = _deepcopy_atomic\nd[str] = _deepcopy_atomic\n# try:\n# d[types.CodeType] = _deepcopy_atomic\n# except AttributeError:\n#   pass\nd[type] = _deepcopy_atomic\n# d[types.BuiltinFunctionType] = _deepcopy_atomic\nd[types.FunctionType] = _deepcopy_atomic\n# d[weakref.ref] = _deepcopy_atomic\n\ndef _deepcopy_list(x, memo):\n    y = []\n    memo[id(x)] = y\n    for a in x:\n        y.append(deepcopy(a, memo))\n    return y\nd[list] = _deepcopy_list\n\ndef _deepcopy_set(x, memo):\n    result = set([])  # make empty set\n    memo[id(x)] = result  # register this set in the memo for loop checking\n    for a in x:   # go through elements of set\n        result.add(deepcopy(a, memo))  # add the copied elements into the new set\n    return result # return the new set\nd[set] = _deepcopy_set\n\ndef _deepcopy_tuple(x, memo):\n    y = [deepcopy(a, memo) for a in x]\n    # We're not going to put the tuple in the memo, but it's still important we\n    # check for it, in case the tuple contains recursive mutable structures.\n    try:\n        return memo[id(x)]\n    except KeyError:\n        pass\n    for k, j in zip(x, y):\n        if k is not j:\n            y = tuple(y)\n            break\n    else:\n        y = x\n    return y\nd[tuple] = _deepcopy_tuple\n\ndef _deepcopy_dict(x, memo):\n    y = {}\n    memo[id(x)] = y\n    for key, value in x.items():\n        y[deepcopy(key, memo)] = deepcopy(value, memo)\n    return y\nd[dict] = _deepcopy_dict\n\n# def _deepcopy_method(x, memo): # Copy instance methods\n#     y = type(x)(x.im_func, deepcopy(x.im_self, memo), x.im_class);\n#     return y\nd[types.MethodType] = _deepcopy_atomic\n\ndef _deepcopy_inst(x, memo):\n    if hasattr(x, '__deepcopy__'):\n         return x.__deepcopy__(memo)\n    if hasattr(x, '__getinitargs__'):\n        args = x.__getinitargs__()\n        args = deepcopy(args, memo)\n        y = x.__class__(*args)\n    else:\n        y = _EmptyClass()\n        y.__class__ = x.__class__\n    memo[id(x)] = y\n    if hasattr(x, '__getstate__'):\n        state = x.__getstate__()\n    else:\n        state = x.__dict__\n    state = deepcopy(state, memo)\n    if hasattr(y, '__setstate__'):\n        y.__setstate__(state)\n    else:\n        y.__dict__.update(state)\n        return y\nd[\"InstanceType\"] = _deepcopy_inst\n\ndef _keep_alive(x, memo):\n    \"\"\"Keeps a reference to the object x in the memo.\n    Because we remember objects by their id, we have\n    to assure that possibly temporary objects are kept\n    alive by referencing them.\n    We store a reference at the id of the memo, which should\n    normally not be used unless someone tries to deepcopy\n    the memo itself...\n    \"\"\"\n    try:\n        memo[id(memo)].append(x)\n    except KeyError:\n        # aha, this is the first one :-)\n        memo[id(memo)]=[x]\n\ndef _reconstruct(x, info, deep, memo=None):\n    if isinstance(info, str):\n        return x\n    assert isinstance(info, tuple)\n    if memo is None:\n        memo = {}\n    n = len(info)\n    assert n in (2, 3, 4, 5)\n    callable, args = info[:2]\n    if n > 2:\n        state = info[2]\n    else:\n        state = None\n    if n > 3:\n        listiter = info[3]\n    else:\n        listiter = None\n    if n > 4:\n        dictiter = info[4]\n    else:\n        dictiter = None\n    if deep:\n        args = deepcopy(args, memo)\n    y = callable(*args)\n    memo[id(x)] = y\n\n    if state is not None:\n        if deep:\n            state = deepcopy(state, memo)\n        if hasattr(y, '__setstate__'):\n            y.__setstate__(state)\n        else:\n            if isinstance(state, tuple) and len(state) == 2:\n                state, slotstate = state\n            else:\n                slotstate = None\n            if state is not None:\n                y.__dict__.update(state)\n            if slotstate is not None:\n                for key, value in slotstate.items():\n                    setattr(y, key, value)\n\n    if listiter is not None:\n        for item in listiter:\n            if deep:\n                item = deepcopy(item, memo)\n            y.append(item)\n    if dictiter is not None:\n        for key, value in dictiter:\n            if deep:\n                key = deepcopy(key, memo)\n                value = deepcopy(value, memo)\n            y[key] = value\n    return y\n\ndel d\n\ndel types\n\n# Helper for instance creation without calling __init__\nclass _EmptyClass:\n    pass","src/lib/copy_reg.py":"raise NotImplementedError(\"copy_reg is not yet implemented in Skulpt\")\n","src/lib/csv.py":"raise NotImplementedError(\"csv is not yet implemented in Skulpt\")\n","src/lib/ctypes/__init__.py":"raise NotImplementedError(\"ctypes is not yet implemented in Skulpt\")\n","src/lib/ctypes/macholib/__init__.py":"raise NotImplementedError(\"macholib is not yet implemented in Skulpt\")\n","src/lib/curses/__init__.py":"raise NotImplementedError(\"curses is not yet implemented in Skulpt\")\n","src/lib/datetime.py":"\"\"\"Concrete date/time and related types -- prototype implemented in Python.\n\nSee http://www.zope.org/Members/fdrake/DateTimeWiki/FrontPage\n\nSee also http://dir.yahoo.com/Reference/calendars/\n\nFor a primer on DST, including many current DST rules, see\nhttp://webexhibits.org/daylightsaving/\n\nFor more about DST than you ever wanted to know, see\nftp://elsie.nci.nih.gov/pub/\n\nSources for time zone and DST data: http://www.twinsun.com/tz/tz-link.htm\n\nThis was originally copied from the sandbox of the CPython CVS repository.\nThanks to Tim Peters for suggesting using it.\n\nThis was then copied from PyPy v5.1.0 into Skulpt by Meredydd Luff, removing\n'from __future__ import division' (and replacing division operators accordingly)\nand pickle support (which requires 'struct', which Skulpt does not currently\n[as of 31/8/2016] have)\n\"\"\"\n\nimport time as _time\nimport math as _math\n\n# Python 2-vs-3 compat hack\nimport sys\nunicode = unicode if sys.version_info < (3,) else str\n\n_SENTINEL = object()\n\ndef _cmp(x, y):\n    return 0 if x == y else 1 if x > y else -1\n\ndef _round(x):\n    return int(_math.floor(x + 0.5) if x >= 0.0 else _math.ceil(x - 0.5))\n\nMINYEAR = 1\nMAXYEAR = 9999\n_MINYEARFMT = 1900\n\n_MAX_DELTA_DAYS = 999999999\n\n# Utility functions, adapted from Python's Demo/classes/Dates.py, which\n# also assumes the current Gregorian calendar indefinitely extended in\n# both directions.  Difference:  Dates.py calls January 1 of year 0 day\n# number 1.  The code here calls January 1 of year 1 day number 1.  This is\n# to match the definition of the \"proleptic Gregorian\" calendar in Dershowitz\n# and Reingold's \"Calendrical Calculations\", where it's the base calendar\n# for all computations.  See the book for algorithms for converting between\n# proleptic Gregorian ordinals and many other calendar systems.\n\n_DAYS_IN_MONTH = [-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]\n\n_DAYS_BEFORE_MONTH = [-1]\ndbm = 0\nfor dim in _DAYS_IN_MONTH[1:]:\n    _DAYS_BEFORE_MONTH.append(dbm)\n    dbm += dim\ndel dbm, dim\n\ndef _is_leap(year):\n    \"year -> 1 if leap year, else 0.\"\n    return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)\n\ndef _days_before_year(year):\n    \"year -> number of days before January 1st of year.\"\n    y = year - 1\n    return y*365 + y//4 - y//100 + y//400\n\ndef _days_in_month(year, month):\n    \"year, month -> number of days in that month in that year.\"\n    assert 1 <= month <= 12, month\n    if month == 2 and _is_leap(year):\n        return 29\n    return _DAYS_IN_MONTH[month]\n\ndef _days_before_month(year, month):\n    \"year, month -> number of days in year preceding first day of month.\"\n    assert 1 <= month <= 12, 'month must be in 1..12'\n    return _DAYS_BEFORE_MONTH[month] + (month > 2 and _is_leap(year))\n\ndef _ymd2ord(year, month, day):\n    \"year, month, day -> ordinal, considering 01-Jan-0001 as day 1.\"\n    assert 1 <= month <= 12, 'month must be in 1..12'\n    dim = _days_in_month(year, month)\n    assert 1 <= day <= dim, ('day must be in 1..%d' % dim)\n    return (_days_before_year(year) +\n            _days_before_month(year, month) +\n            day)\n\n_DI400Y = _days_before_year(401)    # number of days in 400 years\n_DI100Y = _days_before_year(101)    #    \"    \"   \"   \" 100   \"\n_DI4Y   = _days_before_year(5)      #    \"    \"   \"   \"   4   \"\n\n# A 4-year cycle has an extra leap day over what we'd get from pasting\n# together 4 single years.\nassert _DI4Y == 4 * 365 + 1\n\n# Similarly, a 400-year cycle has an extra leap day over what we'd get from\n# pasting together 4 100-year cycles.\nassert _DI400Y == 4 * _DI100Y + 1\n\n# OTOH, a 100-year cycle has one fewer leap day than we'd get from\n# pasting together 25 4-year cycles.\nassert _DI100Y == 25 * _DI4Y - 1\n\n_US_PER_US = 1\n_US_PER_MS = 1000\n_US_PER_SECOND = 1000000\n_US_PER_MINUTE = 60000000\n_SECONDS_PER_DAY = 24 * 3600\n_US_PER_HOUR = 3600000000\n_US_PER_DAY = 86400000000\n_US_PER_WEEK = 604800000000\n\ndef _ord2ymd(n):\n    \"ordinal -> (year, month, day), considering 01-Jan-0001 as day 1.\"\n\n    # n is a 1-based index, starting at 1-Jan-1.  The pattern of leap years\n    # repeats exactly every 400 years.  The basic strategy is to find the\n    # closest 400-year boundary at or before n, then work with the offset\n    # from that boundary to n.  Life is much clearer if we subtract 1 from\n    # n first -- then the values of n at 400-year boundaries are exactly\n    # those divisible by _DI400Y:\n    #\n    #     D  M   Y            n              n-1\n    #     -- --- ----        ----------     ----------------\n    #     31 Dec -400        -_DI400Y       -_DI400Y -1\n    #      1 Jan -399         -_DI400Y +1   -_DI400Y      400-year boundary\n    #     ...\n    #     30 Dec  000        -1             -2\n    #     31 Dec  000         0             -1\n    #      1 Jan  001         1              0            400-year boundary\n    #      2 Jan  001         2              1\n    #      3 Jan  001         3              2\n    #     ...\n    #     31 Dec  400         _DI400Y        _DI400Y -1\n    #      1 Jan  401         _DI400Y +1     _DI400Y      400-year boundary\n    n -= 1\n    n400, n = divmod(n, _DI400Y)\n    year = n400 * 400 + 1   # ..., -399, 1, 401, ...\n\n    # Now n is the (non-negative) offset, in days, from January 1 of year, to\n    # the desired date.  Now compute how many 100-year cycles precede n.\n    # Note that it's possible for n100 to equal 4!  In that case 4 full\n    # 100-year cycles precede the desired day, which implies the desired\n    # day is December 31 at the end of a 400-year cycle.\n    n100, n = divmod(n, _DI100Y)\n\n    # Now compute how many 4-year cycles precede it.\n    n4, n = divmod(n, _DI4Y)\n\n    # And now how many single years.  Again n1 can be 4, and again meaning\n    # that the desired day is December 31 at the end of the 4-year cycle.\n    n1, n = divmod(n, 365)\n\n    year += n100 * 100 + n4 * 4 + n1\n    if n1 == 4 or n100 == 4:\n        assert n == 0\n        return year-1, 12, 31\n\n    # Now the year is correct, and n is the offset from January 1.  We find\n    # the month via an estimate that's either exact or one too large.\n    leapyear = n1 == 3 and (n4 != 24 or n100 == 3)\n    assert leapyear == _is_leap(year)\n    month = (n + 50) >> 5\n    preceding = _DAYS_BEFORE_MONTH[month] + (month > 2 and leapyear)\n    if preceding > n:  # estimate is too large\n        month -= 1\n        preceding -= _DAYS_IN_MONTH[month] + (month == 2 and leapyear)\n    n -= preceding\n    assert 0 <= n < _days_in_month(year, month)\n\n    # Now the year and month are correct, and n is the offset from the\n    # start of that month:  we're done!\n    return year, month, n+1\n\n# Month and day names.  For localized versions, see the calendar module.\n_MONTHNAMES = [None, \"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\",\n                     \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\n_DAYNAMES = [None, \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\"]\n\n\ndef _build_struct_time(y, m, d, hh, mm, ss, dstflag):\n    wday = (_ymd2ord(y, m, d) + 6) % 7\n    dnum = _days_before_month(y, m) + d\n    return _time.struct_time((y, m, d, hh, mm, ss, wday, dnum, dstflag))\n\ndef _format_time(hh, mm, ss, us):\n    # Skip trailing microseconds when us==0.\n    result = \"%02d:%02d:%02d\" % (hh, mm, ss)\n    if us:\n        result += \".%06d\" % us\n    return result\n\n# Correctly substitute for %z and %Z escapes in strftime formats.\ndef _wrap_strftime(object, format, timetuple):\n    year = timetuple[0]\n    if year < _MINYEARFMT:\n        raise ValueError(\"year=%d is before %d; the datetime strftime() \"\n                         \"methods require year >= %d\" %\n                         (year, _MINYEARFMT, _MINYEARFMT))\n    # Don't call utcoffset() or tzname() unless actually needed.\n    freplace = None  # the string to use for %f\n    zreplace = None  # the string to use for %z\n    Zreplace = None  # the string to use for %Z\n\n    # Scan format for %z and %Z escapes, replacing as needed.\n    newformat = []\n    push = newformat.append\n    i, n = 0, len(format)\n    while i < n:\n        ch = format[i]\n        i += 1\n        if ch == '%':\n            if i < n:\n                ch = format[i]\n                i += 1\n                if ch == 'f':\n                    if freplace is None:\n                        freplace = '%06d' % getattr(object,\n                                                    'microsecond', 0)\n                    newformat.append(freplace)\n                elif ch == 'z':\n                    if zreplace is None:\n                        zreplace = \"\"\n                        if hasattr(object, \"_utcoffset\"):\n                            offset = object._utcoffset()\n                            if offset is not None:\n                                sign = '+'\n                                if offset < 0:\n                                    offset = -offset\n                                    sign = '-'\n                                h, m = divmod(offset, 60)\n                                zreplace = '%c%02d%02d' % (sign, h, m)\n                    assert '%' not in zreplace\n                    newformat.append(zreplace)\n                elif ch == 'Z':\n                    if Zreplace is None:\n                        Zreplace = \"\"\n                        if hasattr(object, \"tzname\"):\n                            s = object.tzname()\n                            if s is not None:\n                                # strftime is going to have at this: escape %\n                                Zreplace = s.replace('%', '%%')\n                    newformat.append(Zreplace)\n                else:\n                    push('%')\n                    push(ch)\n            else:\n                push('%')\n        else:\n            push(ch)\n    newformat = \"\".join(newformat)\n    return _time.strftime(newformat, timetuple)\n\n# Just raise TypeError if the arg isn't None or a string.\ndef _check_tzname(name):\n    if name is not None and not isinstance(name, str):\n        raise TypeError(\"tzinfo.tzname() must return None or string, \"\n                        \"not '%s'\" % type(name))\n\n# name is the offset-producing method, \"utcoffset\" or \"dst\".\n# offset is what it returned.\n# If offset isn't None or timedelta, raises TypeError.\n# If offset is None, returns None.\n# Else offset is checked for being in range, and a whole # of minutes.\n# If it is, its integer value is returned.  Else ValueError is raised.\ndef _check_utc_offset(name, offset):\n    assert name in (\"utcoffset\", \"dst\")\n    if offset is None:\n        return\n    if not isinstance(offset, timedelta):\n        raise TypeError(\"tzinfo.%s() must return None \"\n                        \"or timedelta, not '%s'\" % (name, type(offset)))\n    days = offset.days\n    if days < -1 or days > 0:\n        offset = 1440  # trigger out-of-range\n    else:\n        seconds = days * 86400 + offset.seconds\n        minutes, seconds = divmod(seconds, 60)\n        if seconds or offset.microseconds:\n            raise ValueError(\"tzinfo.%s() must return a whole number \"\n                             \"of minutes\" % name)\n        offset = minutes\n    if not -1440 < offset < 1440:\n        raise ValueError(\"%s()=%d, must be in -1439..1439\" % (name, offset))\n    return offset\n\ndef _check_int_field(value):\n    if isinstance(value, int):\n        return int(value)\n    if not isinstance(value, float):\n        try:\n            value = value.__int__()\n        except AttributeError:\n            pass\n        else:\n            if isinstance(value, int):\n                return int(value)\n            elif isinstance(value, long):\n                return int(long(value))\n            raise TypeError('__int__ method should return an integer')\n        raise TypeError('an integer is required')\n    raise TypeError('integer argument expected, got float')\n\ndef _check_date_fields(year, month, day):\n    year = _check_int_field(year)\n    month = _check_int_field(month)\n    day = _check_int_field(day)\n    if not MINYEAR <= year <= MAXYEAR:\n        raise ValueError('year must be in %d..%d' % (MINYEAR, MAXYEAR), year)\n    if not 1 <= month <= 12:\n        raise ValueError('month must be in 1..12', month)\n    dim = _days_in_month(year, month)\n    if not 1 <= day <= dim:\n        raise ValueError('day must be in 1..%d' % dim, day)\n    return year, month, day\n\ndef _check_time_fields(hour, minute, second, microsecond):\n    hour = _check_int_field(hour)\n    minute = _check_int_field(minute)\n    second = _check_int_field(second)\n    microsecond = _check_int_field(microsecond)\n    if not 0 <= hour <= 23:\n        raise ValueError('hour must be in 0..23', hour)\n    if not 0 <= minute <= 59:\n        raise ValueError('minute must be in 0..59', minute)\n    if not 0 <= second <= 59:\n        raise ValueError('second must be in 0..59', second)\n    if not 0 <= microsecond <= 999999:\n        raise ValueError('microsecond must be in 0..999999', microsecond)\n    return hour, minute, second, microsecond\n\ndef _check_tzinfo_arg(tz):\n    if tz is not None and not isinstance(tz, tzinfo):\n        raise TypeError(\"tzinfo argument must be None or of a tzinfo subclass\")\n\n\n# Notes on comparison:  In general, datetime module comparison operators raise\n# TypeError when they don't know how to do a comparison themself.  If they\n# returned NotImplemented instead, comparison could (silently) fall back to\n# the default compare-objects-by-comparing-their-memory-addresses strategy,\n# and that's not helpful.  There are two exceptions:\n#\n# 1. For date and datetime, if the other object has a \"timetuple\" attr,\n#    NotImplemented is returned.  This is a hook to allow other kinds of\n#    datetime-like objects a chance to intercept the comparison.\n#\n# 2. Else __eq__ and __ne__ return False and True, respectively.  This is\n#    so opertaions like\n#\n#        x == y\n#        x != y\n#        x in sequence\n#        x not in sequence\n#        dict[x] = y\n#\n#    don't raise annoying TypeErrors just because a datetime object\n#    is part of a heterogeneous collection.  If there's no known way to\n#    compare X to a datetime, saying they're not equal is reasonable.\n\ndef _cmperror(x, y):\n    raise TypeError(\"can't compare '%s' to '%s'\" % (\n                    type(x).__name__, type(y).__name__))\n\ndef _normalize_pair(hi, lo, factor):\n    if not 0 <= lo <= factor-1:\n        inc, lo = divmod(lo, factor)\n        hi += inc\n    return hi, lo\n\ndef _normalize_datetime(y, m, d, hh, mm, ss, us, ignore_overflow=False):\n    # Normalize all the inputs, and store the normalized values.\n    ss, us = _normalize_pair(ss, us, 1000000)\n    mm, ss = _normalize_pair(mm, ss, 60)\n    hh, mm = _normalize_pair(hh, mm, 60)\n    d, hh = _normalize_pair(d, hh, 24)\n    y, m, d = _normalize_date(y, m, d, ignore_overflow)\n    return y, m, d, hh, mm, ss, us\n\ndef _normalize_date(year, month, day, ignore_overflow=False):\n    # That was easy.  Now it gets muddy:  the proper range for day\n    # can't be determined without knowing the correct month and year,\n    # but if day is, e.g., plus or minus a million, the current month\n    # and year values make no sense (and may also be out of bounds\n    # themselves).\n    # Saying 12 months == 1 year should be non-controversial.\n    if not 1 <= month <= 12:\n        year, month = _normalize_pair(year, month-1, 12)\n        month += 1\n        assert 1 <= month <= 12\n\n    # Now only day can be out of bounds (year may also be out of bounds\n    # for a datetime object, but we don't care about that here).\n    # If day is out of bounds, what to do is arguable, but at least the\n    # method here is principled and explainable.\n    dim = _days_in_month(year, month)\n    if not 1 <= day <= dim:\n        # Move day-1 days from the first of the month.  First try to\n        # get off cheap if we're only one day out of range (adjustments\n        # for timezone alone can't be worse than that).\n        if day == 0:    # move back a day\n            month -= 1\n            if month > 0:\n                day = _days_in_month(year, month)\n            else:\n                year, month, day = year-1, 12, 31\n        elif day == dim + 1:    # move forward a day\n            month += 1\n            day = 1\n            if month > 12:\n                month = 1\n                year += 1\n        else:\n            ordinal = _ymd2ord(year, month, 1) + (day - 1)\n            year, month, day = _ord2ymd(ordinal)\n\n    if not ignore_overflow and not MINYEAR <= year <= MAXYEAR:\n        raise OverflowError(\"date value out of range\")\n    return year, month, day\n\ndef _accum(tag, sofar, num, factor, leftover):\n    if isinstance(num, (int, long)):\n        prod = num * factor\n        rsum = sofar + prod\n        return rsum, leftover\n    if isinstance(num, float):\n        fracpart, intpart = _math.modf(num)\n        prod = int(intpart) * factor\n        rsum = sofar + prod\n        if fracpart == 0.0:\n            return rsum, leftover\n        assert isinstance(factor, (int, long))\n        fracpart, intpart = _math.modf(factor * fracpart)\n        rsum += int(intpart)\n        return rsum, leftover + fracpart\n    raise TypeError(\"unsupported type for timedelta %s component: %s\" %\n                    (tag, type(num)))\n\nclass timedelta(object):\n    \"\"\"Represent the difference between two datetime objects.\n\n    Supported operators:\n\n    - add, subtract timedelta\n    - unary plus, minus, abs\n    - compare to timedelta\n    - multiply, divide by int/long\n\n    In addition, datetime supports subtraction of two datetime objects\n    returning a timedelta, and addition or subtraction of a datetime\n    and a timedelta giving a datetime.\n\n    Representation: (days, seconds, microseconds).  Why?  Because I\n    felt like it.\n    \"\"\"\n    __slots__ = '_days', '_seconds', '_microseconds', '_hashcode'\n\n    def __new__(cls, days=_SENTINEL, seconds=_SENTINEL, microseconds=_SENTINEL,\n                milliseconds=_SENTINEL, minutes=_SENTINEL, hours=_SENTINEL, weeks=_SENTINEL):\n        x = 0\n        leftover = 0.0\n        if microseconds is not _SENTINEL:\n            x, leftover = _accum(\"microseconds\", x, microseconds, _US_PER_US, leftover)\n        if milliseconds is not _SENTINEL:\n            x, leftover = _accum(\"milliseconds\", x, milliseconds, _US_PER_MS, leftover)\n        if seconds is not _SENTINEL:\n            x, leftover = _accum(\"seconds\", x, seconds, _US_PER_SECOND, leftover)\n        if minutes is not _SENTINEL:\n            x, leftover = _accum(\"minutes\", x, minutes, _US_PER_MINUTE, leftover)\n        if hours is not _SENTINEL:\n            x, leftover = _accum(\"hours\", x, hours, _US_PER_HOUR, leftover)\n        if days is not _SENTINEL:\n            x, leftover = _accum(\"days\", x, days, _US_PER_DAY, leftover)\n        if weeks is not _SENTINEL:\n            x, leftover = _accum(\"weeks\", x, weeks, _US_PER_WEEK, leftover)\n        if leftover != 0.0:\n            x += _round(leftover)\n        return cls._from_microseconds(x)\n\n    @classmethod\n    def _from_microseconds(cls, us):\n        s, us = divmod(us, _US_PER_SECOND)\n        d, s = divmod(s, _SECONDS_PER_DAY)\n        return cls._create(d, s, us, False)\n\n    @classmethod\n    def _create(cls, d, s, us, normalize):\n        if normalize:\n            s, us = _normalize_pair(s, us, 1000000)\n            d, s = _normalize_pair(d, s, 24*3600)\n\n        if not -_MAX_DELTA_DAYS <= d <= _MAX_DELTA_DAYS:\n            raise OverflowError(\"days=%d; must have magnitude <= %d\" % (d, _MAX_DELTA_DAYS))\n\n        self = object.__new__(cls)\n        self._days = d\n        self._seconds = s\n        self._microseconds = us\n        self._hashcode = -1\n        return self\n\n    def _to_microseconds(self):\n        return ((self._days * _SECONDS_PER_DAY + self._seconds) * _US_PER_SECOND +\n                self._microseconds)\n\n    def __repr__(self):\n        module = \"datetime.\" if self.__class__ is timedelta else \"\"\n        if self._microseconds:\n            return \"%s(%d, %d, %d)\" % (module + self.__class__.__name__,\n                                       self._days,\n                                       self._seconds,\n                                       self._microseconds)\n        if self._seconds:\n            return \"%s(%d, %d)\" % (module + self.__class__.__name__,\n                                   self._days,\n                                   self._seconds)\n        return \"%s(%d)\" % (module + self.__class__.__name__, self._days)\n\n    def __str__(self):\n        mm, ss = divmod(self._seconds, 60)\n        hh, mm = divmod(mm, 60)\n        s = \"%d:%02d:%02d\" % (hh, mm, ss)\n        if self._days:\n            def plural(n):\n                return n, abs(n) != 1 and \"s\" or \"\"\n            s = (\"%d day%s, \" % plural(self._days)) + s\n        if self._microseconds:\n            s = s + \".%06d\" % self._microseconds\n        return s\n\n    def total_seconds(self):\n        \"\"\"Total seconds in the duration.\"\"\"\n        return self._to_microseconds() / 10.0**6\n\n    # Read-only field accessors\n    @property\n    def days(self):\n        \"\"\"days\"\"\"\n        return self._days\n\n    @property\n    def seconds(self):\n        \"\"\"seconds\"\"\"\n        return self._seconds\n\n    @property\n    def microseconds(self):\n        \"\"\"microseconds\"\"\"\n        return self._microseconds\n\n    def __add__(self, other):\n        if isinstance(other, timedelta):\n            # for CPython compatibility, we cannot use\n            # our __class__ here, but need a real timedelta\n            return timedelta._create(self._days + other._days,\n                                     self._seconds + other._seconds,\n                                     self._microseconds + other._microseconds,\n                                     True)\n        return NotImplemented\n\n    def __sub__(self, other):\n        if isinstance(other, timedelta):\n            # for CPython compatibility, we cannot use\n            # our __class__ here, but need a real timedelta\n            return timedelta._create(self._days - other._days,\n                                     self._seconds - other._seconds,\n                                     self._microseconds - other._microseconds,\n                                     True)\n        return NotImplemented\n\n    def __neg__(self):\n        # for CPython compatibility, we cannot use\n        # our __class__ here, but need a real timedelta\n        return timedelta._create(-self._days,\n                                 -self._seconds,\n                                 -self._microseconds,\n                                 True)\n\n    def __pos__(self):\n        # for CPython compatibility, we cannot use\n        # our __class__ here, but need a real timedelta\n        return timedelta._create(self._days,\n                                 self._seconds,\n                                 self._microseconds,\n                                 False)\n\n    def __abs__(self):\n        if self._days < 0:\n            return -self\n        else:\n            return self\n\n    def __mul__(self, other):\n        if not isinstance(other, (int, long)):\n            return NotImplemented\n        usec = self._to_microseconds()\n        return timedelta._from_microseconds(usec * other)\n\n    __rmul__ = __mul__\n\n    def __div__(self, other):\n        if not isinstance(other, (int, long)):\n            return NotImplemented\n        usec = self._to_microseconds()\n        return timedelta._from_microseconds(usec // other)\n\n    __floordiv__ = __div__\n\n    # Comparisons of timedelta objects with other.\n\n    def __eq__(self, other):\n        if isinstance(other, timedelta):\n            return self._cmp(other) == 0\n        else:\n            return False\n\n    def __ne__(self, other):\n        if isinstance(other, timedelta):\n            return self._cmp(other) != 0\n        else:\n            return True\n\n    def __le__(self, other):\n        if isinstance(other, timedelta):\n            return self._cmp(other) <= 0\n        else:\n            _cmperror(self, other)\n\n    def __lt__(self, other):\n        if isinstance(other, timedelta):\n            return self._cmp(other) < 0\n        else:\n            _cmperror(self, other)\n\n    def __ge__(self, other):\n        if isinstance(other, timedelta):\n            return self._cmp(other) >= 0\n        else:\n            _cmperror(self, other)\n\n    def __gt__(self, other):\n        if isinstance(other, timedelta):\n            return self._cmp(other) > 0\n        else:\n            _cmperror(self, other)\n\n    def _cmp(self, other):\n        assert isinstance(other, timedelta)\n        return _cmp(self._getstate(), other._getstate())\n\n    def __hash__(self):\n        if self._hashcode == -1:\n            self._hashcode = hash(self._getstate())\n        return self._hashcode\n\n    def __nonzero__(self):\n        return (self._days != 0 or\n                self._seconds != 0 or\n                self._microseconds != 0)\n\ntimedelta.min = timedelta(-_MAX_DELTA_DAYS)\ntimedelta.max = timedelta(_MAX_DELTA_DAYS, 24*3600-1, 1000000-1)\ntimedelta.resolution = timedelta(microseconds=1)\n\nclass date(object):\n    \"\"\"Concrete date type.\n\n    Constructors:\n\n    __new__()\n    fromtimestamp()\n    today()\n    fromordinal()\n\n    Operators:\n\n    __repr__, __str__\n    __cmp__, __hash__\n    __add__, __radd__, __sub__ (add/radd only with timedelta arg)\n\n    Methods:\n\n    timetuple()\n    toordinal()\n    weekday()\n    isoweekday(), isocalendar(), isoformat()\n    ctime()\n    strftime()\n\n    Properties (readonly):\n    year, month, day\n    \"\"\"\n    __slots__ = '_year', '_month', '_day', '_hashcode'\n\n    def __new__(cls, year, month=None, day=None):\n        \"\"\"Constructor.\n\n        Arguments:\n\n        year, month, day (required, base 1)\n        \"\"\"\n        year, month, day = _check_date_fields(year, month, day)\n        self = object.__new__(cls)\n        self._year = year\n        self._month = month\n        self._day = day\n        self._hashcode = -1\n        return self\n\n    # Additional constructors\n\n    @classmethod\n    def fromtimestamp(cls, t):\n        \"Construct a date from a POSIX timestamp (like time.time()).\"\n        y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)\n        return cls(y, m, d)\n\n    @classmethod\n    def today(cls):\n        \"Construct a date from time.time().\"\n        t = _time.time()\n        return cls.fromtimestamp(t)\n\n    @classmethod\n    def fromordinal(cls, n):\n        \"\"\"Contruct a date from a proleptic Gregorian ordinal.\n\n        January 1 of year 1 is day 1.  Only the year, month and day are\n        non-zero in the result.\n        \"\"\"\n        y, m, d = _ord2ymd(n)\n        return cls(y, m, d)\n\n    # Conversions to string\n\n    def __repr__(self):\n        \"\"\"Convert to formal string, for repr().\n\n        >>> dt = datetime(2010, 1, 1)\n        >>> repr(dt)\n        'datetime.datetime(2010, 1, 1, 0, 0)'\n\n        >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)\n        >>> repr(dt)\n        'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'\n        \"\"\"\n        module = \"datetime.\" if self.__class__ is date else \"\"\n        return \"%s(%d, %d, %d)\" % (module + self.__class__.__name__,\n                                   self._year,\n                                   self._month,\n                                   self._day)\n\n    # XXX These shouldn't depend on time.localtime(), because that\n    # clips the usable dates to [1970 .. 2038).  At least ctime() is\n    # easily done without using strftime() -- that's better too because\n    # strftime(\"%c\", ...) is locale specific.\n\n    def ctime(self):\n        \"Return ctime() style string.\"\n        weekday = self.toordinal() % 7 or 7\n        return \"%s %s %2d 00:00:00 %04d\" % (\n            _DAYNAMES[weekday],\n            _MONTHNAMES[self._month],\n            self._day, self._year)\n\n    def strftime(self, format):\n        \"Format using strftime().\"\n        return _wrap_strftime(self, format, self.timetuple())\n\n    def __format__(self, fmt):\n        if not isinstance(fmt, (str, unicode)):\n            raise ValueError(\"__format__ expects str or unicode, not %s\" %\n                             fmt.__class__.__name__)\n        if len(fmt) != 0:\n            return self.strftime(fmt)\n        return str(self)\n\n    def isoformat(self):\n        \"\"\"Return the date formatted according to ISO.\n\n        This is 'YYYY-MM-DD'.\n\n        References:\n        - http://www.w3.org/TR/NOTE-datetime\n        - http://www.cl.cam.ac.uk/~mgk25/iso-time.html\n        \"\"\"\n        return \"%04d-%02d-%02d\" % (self._year, self._month, self._day)\n\n    __str__ = isoformat\n\n    # Read-only field accessors\n    @property\n    def year(self):\n        \"\"\"year (1-9999)\"\"\"\n        return self._year\n\n    @property\n    def month(self):\n        \"\"\"month (1-12)\"\"\"\n        return self._month\n\n    @property\n    def day(self):\n        \"\"\"day (1-31)\"\"\"\n        return self._day\n\n    # Standard conversions, __cmp__, __hash__ (and helpers)\n\n    def timetuple(self):\n        \"Return local time tuple compatible with time.localtime().\"\n        return _build_struct_time(self._year, self._month, self._day,\n                                  0, 0, 0, -1)\n\n    def toordinal(self):\n        \"\"\"Return proleptic Gregorian ordinal for the year, month and day.\n\n        January 1 of year 1 is day 1.  Only the year, month and day values\n        contribute to the result.\n        \"\"\"\n        return _ymd2ord(self._year, self._month, self._day)\n\n    def replace(self, year=None, month=None, day=None):\n        \"\"\"Return a new date with new values for the specified fields.\"\"\"\n        if year is None:\n            year = self._year\n        if month is None:\n            month = self._month\n        if day is None:\n            day = self._day\n        return date(year, month, day)\n\n    # Comparisons of date objects with other.\n\n    def __eq__(self, other):\n        if isinstance(other, date):\n            return self._cmp(other) == 0\n        elif hasattr(other, \"timetuple\"):\n            return NotImplemented\n        else:\n            return False\n\n    def __ne__(self, other):\n        if isinstance(other, date):\n            return self._cmp(other) != 0\n        elif hasattr(other, \"timetuple\"):\n            return NotImplemented\n        else:\n            return True\n\n    def __le__(self, other):\n        if isinstance(other, date):\n            return self._cmp(other) <= 0\n        elif hasattr(other, \"timetuple\"):\n            return NotImplemented\n        else:\n            _cmperror(self, other)\n\n    def __lt__(self, other):\n        if isinstance(other, date):\n            return self._cmp(other) < 0\n        elif hasattr(other, \"timetuple\"):\n            return NotImplemented\n        else:\n            _cmperror(self, other)\n\n    def __ge__(self, other):\n        if isinstance(other, date):\n            return self._cmp(other) >= 0\n        elif hasattr(other, \"timetuple\"):\n            return NotImplemented\n        else:\n            _cmperror(self, other)\n\n    def __gt__(self, other):\n        if isinstance(other, date):\n            return self._cmp(other) > 0\n        elif hasattr(other, \"timetuple\"):\n            return NotImplemented\n        else:\n            _cmperror(self, other)\n\n    def _cmp(self, other):\n        assert isinstance(other, date)\n        y, m, d = self._year, self._month, self._day\n        y2, m2, d2 = other._year, other._month, other._day\n        return _cmp((y, m, d), (y2, m2, d2))\n\n    def __hash__(self):\n        \"Hash.\"\n        if self._hashcode == -1:\n            self._hashcode = hash(self._getstate())\n        return self._hashcode\n\n    # Computations\n\n    def _add_timedelta(self, other, factor):\n        y, m, d = _normalize_date(\n            self._year,\n            self._month,\n            self._day + other.days * factor)\n        return date(y, m, d)\n\n    def __add__(self, other):\n        \"Add a date to a timedelta.\"\n        if isinstance(other, timedelta):\n            return self._add_timedelta(other, 1)\n        return NotImplemented\n\n    __radd__ = __add__\n\n    def __sub__(self, other):\n        \"\"\"Subtract two dates, or a date and a timedelta.\"\"\"\n        if isinstance(other, date):\n            days1 = self.toordinal()\n            days2 = other.toordinal()\n            return timedelta._create(days1 - days2, 0, 0, False)\n        if isinstance(other, timedelta):\n            return self._add_timedelta(other, -1)\n        return NotImplemented\n\n    def weekday(self):\n        \"Return day of the week, where Monday == 0 ... Sunday == 6.\"\n        return (self.toordinal() + 6) % 7\n\n    # Day-of-the-week and week-of-the-year, according to ISO\n\n    def isoweekday(self):\n        \"Return day of the week, where Monday == 1 ... Sunday == 7.\"\n        # 1-Jan-0001 is a Monday\n        return self.toordinal() % 7 or 7\n\n    def isocalendar(self):\n        \"\"\"Return a 3-tuple containing ISO year, week number, and weekday.\n\n        The first ISO week of the year is the (Mon-Sun) week\n        containing the year's first Thursday; everything else derives\n        from that.\n\n        The first week is 1; Monday is 1 ... Sunday is 7.\n\n        ISO calendar algorithm taken from\n        http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm\n        \"\"\"\n        year = self._year\n        week1monday = _isoweek1monday(year)\n        today = _ymd2ord(self._year, self._month, self._day)\n        # Internally, week and day have origin 0\n        week, day = divmod(today - week1monday, 7)\n        if week < 0:\n            year -= 1\n            week1monday = _isoweek1monday(year)\n            week, day = divmod(today - week1monday, 7)\n        elif week >= 52:\n            if today >= _isoweek1monday(year+1):\n                year += 1\n                week = 0\n        return year, week+1, day+1\n\n_date_class = date  # so functions w/ args named \"date\" can get at the class\n\ndate.min = date(1, 1, 1)\ndate.max = date(9999, 12, 31)\ndate.resolution = timedelta(days=1)\n\nclass tzinfo(object):\n    \"\"\"Abstract base class for time zone info classes.\n\n    Subclasses must override the name(), utcoffset() and dst() methods.\n    \"\"\"\n    __slots__ = ()\n\n    def tzname(self, dt):\n        \"datetime -> string name of time zone.\"\n        raise NotImplementedError(\"tzinfo subclass must override tzname()\")\n\n    def utcoffset(self, dt):\n        \"datetime -> minutes east of UTC (negative for west of UTC)\"\n        raise NotImplementedError(\"tzinfo subclass must override utcoffset()\")\n\n    def dst(self, dt):\n        \"\"\"datetime -> DST offset in minutes east of UTC.\n\n        Return 0 if DST not in effect.  utcoffset() must include the DST\n        offset.\n        \"\"\"\n        raise NotImplementedError(\"tzinfo subclass must override dst()\")\n\n    def fromutc(self, dt):\n        \"datetime in UTC -> datetime in local time.\"\n\n        if not isinstance(dt, datetime):\n            raise TypeError(\"fromutc() requires a datetime argument\")\n        if dt.tzinfo is not self:\n            raise ValueError(\"dt.tzinfo is not self\")\n\n        dtoff = dt.utcoffset()\n        if dtoff is None:\n            raise ValueError(\"fromutc() requires a non-None utcoffset() \"\n                             \"result\")\n\n        # See the long comment block at the end of this file for an\n        # explanation of this algorithm.\n        dtdst = dt.dst()\n        if dtdst is None:\n            raise ValueError(\"fromutc() requires a non-None dst() result\")\n        delta = dtoff - dtdst\n        if delta:\n            dt = dt + delta\n            dtdst = dt.dst()\n            if dtdst is None:\n                raise ValueError(\"fromutc(): dt.dst gave inconsistent \"\n                                 \"results; cannot convert\")\n        if dtdst:\n            return dt + dtdst\n        else:\n            return dt\n\n_tzinfo_class = tzinfo\n\nclass time(object):\n    \"\"\"Time with time zone.\n\n    Constructors:\n\n    __new__()\n\n    Operators:\n\n    __repr__, __str__\n    __cmp__, __hash__\n\n    Methods:\n\n    strftime()\n    isoformat()\n    utcoffset()\n    tzname()\n    dst()\n\n    Properties (readonly):\n    hour, minute, second, microsecond, tzinfo\n    \"\"\"\n    __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hashcode'\n\n    def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None):\n        \"\"\"Constructor.\n\n        Arguments:\n\n        hour, minute (required)\n        second, microsecond (default to zero)\n        tzinfo (default to None)\n        \"\"\"\n        hour, minute, second, microsecond = _check_time_fields(\n            hour, minute, second, microsecond)\n        _check_tzinfo_arg(tzinfo)\n        self = object.__new__(cls)\n        self._hour = hour\n        self._minute = minute\n        self._second = second\n        self._microsecond = microsecond\n        self._tzinfo = tzinfo\n        self._hashcode = -1\n        return self\n\n    # Read-only field accessors\n    @property\n    def hour(self):\n        \"\"\"hour (0-23)\"\"\"\n        return self._hour\n\n    @property\n    def minute(self):\n        \"\"\"minute (0-59)\"\"\"\n        return self._minute\n\n    @property\n    def second(self):\n        \"\"\"second (0-59)\"\"\"\n        return self._second\n\n    @property\n    def microsecond(self):\n        \"\"\"microsecond (0-999999)\"\"\"\n        return self._microsecond\n\n    @property\n    def tzinfo(self):\n        \"\"\"timezone info object\"\"\"\n        return self._tzinfo\n\n    # Standard conversions, __hash__ (and helpers)\n\n    # Comparisons of time objects with other.\n\n    def __eq__(self, other):\n        if isinstance(other, time):\n            return self._cmp(other) == 0\n        else:\n            return False\n\n    def __ne__(self, other):\n        if isinstance(other, time):\n            return self._cmp(other) != 0\n        else:\n            return True\n\n    def __le__(self, other):\n        if isinstance(other, time):\n            return self._cmp(other) <= 0\n        else:\n            _cmperror(self, other)\n\n    def __lt__(self, other):\n        if isinstance(other, time):\n            return self._cmp(other) < 0\n        else:\n            _cmperror(self, other)\n\n    def __ge__(self, other):\n        if isinstance(other, time):\n            return self._cmp(other) >= 0\n        else:\n            _cmperror(self, other)\n\n    def __gt__(self, other):\n        if isinstance(other, time):\n            return self._cmp(other) > 0\n        else:\n            _cmperror(self, other)\n\n    def _cmp(self, other):\n        assert isinstance(other, time)\n        mytz = self._tzinfo\n        ottz = other._tzinfo\n        myoff = otoff = None\n\n        if mytz is ottz:\n            base_compare = True\n        else:\n            myoff = self._utcoffset()\n            otoff = other._utcoffset()\n            base_compare = myoff == otoff\n\n        if base_compare:\n            return _cmp((self._hour, self._minute, self._second,\n                         self._microsecond),\n                        (other._hour, other._minute, other._second,\n                         other._microsecond))\n        if myoff is None or otoff is None:\n            raise TypeError(\"can't compare offset-naive and offset-aware times\")\n        myhhmm = self._hour * 60 + self._minute - myoff\n        othhmm = other._hour * 60 + other._minute - otoff\n        return _cmp((myhhmm, self._second, self._microsecond),\n                    (othhmm, other._second, other._microsecond))\n\n    def __hash__(self):\n        \"\"\"Hash.\"\"\"\n        if self._hashcode == -1:\n            tzoff = self._utcoffset()\n            if not tzoff:  # zero or None\n                self._hashcode = hash(self._getstate()[0])\n            else:\n                h, m = divmod(self.hour * 60 + self.minute - tzoff, 60)\n                if 0 <= h < 24:\n                    self._hashcode = hash(time(h, m, self.second, self.microsecond))\n                else:\n                    self._hashcode = hash((h, m, self.second, self.microsecond))\n        return self._hashcode\n\n    # Conversion to string\n\n    def _tzstr(self, sep=\":\"):\n        \"\"\"Return formatted timezone offset (+xx:xx) or None.\"\"\"\n        off = self._utcoffset()\n        if off is not None:\n            if off < 0:\n                sign = \"-\"\n                off = -off\n            else:\n                sign = \"+\"\n            hh, mm = divmod(off, 60)\n            assert 0 <= hh < 24\n            off = \"%s%02d%s%02d\" % (sign, hh, sep, mm)\n        return off\n\n    def __repr__(self):\n        \"\"\"Convert to formal string, for repr().\"\"\"\n        if self._microsecond != 0:\n            s = \", %d, %d\" % (self._second, self._microsecond)\n        elif self._second != 0:\n            s = \", %d\" % self._second\n        else:\n            s = \"\"\n        module = \"datetime.\" if self.__class__ is time else \"\"\n        s= \"%s(%d, %d%s)\" % (module + self.__class__.__name__,\n                             self._hour, self._minute, s)\n        if self._tzinfo is not None:\n            assert s[-1:] == \")\"\n            s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n        return s\n\n    def isoformat(self):\n        \"\"\"Return the time formatted according to ISO.\n\n        This is 'HH:MM:SS.mmmmmm+zz:zz', or 'HH:MM:SS+zz:zz' if\n        self.microsecond == 0.\n        \"\"\"\n        s = _format_time(self._hour, self._minute, self._second,\n                         self._microsecond)\n        tz = self._tzstr()\n        if tz:\n            s += tz\n        return s\n\n    __str__ = isoformat\n\n    def strftime(self, format):\n        \"\"\"Format using strftime().  The date part of the timestamp passed\n        to underlying strftime should not be used.\n        \"\"\"\n        # The year must be >= _MINYEARFMT else Python's strftime implementation\n        # can raise a bogus exception.\n        timetuple = (1900, 1, 1,\n                     self._hour, self._minute, self._second,\n                     0, 1, -1)\n        return _wrap_strftime(self, format, timetuple)\n\n    def __format__(self, fmt):\n        if not isinstance(fmt, (str, unicode)):\n            raise ValueError(\"__format__ expects str or unicode, not %s\" %\n                             fmt.__class__.__name__)\n        if len(fmt) != 0:\n            return self.strftime(fmt)\n        return str(self)\n\n    # Timezone functions\n\n    def utcoffset(self):\n        \"\"\"Return the timezone offset in minutes east of UTC (negative west of\n        UTC).\"\"\"\n        if self._tzinfo is None:\n            return None\n        offset = self._tzinfo.utcoffset(None)\n        offset = _check_utc_offset(\"utcoffset\", offset)\n        if offset is not None:\n            offset = timedelta._create(0, offset * 60, 0, True)\n        return offset\n\n    # Return an integer (or None) instead of a timedelta (or None).\n    def _utcoffset(self):\n        if self._tzinfo is None:\n            return None\n        offset = self._tzinfo.utcoffset(None)\n        offset = _check_utc_offset(\"utcoffset\", offset)\n        return offset\n\n    def tzname(self):\n        \"\"\"Return the timezone name.\n\n        Note that the name is 100% informational -- there's no requirement that\n        it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n        \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n        \"\"\"\n        if self._tzinfo is None:\n            return None\n        name = self._tzinfo.tzname(None)\n        _check_tzname(name)\n        return name\n\n    def dst(self):\n        \"\"\"Return 0 if DST is not in effect, or the DST offset (in minutes\n        eastward) if DST is in effect.\n\n        This is purely informational; the DST offset has already been added to\n        the UTC offset returned by utcoffset() if applicable, so there's no\n        need to consult dst() unless you're interested in displaying the DST\n        info.\n        \"\"\"\n        if self._tzinfo is None:\n            return None\n        offset = self._tzinfo.dst(None)\n        offset = _check_utc_offset(\"dst\", offset)\n        if offset is not None:\n            offset = timedelta._create(0, offset * 60, 0, True)\n        return offset\n\n    # Return an integer (or None) instead of a timedelta (or None).\n    def _dst(self):\n        if self._tzinfo is None:\n            return None\n        offset = self._tzinfo.dst(None)\n        offset = _check_utc_offset(\"dst\", offset)\n        return offset\n\n    def replace(self, hour=None, minute=None, second=None, microsecond=None,\n                tzinfo=True):\n        \"\"\"Return a new time with new values for the specified fields.\"\"\"\n        if hour is None:\n            hour = self.hour\n        if minute is None:\n            minute = self.minute\n        if second is None:\n            second = self.second\n        if microsecond is None:\n            microsecond = self.microsecond\n        if tzinfo is True:\n            tzinfo = self.tzinfo\n        return time(hour, minute, second, microsecond, tzinfo)\n\n    def __nonzero__(self):\n        if self.second or self.microsecond:\n            return True\n        offset = self._utcoffset() or 0\n        return self.hour * 60 + self.minute != offset\n\n_time_class = time  # so functions w/ args named \"time\" can get at the class\n\ntime.min = time(0, 0, 0)\ntime.max = time(23, 59, 59, 999999)\ntime.resolution = timedelta(microseconds=1)\n\nclass datetime(date):\n    \"\"\"datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])\n\n    The year, month and day arguments are required. tzinfo may be None, or an\n    instance of a tzinfo subclass. The remaining arguments may be ints or longs.\n    \"\"\"\n    __slots__ = date.__slots__ + time.__slots__\n\n    def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,\n                microsecond=0, tzinfo=None):\n        year, month, day = _check_date_fields(year, month, day)\n        hour, minute, second, microsecond = _check_time_fields(\n            hour, minute, second, microsecond)\n        _check_tzinfo_arg(tzinfo)\n        self = object.__new__(cls)\n        self._year = year\n        self._month = month\n        self._day = day\n        self._hour = hour\n        self._minute = minute\n        self._second = second\n        self._microsecond = microsecond\n        self._tzinfo = tzinfo\n        self._hashcode = -1\n        return self\n\n    # Read-only field accessors\n    @property\n    def hour(self):\n        \"\"\"hour (0-23)\"\"\"\n        return self._hour\n\n    @property\n    def minute(self):\n        \"\"\"minute (0-59)\"\"\"\n        return self._minute\n\n    @property\n    def second(self):\n        \"\"\"second (0-59)\"\"\"\n        return self._second\n\n    @property\n    def microsecond(self):\n        \"\"\"microsecond (0-999999)\"\"\"\n        return self._microsecond\n\n    @property\n    def tzinfo(self):\n        \"\"\"timezone info object\"\"\"\n        return self._tzinfo\n\n    @classmethod\n    def fromtimestamp(cls, timestamp, tz=None):\n        \"\"\"Construct a datetime from a POSIX timestamp (like time.time()).\n\n        A timezone info object may be passed in as well.\n        \"\"\"\n        _check_tzinfo_arg(tz)\n        converter = _time.localtime if tz is None else _time.gmtime\n        self = cls._from_timestamp(converter, timestamp, tz)\n        if tz is not None:\n            self = tz.fromutc(self)\n        return self\n\n    @classmethod\n    def utcfromtimestamp(cls, t):\n        \"Construct a UTC datetime from a POSIX timestamp (like time.time()).\"\n        return cls._from_timestamp(_time.gmtime, t, None)\n\n    @classmethod\n    def _from_timestamp(cls, converter, timestamp, tzinfo):\n        t_full = timestamp\n        timestamp = int(_math.floor(timestamp))\n        frac = t_full - timestamp\n        us = _round(frac * 1e6)\n\n        # If timestamp is less than one microsecond smaller than a\n        # full second, us can be rounded up to 1000000.  In this case,\n        # roll over to seconds, otherwise, ValueError is raised\n        # by the constructor.\n        if us == 1000000:\n            timestamp += 1\n            us = 0\n        y, m, d, hh, mm, ss, weekday, jday, dst = converter(timestamp)\n        ss = min(ss, 59)    # clamp out leap seconds if the platform has them\n        return cls(y, m, d, hh, mm, ss, us, tzinfo)\n\n    @classmethod\n    def now(cls, tz=None):\n        \"Construct a datetime from time.time() and optional time zone info.\"\n        t = _time.time()\n        return cls.fromtimestamp(t, tz)\n\n    @classmethod\n    def utcnow(cls):\n        \"Construct a UTC datetime from time.time().\"\n        t = _time.time()\n        return cls.utcfromtimestamp(t)\n\n    @classmethod\n    def combine(cls, date, time):\n        \"Construct a datetime from a given date and a given time.\"\n        if not isinstance(date, _date_class):\n            raise TypeError(\"date argument must be a date instance\")\n        if not isinstance(time, _time_class):\n            raise TypeError(\"time argument must be a time instance\")\n        return cls(date.year, date.month, date.day,\n                   time.hour, time.minute, time.second, time.microsecond,\n                   time.tzinfo)\n\n    def timetuple(self):\n        \"Return local time tuple compatible with time.localtime().\"\n        dst = self._dst()\n        if dst is None:\n            dst = -1\n        elif dst:\n            dst = 1\n        return _build_struct_time(self.year, self.month, self.day,\n                                  self.hour, self.minute, self.second,\n                                  dst)\n\n    def utctimetuple(self):\n        \"Return UTC time tuple compatible with time.gmtime().\"\n        y, m, d = self.year, self.month, self.day\n        hh, mm, ss = self.hour, self.minute, self.second\n        offset = self._utcoffset()\n        if offset:  # neither None nor 0\n            mm -= offset\n            y, m, d, hh, mm, ss, _ = _normalize_datetime(\n                y, m, d, hh, mm, ss, 0, ignore_overflow=True)\n        return _build_struct_time(y, m, d, hh, mm, ss, 0)\n\n    def date(self):\n        \"Return the date part.\"\n        return date(self._year, self._month, self._day)\n\n    def time(self):\n        \"Return the time part, with tzinfo None.\"\n        return time(self.hour, self.minute, self.second, self.microsecond)\n\n    def timetz(self):\n        \"Return the time part, with same tzinfo.\"\n        return time(self.hour, self.minute, self.second, self.microsecond,\n                    self._tzinfo)\n\n    def replace(self, year=None, month=None, day=None, hour=None,\n                minute=None, second=None, microsecond=None, tzinfo=True):\n        \"\"\"Return a new datetime with new values for the specified fields.\"\"\"\n        if year is None:\n            year = self.year\n        if month is None:\n            month = self.month\n        if day is None:\n            day = self.day\n        if hour is None:\n            hour = self.hour\n        if minute is None:\n            minute = self.minute\n        if second is None:\n            second = self.second\n        if microsecond is None:\n            microsecond = self.microsecond\n        if tzinfo is True:\n            tzinfo = self.tzinfo\n        return datetime(year, month, day, hour, minute, second, microsecond,\n                        tzinfo)\n\n    def astimezone(self, tz):\n        if not isinstance(tz, tzinfo):\n            raise TypeError(\"tz argument must be an instance of tzinfo\")\n\n        mytz = self.tzinfo\n        if mytz is None:\n            raise ValueError(\"astimezone() requires an aware datetime\")\n\n        if tz is mytz:\n            return self\n\n        # Convert self to UTC, and attach the new time zone object.\n        myoffset = self.utcoffset()\n        if myoffset is None:\n            raise ValueError(\"astimezone() requires an aware datetime\")\n        utc = (self - myoffset).replace(tzinfo=tz)\n\n        # Convert from UTC to tz's local time.\n        return tz.fromutc(utc)\n\n    # Ways to produce a string.\n\n    def ctime(self):\n        \"Return ctime() style string.\"\n        weekday = self.toordinal() % 7 or 7\n        return \"%s %s %2d %02d:%02d:%02d %04d\" % (\n            _DAYNAMES[weekday],\n            _MONTHNAMES[self._month],\n            self._day,\n            self._hour, self._minute, self._second,\n            self._year)\n\n    def isoformat(self, sep='T'):\n        \"\"\"Return the time formatted according to ISO.\n\n        This is 'YYYY-MM-DD HH:MM:SS.mmmmmm', or 'YYYY-MM-DD HH:MM:SS' if\n        self.microsecond == 0.\n\n        If self.tzinfo is not None, the UTC offset is also attached, giving\n        'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM' or 'YYYY-MM-DD HH:MM:SS+HH:MM'.\n\n        Optional argument sep specifies the separator between date and\n        time, default 'T'.\n        \"\"\"\n        s = (\"%04d-%02d-%02d%c\" % (self._year, self._month, self._day, sep) +\n             _format_time(self._hour, self._minute, self._second,\n                          self._microsecond))\n        off = self._utcoffset()\n        if off is not None:\n            if off < 0:\n                sign = \"-\"\n                off = -off\n            else:\n                sign = \"+\"\n            hh, mm = divmod(off, 60)\n            s += \"%s%02d:%02d\" % (sign, hh, mm)\n        return s\n\n    def __repr__(self):\n        \"\"\"Convert to formal string, for repr().\"\"\"\n        L = [self._year, self._month, self._day,  # These are never zero\n             self._hour, self._minute, self._second, self._microsecond]\n        if L[-1] == 0:\n            del L[-1]\n        if L[-1] == 0:\n            del L[-1]\n        s = \", \".join(map(str, L))\n        module = \"datetime.\" if self.__class__ is datetime else \"\"\n        s = \"%s(%s)\" % (module + self.__class__.__name__, s)\n        if self._tzinfo is not None:\n            assert s[-1:] == \")\"\n            s = s[:-1] + \", tzinfo=%r\" % self._tzinfo + \")\"\n        return s\n\n    def __str__(self):\n        \"Convert to string, for str().\"\n        return self.isoformat(sep=' ')\n\n    @classmethod\n    def strptime(cls, date_string, format):\n        'string, format -> new datetime parsed from a string (like time.strptime()).'\n        from _strptime import _strptime\n        # _strptime._strptime returns a two-element tuple.  The first\n        # element is a time.struct_time object.  The second is the\n        # microseconds (which are not defined for time.struct_time).\n        struct, micros = _strptime(date_string, format)\n        return cls(*(struct[0:6] + (micros,)))\n\n    def utcoffset(self):\n        \"\"\"Return the timezone offset in minutes east of UTC (negative west of\n        UTC).\"\"\"\n        if self._tzinfo is None:\n            return None\n        offset = self._tzinfo.utcoffset(self)\n        offset = _check_utc_offset(\"utcoffset\", offset)\n        if offset is not None:\n            offset = timedelta._create(0, offset * 60, 0, True)\n        return offset\n\n    # Return an integer (or None) instead of a timedelta (or None).\n    def _utcoffset(self):\n        if self._tzinfo is None:\n            return None\n        offset = self._tzinfo.utcoffset(self)\n        offset = _check_utc_offset(\"utcoffset\", offset)\n        return offset\n\n    def tzname(self):\n        \"\"\"Return the timezone name.\n\n        Note that the name is 100% informational -- there's no requirement that\n        it mean anything in particular. For example, \"GMT\", \"UTC\", \"-500\",\n        \"-5:00\", \"EDT\", \"US/Eastern\", \"America/New York\" are all valid replies.\n        \"\"\"\n        if self._tzinfo is None:\n            return None\n        name = self._tzinfo.tzname(self)\n        _check_tzname(name)\n        return name\n\n    def dst(self):\n        \"\"\"Return 0 if DST is not in effect, or the DST offset (in minutes\n        eastward) if DST is in effect.\n\n        This is purely informational; the DST offset has already been added to\n        the UTC offset returned by utcoffset() if applicable, so there's no\n        need to consult dst() unless you're interested in displaying the DST\n        info.\n        \"\"\"\n        if self._tzinfo is None:\n            return None\n        offset = self._tzinfo.dst(self)\n        offset = _check_utc_offset(\"dst\", offset)\n        if offset is not None:\n            offset = timedelta._create(0, offset * 60, 0, True)\n        return offset\n\n    # Return an integer (or None) instead of a timedelta (or None).\n    def _dst(self):\n        if self._tzinfo is None:\n            return None\n        offset = self._tzinfo.dst(self)\n        offset = _check_utc_offset(\"dst\", offset)\n        return offset\n\n    # Comparisons of datetime objects with other.\n\n    def __eq__(self, other):\n        if isinstance(other, datetime):\n            return self._cmp(other) == 0\n        elif hasattr(other, \"timetuple\") and not isinstance(other, date):\n            return NotImplemented\n        else:\n            return False\n\n    def __ne__(self, other):\n        if isinstance(other, datetime):\n            return self._cmp(other) != 0\n        elif hasattr(other, \"timetuple\") and not isinstance(other, date):\n            return NotImplemented\n        else:\n            return True\n\n    def __le__(self, other):\n        if isinstance(other, datetime):\n            return self._cmp(other) <= 0\n        elif hasattr(other, \"timetuple\") and not isinstance(other, date):\n            return NotImplemented\n        else:\n            _cmperror(self, other)\n\n    def __lt__(self, other):\n        if isinstance(other, datetime):\n            return self._cmp(other) < 0\n        elif hasattr(other, \"timetuple\") and not isinstance(other, date):\n            return NotImplemented\n        else:\n            _cmperror(self, other)\n\n    def __ge__(self, other):\n        if isinstance(other, datetime):\n            return self._cmp(other) >= 0\n        elif hasattr(other, \"timetuple\") and not isinstance(other, date):\n            return NotImplemented\n        else:\n            _cmperror(self, other)\n\n    def __gt__(self, other):\n        if isinstance(other, datetime):\n            return self._cmp(other) > 0\n        elif hasattr(other, \"timetuple\") and not isinstance(other, date):\n            return NotImplemented\n        else:\n            _cmperror(self, other)\n\n    def _cmp(self, other):\n        assert isinstance(other, datetime)\n        mytz = self._tzinfo\n        ottz = other._tzinfo\n        myoff = otoff = None\n\n        if mytz is ottz:\n            base_compare = True\n        else:\n            if mytz is not None:\n                myoff = self._utcoffset()\n            if ottz is not None:\n                otoff = other._utcoffset()\n            base_compare = myoff == otoff\n\n        if base_compare:\n            return _cmp((self._year, self._month, self._day,\n                         self._hour, self._minute, self._second,\n                         self._microsecond),\n                        (other._year, other._month, other._day,\n                         other._hour, other._minute, other._second,\n                         other._microsecond))\n        if myoff is None or otoff is None:\n            raise TypeError(\"can't compare offset-naive and offset-aware datetimes\")\n        # XXX What follows could be done more efficiently...\n        diff = self - other     # this will take offsets into account\n        if diff.days < 0:\n            return -1\n        return diff and 1 or 0\n\n    def _add_timedelta(self, other, factor):\n        y, m, d, hh, mm, ss, us = _normalize_datetime(\n            self._year,\n            self._month,\n            self._day + other.days * factor,\n            self._hour,\n            self._minute,\n            self._second + other.seconds * factor,\n            self._microsecond + other.microseconds * factor)\n        return datetime(y, m, d, hh, mm, ss, us, tzinfo=self._tzinfo)\n\n    def __add__(self, other):\n        \"Add a datetime and a timedelta.\"\n        if not isinstance(other, timedelta):\n            return NotImplemented\n        return self._add_timedelta(other, 1)\n\n    __radd__ = __add__\n\n    def __sub__(self, other):\n        \"Subtract two datetimes, or a datetime and a timedelta.\"\n        if not isinstance(other, datetime):\n            if isinstance(other, timedelta):\n                return self._add_timedelta(other, -1)\n            return NotImplemented\n\n        delta_d = self.toordinal() - other.toordinal()\n        delta_s = (self._hour - other._hour) * 3600 + \\\n                  (self._minute - other._minute) * 60 + \\\n                  (self._second - other._second)\n        delta_us = self._microsecond - other._microsecond\n        base = timedelta._create(delta_d, delta_s, delta_us, True)\n        if self._tzinfo is other._tzinfo:\n            return base\n        myoff = self._utcoffset()\n        otoff = other._utcoffset()\n        if myoff == otoff:\n            return base\n        if myoff is None or otoff is None:\n            raise TypeError(\"can't subtract offset-naive and offset-aware datetimes\")\n        return base + timedelta(minutes = otoff-myoff)\n\n    def __hash__(self):\n        if self._hashcode == -1:\n            tzoff = self._utcoffset()\n            if tzoff is None:\n                self._hashcode = hash(self._getstate()[0])\n            else:\n                days = _ymd2ord(self.year, self.month, self.day)\n                seconds = self.hour * 3600 + (self.minute - tzoff) * 60 + self.second\n                self._hashcode = hash(timedelta(days, seconds, self.microsecond))\n        return self._hashcode\n\n\n\ndatetime.min = datetime(1, 1, 1)\ndatetime.max = datetime(9999, 12, 31, 23, 59, 59, 999999)\ndatetime.resolution = timedelta(microseconds=1)\n\n\ndef _isoweek1monday(year):\n    # Helper to calculate the day number of the Monday starting week 1\n    # XXX This could be done more efficiently\n    THURSDAY = 3\n    firstday = _ymd2ord(year, 1, 1)\n    firstweekday = (firstday + 6) % 7  # See weekday() above\n    week1monday = firstday - firstweekday\n    if firstweekday > THURSDAY:\n        week1monday += 7\n    return week1monday\n\n\"\"\"\nSome time zone algebra.  For a datetime x, let\n    x.n = x stripped of its timezone -- its naive time.\n    x.o = x.utcoffset(), and assuming that doesn't raise an exception or\n          return None\n    x.d = x.dst(), and assuming that doesn't raise an exception or\n          return None\n    x.s = x's standard offset, x.o - x.d\n\nNow some derived rules, where k is a duration (timedelta).\n\n1. x.o = x.s + x.d\n   This follows from the definition of x.s.\n\n2. If x and y have the same tzinfo member, x.s = y.s.\n   This is actually a requirement, an assumption we need to make about\n   sane tzinfo classes.\n\n3. The naive UTC time corresponding to x is x.n - x.o.\n   This is again a requirement for a sane tzinfo class.\n\n4. (x+k).s = x.s\n   This follows from #2, and that datimetimetz+timedelta preserves tzinfo.\n\n5. (x+k).n = x.n + k\n   Again follows from how arithmetic is defined.\n\nNow we can explain tz.fromutc(x).  Let's assume it's an interesting case\n(meaning that the various tzinfo methods exist, and don't blow up or return\nNone when called).\n\nThe function wants to return a datetime y with timezone tz, equivalent to x.\nx is already in UTC.\n\nBy #3, we want\n\n    y.n - y.o = x.n                             [1]\n\nThe algorithm starts by attaching tz to x.n, and calling that y.  So\nx.n = y.n at the start.  Then it wants to add a duration k to y, so that [1]\nbecomes true; in effect, we want to solve [2] for k:\n\n   (y+k).n - (y+k).o = x.n                      [2]\n\nBy #1, this is the same as\n\n   (y+k).n - ((y+k).s + (y+k).d) = x.n          [3]\n\nBy #5, (y+k).n = y.n + k, which equals x.n + k because x.n=y.n at the start.\nSubstituting that into [3],\n\n   x.n + k - (y+k).s - (y+k).d = x.n; the x.n terms cancel, leaving\n   k - (y+k).s - (y+k).d = 0; rearranging,\n   k = (y+k).s - (y+k).d; by #4, (y+k).s == y.s, so\n   k = y.s - (y+k).d\n\nOn the RHS, (y+k).d can't be computed directly, but y.s can be, and we\napproximate k by ignoring the (y+k).d term at first.  Note that k can't be\nvery large, since all offset-returning methods return a duration of magnitude\nless than 24 hours.  For that reason, if y is firmly in std time, (y+k).d must\nbe 0, so ignoring it has no consequence then.\n\nIn any case, the new value is\n\n    z = y + y.s                                 [4]\n\nIt's helpful to step back at look at [4] from a higher level:  it's simply\nmapping from UTC to tz's standard time.\n\nAt this point, if\n\n    z.n - z.o = x.n                             [5]\n\nwe have an equivalent time, and are almost done.  The insecurity here is\nat the start of daylight time.  Picture US Eastern for concreteness.  The wall\ntime jumps from 1:59 to 3:00, and wall hours of the form 2:MM don't make good\nsense then.  The docs ask that an Eastern tzinfo class consider such a time to\nbe EDT (because it's \"after 2\"), which is a redundant spelling of 1:MM EST\non the day DST starts.  We want to return the 1:MM EST spelling because that's\nthe only spelling that makes sense on the local wall clock.\n\nIn fact, if [5] holds at this point, we do have the standard-time spelling,\nbut that takes a bit of proof.  We first prove a stronger result.  What's the\ndifference between the LHS and RHS of [5]?  Let\n\n    diff = x.n - (z.n - z.o)                    [6]\n\nNow\n    z.n =                       by [4]\n    (y + y.s).n =               by #5\n    y.n + y.s =                 since y.n = x.n\n    x.n + y.s =                 since z and y are have the same tzinfo member,\n                                    y.s = z.s by #2\n    x.n + z.s\n\nPlugging that back into [6] gives\n\n    diff =\n    x.n - ((x.n + z.s) - z.o) =     expanding\n    x.n - x.n - z.s + z.o =         cancelling\n    - z.s + z.o =                   by #2\n    z.d\n\nSo diff = z.d.\n\nIf [5] is true now, diff = 0, so z.d = 0 too, and we have the standard-time\nspelling we wanted in the endcase described above.  We're done.  Contrarily,\nif z.d = 0, then we have a UTC equivalent, and are also done.\n\nIf [5] is not true now, diff = z.d != 0, and z.d is the offset we need to\nadd to z (in effect, z is in tz's standard time, and we need to shift the\nlocal clock into tz's daylight time).\n\nLet\n\n    z' = z + z.d = z + diff                     [7]\n\nand we can again ask whether\n\n    z'.n - z'.o = x.n                           [8]\n\nIf so, we're done.  If not, the tzinfo class is insane, according to the\nassumptions we've made.  This also requires a bit of proof.  As before, let's\ncompute the difference between the LHS and RHS of [8] (and skipping some of\nthe justifications for the kinds of substitutions we've done several times\nalready):\n\n    diff' = x.n - (z'.n - z'.o) =           replacing z'.n via [7]\n            x.n  - (z.n + diff - z'.o) =    replacing diff via [6]\n            x.n - (z.n + x.n - (z.n - z.o) - z'.o) =\n            x.n - z.n - x.n + z.n - z.o + z'.o =    cancel x.n\n            - z.n + z.n - z.o + z'.o =              cancel z.n\n            - z.o + z'.o =                      #1 twice\n            -z.s - z.d + z'.s + z'.d =          z and z' have same tzinfo\n            z'.d - z.d\n\nSo z' is UTC-equivalent to x iff z'.d = z.d at this point.  If they are equal,\nwe've found the UTC-equivalent so are done.  In fact, we stop with [7] and\nreturn z', not bothering to compute z'.d.\n\nHow could z.d and z'd differ?  z' = z + z.d [7], so merely moving z' by\na dst() offset, and starting *from* a time already in DST (we know z.d != 0),\nwould have to change the result dst() returns:  we start in DST, and moving\na little further into it takes us out of DST.\n\nThere isn't a sane case where this can happen.  The closest it gets is at\nthe end of DST, where there's an hour in UTC with no spelling in a hybrid\ntzinfo class.  In US Eastern, that's 5:MM UTC = 0:MM EST = 1:MM EDT.  During\nthat hour, on an Eastern clock 1:MM is taken as being in standard time (6:MM\nUTC) because the docs insist on that, but 0:MM is taken as being in daylight\ntime (4:MM UTC).  There is no local time mapping to 5:MM UTC.  The local\nclock jumps from 1:59 back to 1:00 again, and repeats the 1:MM hour in\nstandard time.  Since that's what the local clock *does*, we want to map both\nUTC hours 5:MM and 6:MM to 1:MM Eastern.  The result is ambiguous\nin local time, but so it goes -- it's the way the local clock works.\n\nWhen x = 5:MM UTC is the input to this algorithm, x.o=0, y.o=-5 and y.d=0,\nso z=0:MM.  z.d=60 (minutes) then, so [5] doesn't hold and we keep going.\nz' = z + z.d = 1:MM then, and z'.d=0, and z'.d - z.d = -60 != 0 so [8]\n(correctly) concludes that z' is not UTC-equivalent to x.\n\nBecause we know z.d said z was in daylight time (else [5] would have held and\nwe would have stopped then), and we know z.d != z'.d (else [8] would have held\nand we have stopped then), and there are only 2 possible values dst() can\nreturn in Eastern, it follows that z'.d must be 0 (which it is in the example,\nbut the reasoning doesn't depend on the example -- it depends on there being\ntwo possible dst() outcomes, one zero and the other non-zero).  Therefore\nz' must be in standard time, and is the spelling we want in this case.\n\nNote again that z' is not UTC-equivalent as far as the hybrid tzinfo class is\nconcerned (because it takes z' as being in standard time rather than the\ndaylight time we intend here), but returning it gives the real-life \"local\nclock repeats an hour\" behavior when mapping the \"unspellable\" UTC hour into\ntz.\n\nWhen the input is 6:MM, z=1:MM and z.d=0, and we stop at once, again with\nthe 1:MM standard time spelling we want.\n\nSo how can this break?  One of the assumptions must be violated.  Two\npossibilities:\n\n1) [2] effectively says that y.s is invariant across all y belong to a given\n   time zone.  This isn't true if, for political reasons or continental drift,\n   a region decides to change its base offset from UTC.\n\n2) There may be versions of \"double daylight\" time where the tail end of\n   the analysis gives up a step too early.  I haven't thought about that\n   enough to say.\n\nIn any case, it's clear that the default fromutc() is strong enough to handle\n\"almost all\" time zones:  so long as the standard offset is invariant, it\ndoesn't matter if daylight time transition points change from year to year, or\nif daylight time is skipped in some years; it doesn't matter how large or\nsmall dst() may get within its bounds; and it doesn't even matter if some\nperverse time zone returns a negative dst()).  So a breaking case must be\npretty bizarre, and a tzinfo subclass can override fromutc() if it is.\n\"\"\"\n","src/lib/dbhash.py":"raise NotImplementedError(\"dbhash is not yet implemented in Skulpt\")\n","src/lib/decimal.py":"raise NotImplementedError(\"decimal is not yet implemented in Skulpt\")\n","src/lib/difflib.py":"raise NotImplementedError(\"difflib is not yet implemented in Skulpt\")\n","src/lib/dircache.py":"raise NotImplementedError(\"dircache is not yet implemented in Skulpt\")\n","src/lib/dis.py":"raise NotImplementedError(\"dis is not yet implemented in Skulpt\")\n","src/lib/distutils/__init__.py":"raise NotImplementedError(\"distutils is not yet implemented in Skulpt\")\n","src/lib/distutils/command/__init__.py":"raise NotImplementedError(\"command is not yet implemented in Skulpt\")\n","src/lib/distutils/tests/__init__.py":"raise NotImplementedError(\"tests is not yet implemented in Skulpt\")\n","src/lib/doctest.py":"raise NotImplementedError(\"doctest is not yet implemented in Skulpt\")\n","src/lib/document.js":"var $builtinmodule=function(){var a,b={__name__:new Sk.builtin.str(\"document\")};return b.getElementById=new Sk.builtin.func(function(a){var c=document.getElementById(a.v);return c?Sk.misceval.callsimArray(b.Element,[c]):Sk.builtin.none.none$}),b.createElement=new Sk.builtin.func(function(a){var c=document.createElement(a.v);if(c)return Sk.misceval.callsimArray(b.Element,[c])}),b.getElementsByTagName=new Sk.builtin.func(function(a){for(var c=document.getElementsByTagName(a.v),d=[],e=c.length-1;0<=e;e--)d.push(Sk.misceval.callsimArray(b.Element,[c[e]]));return new Sk.builtin.list(d)}),b.getElementsByClassName=new Sk.builtin.func(function(a){for(var c=document.getElementsByClassName(a.v),d=[],e=0;e<c.length;e++)d.push(Sk.misceval.callsimArray(b.Element,[c[e]]));return new Sk.builtin.list(d)}),b.getElementsByName=new Sk.builtin.func(function(a){for(var c=document.getElementsByName(a.v),d=[],e=0;e<c.length;e++)d.push(Sk.misceval.callsimArray(b.Element,[c[e]]));return new Sk.builtin.list(d)}),b.querySelector=new Sk.builtin.func(function(a){var c=document.querySelector(a.v);return c?Sk.misceval.callsimArray(b.Element,[c]):Sk.builtin.none.none$}),b.querySelectorAll=new Sk.builtin.func(function(a){for(var c=document.querySelectorAll(a.v),d=[],e=0;e<c.length;e++)d.push(Sk.misceval.callsimArray(b.Element,[c[e]]));return new Sk.builtin.list(d)}),b.getCurrentEditorValue=new Sk.builtin.func(function(){if(void 0!==Sk.divid&&void 0!==window.edList){if(Sk.gradeContainer!=Sk.divid){var a=Sk.gradeContainer+\" \"+Sk.divid;return new Sk.builtin.str(window.edList[a].editor.getValue())}return new Sk.builtin.str(window.edList[Sk.divid].editor.getValue())}throw new Sk.builtin.AttributeError(\"Can't find editor for this div\")}),b.currentDiv=new Sk.builtin.func(function(){if(void 0!==Sk.divid)return new Sk.builtin.str(Sk.divid);throw new Sk.builtin.AttributeError(\"There is no value set for divid\")}),b.currentCourse=new Sk.builtin.func(function(){if(void 0!==eBookConfig)return new Sk.builtin.str(eBookConfig.course);throw new Sk.builtin.AttributeError(\"There is no course\")}),b.currentGradingContainer=new Sk.builtin.func(function(){if(void 0!==Sk.gradeContainer)return new Sk.builtin.str(Sk.gradeContainer);if(null!=Sk.divid)return new Sk.builtin.str(Sk.divid);throw new Sk.builtin.AttributeError(\"There is no value set for grading\")}),a=function(a,c){c.__init__=new Sk.builtin.func(function(a,b){a.v=b,a.innerHTML=b.innerHTML,a.innerText=b.innerText,void 0!==b.value&&(a.value=b.value,Sk.abstr.objectSetItem(a.$d,new Sk.builtin.str(\"value\"),new Sk.builtin.str(a.value))),void 0!==b.checked&&(a.checked=b.checked,Sk.abstr.objectSetItem(a.$d,new Sk.builtin.str(\"checked\"),new Sk.builtin.str(a.checked))),Sk.abstr.objectSetItem(a.$d,new Sk.builtin.str(\"innerHTML\"),new Sk.builtin.str(a.innerHTML)),Sk.abstr.objectSetItem(a.$d,new Sk.builtin.str(\"innerText\"),new Sk.builtin.str(a.innerText))}),c._internalGenericGetAttr=Sk.builtin.object.prototype.GenericGetAttr,c.__getattr__=new Sk.builtin.func(function(a,d){if(null!=d&&(Sk.builtin.checkString(d)||\"string\"==typeof d)){var e=d;Sk.builtin.checkString(d)&&(e=Sk.ffi.remapToJs(d));var f=a.v[e];if(null!=f)return Sk.misceval.callsimArray(b.Element,[f])}return c._internalGenericGetAttr(d)}),c.__setattr__=new Sk.builtin.func(function(a,b,c){b=Sk.ffi.remapToJs(b),\"innerHTML\"===b&&(a.innerHTML=c,a.v.innerHTML=c.v,Sk.abstr.objectSetItem(a.$d,new Sk.builtin.str(\"innerHTML\"),c)),\"innerText\"===b&&(a.innerText=c,a.v.innerText=c.v,Sk.abstr.objectSetItem(a.$d,new Sk.builtin.str(\"innerText\"),c))}),c.appendChild=new Sk.builtin.func(function(a,b){a.v.appendChild(b.v)}),c.removeChild=new Sk.builtin.func(function(a,b){a.v.removeChild(b.v)}),c.closest=new Sk.builtin.func(function(a,c){var d=a.v.closest(c.v);return d?Sk.misceval.callsimArray(b.Element,[d]):Sk.builtin.none.none$}),c.getCSS=new Sk.builtin.func(function(a,b){return new Sk.builtin.str(a.v.style[b.v])}),c.setCSS=new Sk.builtin.func(function(a,b,c){a.v.style[b.v]=c.v}),c.getAttribute=new Sk.builtin.func(function(a,b){var c=a.v.getAttribute(b.v);return c?new Sk.builtin.str(c):Sk.builtin.none.none$}),c.setAttribute=new Sk.builtin.func(function(a,b,c){a.v.setAttribute(b.v,c.v)}),c.getProperty=new Sk.builtin.func(function(a,b){var c=a.v[b.v];return c?new Sk.builtin.str(c):Sk.builtin.none.none$}),c.__str__=new Sk.builtin.func(function(a){return console.log(a.v.tagName),new Sk.builtin.str(a.v.tagName)}),c.__repr__=new Sk.builtin.func(function(){return new Sk.builtin.str(\"[DOM Element]\")})},b.Element=Sk.misceval.buildClass(b,a,\"Element\",[]),b};","src/lib/dumbdbm.py":"raise NotImplementedError(\"dumbdbm is not yet implemented in Skulpt\")\n","src/lib/dummy_thread.py":"raise NotImplementedError(\"dummy_thread is not yet implemented in Skulpt\")\n","src/lib/dummy_threading.py":"raise NotImplementedError(\"dummy_threading is not yet implemented in Skulpt\")\n","src/lib/email/__init__.py":"raise NotImplementedError(\"email is not yet implemented in Skulpt\")\n","src/lib/email/mime/__init__.py":"raise NotImplementedError(\"mime is not yet implemented in Skulpt\")\n","src/lib/email/test/data/__init__.py":"raise NotImplementedError(\"data is not yet implemented in Skulpt\")\n","src/lib/encodings/__init__.py":"raise NotImplementedError(\"encodings is not yet implemented in Skulpt\")\n","src/lib/filecmp.py":"raise NotImplementedError(\"filecmp is not yet implemented in Skulpt\")\n","src/lib/fileinput.py":"raise NotImplementedError(\"fileinput is not yet implemented in Skulpt\")\n","src/lib/fnmatch.py":"raise NotImplementedError(\"fnmatch is not yet implemented in Skulpt\")\n","src/lib/formatter.py":"raise NotImplementedError(\"formatter is not yet implemented in Skulpt\")\n","src/lib/fpformat.py":"raise NotImplementedError(\"fpformat is not yet implemented in Skulpt\")\n","src/lib/fractions.py":"raise NotImplementedError(\"fractions is not yet implemented in Skulpt\")\n","src/lib/ftplib.py":"raise NotImplementedError(\"ftplib is not yet implemented in Skulpt\")\n","src/lib/functools.py":"raise NotImplementedError(\"functools is not yet implemented in Skulpt\")\n","src/lib/genericpath.py":"raise NotImplementedError(\"genericpath is not yet implemented in Skulpt\")\n","src/lib/getopt.py":"raise NotImplementedError(\"getopt is not yet implemented in Skulpt\")\n","src/lib/getpass.py":"raise NotImplementedError(\"getpass is not yet implemented in Skulpt\")\n","src/lib/gettext.py":"raise NotImplementedError(\"gettext is not yet implemented in Skulpt\")\n","src/lib/glob.py":"raise NotImplementedError(\"glob is not yet implemented in Skulpt\")\n","src/lib/gzip.py":"raise NotImplementedError(\"gzip is not yet implemented in Skulpt\")\n","src/lib/hashlib.py":"raise NotImplementedError(\"hashlib is not yet implemented in Skulpt\")\n","src/lib/heapq.py":"\"\"\"Heap queue algorithm (a.k.a. priority queue).\n\nHeaps are arrays for which a[k] <= a[2*k+1] and a[k] <= a[2*k+2] for\nall k, counting elements from 0.  For the sake of comparison,\nnon-existing elements are considered to be infinite.  The interesting\nproperty of a heap is that a[0] is always its smallest element.\n\nUsage:\n\nheap = []            # creates an empty heap\nheappush(heap, item) # pushes a new item on the heap\nitem = heappop(heap) # pops the smallest item from the heap\nitem = heap[0]       # smallest item on the heap without popping it\nheapify(x)           # transforms list into a heap, in-place, in linear time\nitem = heapreplace(heap, item) # pops and returns smallest item, and adds\n                               # new item; the heap size is unchanged\n\nOur API differs from textbook heap algorithms as follows:\n\n- We use 0-based indexing.  This makes the relationship between the\n  index for a node and the indexes for its children slightly less\n  obvious, but is more suitable since Python uses 0-based indexing.\n\n- Our heappop() method returns the smallest item, not the largest.\n\nThese two make it possible to view the heap as a regular Python list\nwithout surprises: heap[0] is the smallest item, and heap.sort()\nmaintains the heap invariant!\n\"\"\"\n\n# Original code by Kevin O'Connor, augmented by Tim Peters and Raymond Hettinger\n\n__about__ = \"\"\"Heap queues\n\n[explanation by François Pinard]\n\nHeaps are arrays for which a[k] <= a[2*k+1] and a[k] <= a[2*k+2] for\nall k, counting elements from 0.  For the sake of comparison,\nnon-existing elements are considered to be infinite.  The interesting\nproperty of a heap is that a[0] is always its smallest element.\n\nThe strange invariant above is meant to be an efficient memory\nrepresentation for a tournament.  The numbers below are `k', not a[k]:\n\n                                   0\n\n                  1                                 2\n\n          3               4                5               6\n\n      7       8       9       10      11      12      13      14\n\n    15 16   17 18   19 20   21 22   23 24   25 26   27 28   29 30\n\n\nIn the tree above, each cell `k' is topping `2*k+1' and `2*k+2'.  In\na usual binary tournament we see in sports, each cell is the winner\nover the two cells it tops, and we can trace the winner down the tree\nto see all opponents s/he had.  However, in many computer applications\nof such tournaments, we do not need to trace the history of a winner.\nTo be more memory efficient, when a winner is promoted, we try to\nreplace it by something else at a lower level, and the rule becomes\nthat a cell and the two cells it tops contain three different items,\nbut the top cell \"wins\" over the two topped cells.\n\nIf this heap invariant is protected at all time, index 0 is clearly\nthe overall winner.  The simplest algorithmic way to remove it and\nfind the \"next\" winner is to move some loser (let's say cell 30 in the\ndiagram above) into the 0 position, and then percolate this new 0 down\nthe tree, exchanging values, until the invariant is re-established.\nThis is clearly logarithmic on the total number of items in the tree.\nBy iterating over all items, you get an O(n ln n) sort.\n\nA nice feature of this sort is that you can efficiently insert new\nitems while the sort is going on, provided that the inserted items are\nnot \"better\" than the last 0'th element you extracted.  This is\nespecially useful in simulation contexts, where the tree holds all\nincoming events, and the \"win\" condition means the smallest scheduled\ntime.  When an event schedule other events for execution, they are\nscheduled into the future, so they can easily go into the heap.  So, a\nheap is a good structure for implementing schedulers (this is what I\nused for my MIDI sequencer :-).\n\nVarious structures for implementing schedulers have been extensively\nstudied, and heaps are good for this, as they are reasonably speedy,\nthe speed is almost constant, and the worst case is not much different\nthan the average case.  However, there are other representations which\nare more efficient overall, yet the worst cases might be terrible.\n\nHeaps are also very useful in big disk sorts.  You most probably all\nknow that a big sort implies producing \"runs\" (which are pre-sorted\nsequences, which size is usually related to the amount of CPU memory),\nfollowed by a merging passes for these runs, which merging is often\nvery cleverly organised[1].  It is very important that the initial\nsort produces the longest runs possible.  Tournaments are a good way\nto that.  If, using all the memory available to hold a tournament, you\nreplace and percolate items that happen to fit the current run, you'll\nproduce runs which are twice the size of the memory for random input,\nand much better for input fuzzily ordered.\n\nMoreover, if you output the 0'th item on disk and get an input which\nmay not fit in the current tournament (because the value \"wins\" over\nthe last output value), it cannot fit in the heap, so the size of the\nheap decreases.  The freed memory could be cleverly reused immediately\nfor progressively building a second heap, which grows at exactly the\nsame rate the first heap is melting.  When the first heap completely\nvanishes, you switch heaps and start a new run.  Clever and quite\neffective!\n\nIn a word, heaps are useful memory structures to know.  I use them in\na few applications, and I think it is good to keep a `heap' module\naround. :-)\n\n--------------------\n[1] The disk balancing algorithms which are current, nowadays, are\nmore annoying than clever, and this is a consequence of the seeking\ncapabilities of the disks.  On devices which cannot seek, like big\ntape drives, the story was quite different, and one had to be very\nclever to ensure (far in advance) that each tape movement will be the\nmost effective possible (that is, will best participate at\n\"progressing\" the merge).  Some tapes were even able to read\nbackwards, and this was also used to avoid the rewinding time.\nBelieve me, real good tape sorts were quite spectacular to watch!\nFrom all times, sorting has always been a Great Art! :-)\n\"\"\"\n\n__all__ = ['heappush', 'heappop', 'heapify', 'heapreplace',\n           'nlargest', 'nsmallest', 'heappushpop']\n\ndef heappush(heap, item):\n    \"\"\"Push item onto heap, maintaining the heap invariant.\"\"\"\n    heap.append(item)\n    _siftdown(heap, 0, len(heap)-1)\n\ndef heappop(heap):\n    \"\"\"Pop the smallest item off the heap, maintaining the heap invariant.\"\"\"\n    lastelt = heap.pop()    # raises appropriate IndexError if heap is empty\n    if heap:\n        returnitem = heap[0]\n        heap[0] = lastelt\n        _siftup(heap, 0)\n        return returnitem\n    return lastelt\n\ndef heapreplace(heap, item):\n    \"\"\"Pop and return the current smallest value, and add the new item.\n\n    This is more efficient than heappop() followed by heappush(), and can be\n    more appropriate when using a fixed-size heap.  Note that the value\n    returned may be larger than item!  That constrains reasonable uses of\n    this routine unless written as part of a conditional replacement:\n\n        if item > heap[0]:\n            item = heapreplace(heap, item)\n    \"\"\"\n    returnitem = heap[0]    # raises appropriate IndexError if heap is empty\n    heap[0] = item\n    _siftup(heap, 0)\n    return returnitem\n\ndef heappushpop(heap, item):\n    \"\"\"Fast version of a heappush followed by a heappop.\"\"\"\n    if heap and heap[0] < item:\n        item, heap[0] = heap[0], item\n        _siftup(heap, 0)\n    return item\n\ndef heapify(x):\n    \"\"\"Transform list into a heap, in-place, in O(len(x)) time.\"\"\"\n    n = len(x)\n    # Transform bottom-up.  The largest index there's any point to looking at\n    # is the largest with a child index in-range, so must have 2*i + 1 < n,\n    # or i < (n-1)/2.  If n is even = 2*j, this is (2*j-1)/2 = j-1/2 so\n    # j-1 is the largest, which is n//2 - 1.  If n is odd = 2*j+1, this is\n    # (2*j+1-1)/2 = j so j-1 is the largest, and that's again n//2-1.\n    for i in reversed(range(n//2)):\n        _siftup(x, i)\n\ndef _heappop_max(heap):\n    \"\"\"Maxheap version of a heappop.\"\"\"\n    lastelt = heap.pop()    # raises appropriate IndexError if heap is empty\n    if heap:\n        returnitem = heap[0]\n        heap[0] = lastelt\n        _siftup_max(heap, 0)\n        return returnitem\n    return lastelt\n\ndef _heapreplace_max(heap, item):\n    \"\"\"Maxheap version of a heappop followed by a heappush.\"\"\"\n    returnitem = heap[0]    # raises appropriate IndexError if heap is empty\n    heap[0] = item\n    _siftup_max(heap, 0)\n    return returnitem\n\ndef _heapify_max(x):\n    \"\"\"Transform list into a maxheap, in-place, in O(len(x)) time.\"\"\"\n    n = len(x)\n    for i in reversed(range(n//2)):\n        _siftup_max(x, i)\n\n# 'heap' is a heap at all indices >= startpos, except possibly for pos.  pos\n# is the index of a leaf with a possibly out-of-order value.  Restore the\n# heap invariant.\ndef _siftdown(heap, startpos, pos):\n    newitem = heap[pos]\n    # Follow the path to the root, moving parents down until finding a place\n    # newitem fits.\n    while pos > startpos:\n        parentpos = (pos - 1) >> 1\n        parent = heap[parentpos]\n        if newitem < parent:\n            heap[pos] = parent\n            pos = parentpos\n            continue\n        break\n    heap[pos] = newitem\n\n# The child indices of heap index pos are already heaps, and we want to make\n# a heap at index pos too.  We do this by bubbling the smaller child of\n# pos up (and so on with that child's children, etc) until hitting a leaf,\n# then using _siftdown to move the oddball originally at index pos into place.\n#\n# We *could* break out of the loop as soon as we find a pos where newitem <=\n# both its children, but turns out that's not a good idea, and despite that\n# many books write the algorithm that way.  During a heap pop, the last array\n# element is sifted in, and that tends to be large, so that comparing it\n# against values starting from the root usually doesn't pay (= usually doesn't\n# get us out of the loop early).  See Knuth, Volume 3, where this is\n# explained and quantified in an exercise.\n#\n# Cutting the # of comparisons is important, since these routines have no\n# way to extract \"the priority\" from an array element, so that intelligence\n# is likely to be hiding in custom comparison methods, or in array elements\n# storing (priority, record) tuples.  Comparisons are thus potentially\n# expensive.\n#\n# On random arrays of length 1000, making this change cut the number of\n# comparisons made by heapify() a little, and those made by exhaustive\n# heappop() a lot, in accord with theory.  Here are typical results from 3\n# runs (3 just to demonstrate how small the variance is):\n#\n# Compares needed by heapify     Compares needed by 1000 heappops\n# --------------------------     --------------------------------\n# 1837 cut to 1663               14996 cut to 8680\n# 1855 cut to 1659               14966 cut to 8678\n# 1847 cut to 1660               15024 cut to 8703\n#\n# Building the heap by using heappush() 1000 times instead required\n# 2198, 2148, and 2219 compares:  heapify() is more efficient, when\n# you can use it.\n#\n# The total compares needed by list.sort() on the same lists were 8627,\n# 8627, and 8632 (this should be compared to the sum of heapify() and\n# heappop() compares):  list.sort() is (unsurprisingly!) more efficient\n# for sorting.\n\ndef _siftup(heap, pos):\n    endpos = len(heap)\n    startpos = pos\n    newitem = heap[pos]\n    # Bubble up the smaller child until hitting a leaf.\n    childpos = 2*pos + 1    # leftmost child position\n    while childpos < endpos:\n        # Set childpos to index of smaller child.\n        rightpos = childpos + 1\n        if rightpos < endpos and not heap[childpos] < heap[rightpos]:\n            childpos = rightpos\n        # Move the smaller child up.\n        heap[pos] = heap[childpos]\n        pos = childpos\n        childpos = 2*pos + 1\n    # The leaf at pos is empty now.  Put newitem there, and bubble it up\n    # to its final resting place (by sifting its parents down).\n    heap[pos] = newitem\n    _siftdown(heap, startpos, pos)\n\ndef _siftdown_max(heap, startpos, pos):\n    'Maxheap variant of _siftdown'\n    newitem = heap[pos]\n    # Follow the path to the root, moving parents down until finding a place\n    # newitem fits.\n    while pos > startpos:\n        parentpos = (pos - 1) >> 1\n        parent = heap[parentpos]\n        if parent < newitem:\n            heap[pos] = parent\n            pos = parentpos\n            continue\n        break\n    heap[pos] = newitem\n\ndef _siftup_max(heap, pos):\n    'Maxheap variant of _siftup'\n    endpos = len(heap)\n    startpos = pos\n    newitem = heap[pos]\n    # Bubble up the larger child until hitting a leaf.\n    childpos = 2*pos + 1    # leftmost child position\n    while childpos < endpos:\n        # Set childpos to index of larger child.\n        rightpos = childpos + 1\n        if rightpos < endpos and not heap[rightpos] < heap[childpos]:\n            childpos = rightpos\n        # Move the larger child up.\n        heap[pos] = heap[childpos]\n        pos = childpos\n        childpos = 2*pos + 1\n    # The leaf at pos is empty now.  Put newitem there, and bubble it up\n    # to its final resting place (by sifting its parents down).\n    heap[pos] = newitem\n    _siftdown_max(heap, startpos, pos)\n\n\n# Algorithm notes for nlargest() and nsmallest()\n# ==============================================\n#\n# Make a single pass over the data while keeping the k most extreme values\n# in a heap.  Memory consumption is limited to keeping k values in a list.\n#\n# Measured performance for random inputs:\n#\n#                                   number of comparisons\n#    n inputs     k-extreme values  (average of 5 trials)   % more than min()\n# -------------   ----------------  ---------------------   -----------------\n#      1,000           100                  3,317               231.7%\n#     10,000           100                 14,046                40.5%\n#    100,000           100                105,749                 5.7%\n#  1,000,000           100              1,007,751                 0.8%\n# 10,000,000           100             10,009,401                 0.1%\n#\n# Theoretical number of comparisons for k smallest of n random inputs:\n#\n# Step   Comparisons                  Action\n# ----   --------------------------   ---------------------------\n#  1     1.66 * k                     heapify the first k-inputs\n#  2     n - k                        compare remaining elements to top of heap\n#  3     k * (1 + lg2(k)) * ln(n/k)   replace the topmost value on the heap\n#  4     k * lg2(k) - (k/2)           final sort of the k most extreme values\n#\n# Combining and simplifying for a rough estimate gives:\n#\n#        comparisons = n + k * (log(k, 2) * log(n/k) + log(k, 2) + log(n/k))\n#\n# Computing the number of comparisons for step 3:\n# -----------------------------------------------\n# * For the i-th new value from the iterable, the probability of being in the\n#   k most extreme values is k/i.  For example, the probability of the 101st\n#   value seen being in the 100 most extreme values is 100/101.\n# * If the value is a new extreme value, the cost of inserting it into the\n#   heap is 1 + log(k, 2).\n# * The probability times the cost gives:\n#            (k/i) * (1 + log(k, 2))\n# * Summing across the remaining n-k elements gives:\n#            sum((k/i) * (1 + log(k, 2)) for i in range(k+1, n+1))\n# * This reduces to:\n#            (H(n) - H(k)) * k * (1 + log(k, 2))\n# * Where H(n) is the n-th harmonic number estimated by:\n#            gamma = 0.5772156649\n#            H(n) = log(n, e) + gamma + 1 / (2 * n)\n#   http://en.wikipedia.org/wiki/Harmonic_series_(mathematics)#Rate_of_divergence\n# * Substituting the H(n) formula:\n#            comparisons = k * (1 + log(k, 2)) * (log(n/k, e) + (1/n - 1/k) / 2)\n#\n# Worst-case for step 3:\n# ----------------------\n# In the worst case, the input data is reversed sorted so that every new element\n# must be inserted in the heap:\n#\n#             comparisons = 1.66 * k + log(k, 2) * (n - k)\n#\n# Alternative Algorithms\n# ----------------------\n# Other algorithms were not used because they:\n# 1) Took much more auxiliary memory,\n# 2) Made multiple passes over the data.\n# 3) Made more comparisons in common cases (small k, large n, semi-random input).\n# See the more detailed comparison of approach at:\n# http://code.activestate.com/recipes/577573-compare-algorithms-for-heapqsmallest\n\ndef nsmallest(n, iterable, key=None):\n    \"\"\"Find the n smallest elements in a dataset.\n\n    Equivalent to:  sorted(iterable, key=key)[:n]\n    \"\"\"\n\n    # Short-cut for n==1 is to use min()\n    if n == 1:\n        it = iter(iterable)\n        sentinel = object()\n        result = min(it, default=sentinel, key=key)\n        return [] if result is sentinel else [result]\n\n    # When n>=size, it's faster to use sorted()\n    try:\n        size = len(iterable)\n    except (TypeError, AttributeError):\n        pass\n    else:\n        if n >= size:\n            return sorted(iterable, key=key)[:n]\n\n    # When key is none, use simpler decoration\n    if key is None:\n        it = iter(iterable)\n        # put the range(n) first so that zip() doesn't\n        # consume one too many elements from the iterator\n        result = [(elem, i) for i, elem in zip(range(n), it)]\n        if not result:\n            return result\n        _heapify_max(result)\n        top = result[0][0]\n        order = n\n        _heapreplace = _heapreplace_max\n        for elem in it:\n            if elem < top:\n                _heapreplace(result, (elem, order))\n                top, _order = result[0]\n                order += 1\n        result.sort()\n        return [elem for (elem, order) in result]\n\n    # General case, slowest method\n    it = iter(iterable)\n    result = [(key(elem), i, elem) for i, elem in zip(range(n), it)]\n    if not result:\n        return result\n    _heapify_max(result)\n    top = result[0][0]\n    order = n\n    _heapreplace = _heapreplace_max\n    for elem in it:\n        k = key(elem)\n        if k < top:\n            _heapreplace(result, (k, order, elem))\n            top, _order, _elem = result[0]\n            order += 1\n    result.sort()\n    return [elem for (k, order, elem) in result]\n\ndef nlargest(n, iterable, key=None):\n    \"\"\"Find the n largest elements in a dataset.\n\n    Equivalent to:  sorted(iterable, key=key, reverse=True)[:n]\n    \"\"\"\n\n    # Short-cut for n==1 is to use max()\n    if n == 1:\n        it = iter(iterable)\n        sentinel = object()\n        result = max(it, default=sentinel, key=key)\n        return [] if result is sentinel else [result]\n\n    # When n>=size, it's faster to use sorted()\n    try:\n        size = len(iterable)\n    except (TypeError, AttributeError):\n        pass\n    else:\n        if n >= size:\n            return sorted(iterable, key=key, reverse=True)[:n]\n\n    # When key is none, use simpler decoration\n    if key is None:\n        it = iter(iterable)\n        result = [(elem, i) for i, elem in zip(range(0, -n, -1), it)]\n        if not result:\n            return result\n        heapify(result)\n        top = result[0][0]\n        order = -n\n        _heapreplace = heapreplace\n        for elem in it:\n            if top < elem:\n                _heapreplace(result, (elem, order))\n                top, _order = result[0]\n                order -= 1\n        result.sort(reverse=True)\n        return [elem for (elem, order) in result]\n\n    # General case, slowest method\n    it = iter(iterable)\n    result = [(key(elem), i, elem) for i, elem in zip(range(0, -n, -1), it)]\n    if not result:\n        return result\n    heapify(result)\n    top = result[0][0]\n    order = -n\n    _heapreplace = heapreplace\n    for elem in it:\n        k = key(elem)\n        if top < k:\n            _heapreplace(result, (k, order, elem))\n            top, _order, _elem = result[0]\n            order -= 1\n    result.sort(reverse=True)\n    return [elem for (k, order, elem) in result]\n\n# If available, use C implementation\ntry:\n    from _heapq import *\nexcept ImportError:\n    pass\ntry:\n    from _heapq import _heapreplace_max\nexcept ImportError:\n    pass\ntry:\n    from _heapq import _heapify_max\nexcept ImportError:\n    pass\ntry:\n    from _heapq import _heappop_max\nexcept ImportError:\n    pass\n\n\nif __name__ == \"__main__\":\n\n    import doctest # pragma: no cover\n    print(doctest.testmod()) # pragma: no cover\n","src/lib/hmac.py":"raise NotImplementedError(\"hmac is not yet implemented in Skulpt\")\n","src/lib/hotshot/__init__.py":"raise NotImplementedError(\"hotshot is not yet implemented in Skulpt\")\n","src/lib/htmlentitydefs.py":"raise NotImplementedError(\"htmlentitydefs is not yet implemented in Skulpt\")\n","src/lib/htmllib.py":"raise NotImplementedError(\"htmllib is not yet implemented in Skulpt\")\n","src/lib/httplib.py":"raise NotImplementedError(\"httplib is not yet implemented in Skulpt\")\n","src/lib/idlelib/Icons/__init__.py":"raise NotImplementedError(\"Icons is not yet implemented in Skulpt\")\n","src/lib/idlelib/__init__.py":"raise NotImplementedError(\"idlelib is not yet implemented in Skulpt\")\n","src/lib/ihooks.py":"raise NotImplementedError(\"ihooks is not yet implemented in Skulpt\")\n","src/lib/image.js":"var ImageMod,$builtinmodule;ImageMod||(ImageMod={},ImageMod.canvasLib=[]),$builtinmodule=function(){var a,b,c,d,e,f,g,h={__name__:new Sk.builtin.str(\"image\")};return h.Image=Sk.misceval.buildClass(h,function(a,b){var c=Math.floor;e=function(a){a.width=a.image.width,a.height=a.image.height,a.delay=0,a.updateCount=0,a.updateInterval=1,a.lastx=0,a.lasty=0,a.canvas=document.createElement(\"canvas\"),a.canvas.height=a.height,a.canvas.width=a.width,a.ctx=a.canvas.getContext(\"2d\"),a.ctx.drawImage(a.image,0,0),a.imagedata=a.ctx.getImageData(0,0,a.width,a.height)},b.__init__=new Sk.builtin.func(function(a,b){var c;Sk.builtin.pyCheckArgsLen(\"__init__\",arguments.length,2,2);try{a.image=document.getElementById(Sk.ffi.remapToJs(b)),e(a)}catch(b){a.image=null}if(null==a.image)return c=new Sk.misceval.Suspension,c.resume=function(){if(c.data.error)throw new Sk.builtin.IOError(c.data.error.message)},c.data={type:\"Sk.promise\",promise:new Promise(function(c,d){var f=new Image;f.crossOrigin=\"\",f.onerror=function(){d(Error(\"Failed to load URL: \"+f.src))},f.onload=function(){a.image=this,e(a),c()},f.src=g(b)})},c}),g=function(a){let b=Sk.ffi.remapToJs(a);try{let a=Sk.read(b),c=b.substring(b.lastIndexOf(\".\")+1);return`data:image/${c};base64, ${a}`}catch(a){console.log(`${b} is not in the database ${a}`)}var c,d,e=\"function\"==typeof Sk.imageProxy?Sk.imageProxy:function(a){return c=document.createElement(\"a\"),c.href=d,window.location.host===c.host?a:Sk.imageProxy+\"/\"+a};return d=Sk.ffi.remapToJs(a),d=e(d),d},f=function(a,b,c){if(0>b||0>c||b>=a.width||c>=a.height)throw new Sk.builtin.ValueError(\"Pixel index out of range.\")};var i=function(a,b,c){var d;Sk.builtin.pyCheckArgsLen(\"setdelay\",arguments.length,2,3),a.delay=Sk.ffi.remapToJs(b),d=Sk.builtin.asnum$(c),a.updateInterval=d?d:1};b.set_delay=new Sk.builtin.func(i),b.setDelay=new Sk.builtin.func(i);var j=function(a){var b,d=[];for(Sk.builtin.pyCheckArgsLen(\"getpixels\",arguments.length,1,1),b=0;b<a.image.height*a.image.width;b++)d[b]=Sk.misceval.callsimArray(a.getPixel,[a,b%a.image.width,c(b/a.image.width)]);return new Sk.builtin.tuple(d)};b.get_pixels=new Sk.builtin.func(j),b.getPixels=new Sk.builtin.func(j),b.getData=new Sk.builtin.func(function(a){var b,d,e,g,h,j,k,l=[];for(Sk.builtin.pyCheckArgsLen(\"getData\",arguments.length,1,1),b=0;b<a.image.height*a.image.width;b++)d=b%a.image.width,e=c(b/a.image.width),f(a,d,e),k=4*e*a.width+4*d,g=a.imagedata.data[k],h=a.imagedata.data[k+1],j=a.imagedata.data[k+2],l[b]=new Sk.builtin.tuple([new Sk.builtin.int_(g),new Sk.builtin.int_(h),new Sk.builtin.int_(j)]);return new Sk.builtin.list(l)});var k=function(a,b,c){var d,e,g,i;return Sk.builtin.pyCheckArgsLen(\"getpixel\",arguments.length,3,3),b=Sk.builtin.asnum$(b),c=Sk.builtin.asnum$(c),f(a,b,c),i=4*c*a.width+4*b,d=a.imagedata.data[i],g=a.imagedata.data[i+1],e=a.imagedata.data[i+2],Sk.misceval.callsimArray(h.Pixel,[d,g,e,b,c])};b.get_pixel=new Sk.builtin.func(k),b.getPixel=new Sk.builtin.func(k),d=function(a,b,c){var d=new Sk.misceval.Suspension;return d.resume=function(){return Sk.builtin.none.none$},d.data={type:\"Sk.promise\",promise:new Promise(function(d){var e=Math.max,f=Math.abs,g=Math.min;a.updateCount++,0==a.updateCount%a.updateInterval?(a.lastx+a.updateInterval>=a.width?a.lastCtx.putImageData(a.imagedata,a.lastUlx,a.lastUly,0,a.lasty,a.width,2):a.lasty+a.updateInterval>=a.height?a.lastCtx.putImageData(a.imagedata,a.lastUlx,a.lastUly,a.lastx,0,2,a.height):a.lastCtx.putImageData(a.imagedata,a.lastUlx,a.lastUly,g(b,a.lastx),g(c,a.lasty),e(f(b-a.lastx),1),e(f(c-a.lasty),1)),a.lastx=b,a.lasty=c,0<a.delay?window.setTimeout(d,a.delay):d()):d()})},d};var l=function(a,b,c,e){var g;return Sk.builtin.pyCheckArgsLen(\"setpixel\",arguments.length,4,4),b=Sk.builtin.asnum$(b),c=Sk.builtin.asnum$(c),f(a,b,c),g=4*c*a.width+4*b,a.imagedata.data[g]=Sk.builtin.asnum$(Sk.misceval.callsimArray(e.getRed,[e])),a.imagedata.data[g+1]=Sk.builtin.asnum$(Sk.misceval.callsimArray(e.getGreen,[e])),a.imagedata.data[g+2]=Sk.builtin.asnum$(Sk.misceval.callsimArray(e.getBlue,[e])),a.imagedata.data[g+3]=255,d(a,b,c)};b.set_pixel=new Sk.builtin.func(l),b.setPixel=new Sk.builtin.func(l);var m=function(a,b,e){var g,h,i;return Sk.builtin.pyCheckArgsLen(\"setpixelat\",arguments.length,3,3),b=Sk.builtin.asnum$(b),g=b%a.image.width,h=c(b/a.image.width),f(a,g,h),i=4*h*a.width+4*g,a.imagedata.data[i]=Sk.builtin.asnum$(Sk.misceval.callsimArray(e.getRed,[e])),a.imagedata.data[i+1]=Sk.builtin.asnum$(Sk.misceval.callsimArray(e.getGreen,[e])),a.imagedata.data[i+2]=Sk.builtin.asnum$(Sk.misceval.callsimArray(e.getBlue,[e])),a.imagedata.data[i+3]=255,d(a,g,h)};b.set_pixel_at=new Sk.builtin.func(m),b.setPixelAt=new Sk.builtin.func(m);var n=function(a,b){var c,e,g;return Sk.builtin.pyCheckArgsLen(\"updatepixel\",arguments.length,2,2),c=Sk.builtin.asnum$(Sk.misceval.callsimArray(b.getX,[b])),e=Sk.builtin.asnum$(Sk.misceval.callsimArray(b.getY,[b])),f(a,c,e),g=4*e*a.width+4*c,a.imagedata.data[g]=Sk.builtin.asnum$(Sk.misceval.callsimArray(b.getRed,[b])),a.imagedata.data[g+1]=Sk.builtin.asnum$(Sk.misceval.callsimArray(b.getGreen,[b])),a.imagedata.data[g+2]=Sk.builtin.asnum$(Sk.misceval.callsimArray(b.getBlue,[b])),a.imagedata.data[g+3]=255,d(a,c,e)};b.update_pixel=new Sk.builtin.func(n),b.updatePixel=new Sk.builtin.func(n);var o=function(a){return Sk.builtin.pyCheckArgsLen(\"getheight\",arguments.length,1,1),new Sk.builtin.int_(a.height)};b.get_height=new Sk.builtin.func(o),b.getHeight=new Sk.builtin.func(o);var p=function(a){return Sk.builtin.pyCheckArgsLen(\"getwidth\",arguments.length,1,1),new Sk.builtin.int_(a.width)};b.get_width=new Sk.builtin.func(p),b.getWidth=new Sk.builtin.func(p),b.__getattr__=new Sk.builtin.func(function(a,b){return(b=Sk.ffi.remapToJs(b),\"height\"===b)?Sk.builtin.assk$(a.height):\"width\"===b?Sk.builtin.assk$(a.width):void 0}),b.__setattr__=new Sk.builtin.func(function(a,b){if(b=Sk.ffi.remapToJs(b),\"height\"===b||\"width\"===b)throw new Sk.builtin.Exception(\"Cannot change height or width they can only be set on creation\");else throw new Sk.builtin.Exception(\"Unknown attribute: \"+b)}),b.draw=new Sk.builtin.func(function(a,b,c,d){var e;return Sk.builtin.pyCheckArgsLen(\"draw\",arguments.length,2,4),e=new Sk.misceval.Suspension,e.resume=function(){return Sk.builtin.none.none$},e.data={type:\"Sk.promise\",promise:new Promise(function(e){var f,g;b=Sk.builtin.asnum$(b),c=Sk.builtin.asnum$(c),d=Sk.builtin.asnum$(d),f=Sk.misceval.callsimArray(b.getWin,[b]),g=f.getContext(\"2d\"),void 0===c&&(c=0,d=0),a.lastUlx=c,a.lastUly=d,a.lastCtx=g,g.putImageData(a.imagedata,c,d),0<a.delay?window.setTimeout(e,a.delay):window.setTimeout(e,200)})},e})},\"Image\",[]),c=function(a,b){b.__init__=new Sk.builtin.func(function(a,b,c){Sk.builtin.pyCheckArgsLen(\"__init__\",arguments.length,3,3),a.width=Sk.builtin.asnum$(b),a.height=Sk.builtin.asnum$(c),a.canvas=document.createElement(\"canvas\"),a.ctx=a.canvas.getContext(\"2d\"),a.canvas.height=a.height,a.canvas.width=a.width,a.imagedata=a.ctx.getImageData(0,0,a.width,a.height)})},h.EmptyImage=Sk.misceval.buildClass(h,c,\"EmptyImage\",[h.Image]),b=function(a,b){b.__init__=new Sk.builtin.func(function(a,c,d,e,b,f){Sk.builtin.pyCheckArgsLen(\"__init__\",arguments.length,4,6),a.red=Sk.builtin.asnum$(c),a.green=Sk.builtin.asnum$(d),a.blue=Sk.builtin.asnum$(e),a.x=Sk.builtin.asnum$(b),a.y=Sk.builtin.asnum$(f)});var c=function(a){return Sk.builtin.pyCheckArgsLen(\"getred\",arguments.length,1,1),Sk.builtin.assk$(a.red)};b.get_red=new Sk.builtin.func(c),b.getRed=new Sk.builtin.func(c);var d=function(a){return Sk.builtin.pyCheckArgsLen(\"getgreen\",arguments.length,1,1),Sk.builtin.assk$(a.green)};b.get_green=new Sk.builtin.func(d),b.getGreen=new Sk.builtin.func(d);var e=function(a){return Sk.builtin.pyCheckArgsLen(\"getblue\",arguments.length,1,1),Sk.builtin.assk$(a.blue)};b.get_blue=new Sk.builtin.func(e),b.getBlue=new Sk.builtin.func(e);var f=function(a){return Sk.builtin.pyCheckArgsLen(\"getx\",arguments.length,1,1),Sk.builtin.assk$(a.x)};b.get_x=new Sk.builtin.func(f),b.getX=new Sk.builtin.func(f);var g=function(a){return Sk.builtin.pyCheckArgsLen(\"gety\",arguments.length,1,1),Sk.builtin.assk$(a.y)};b.get_y=new Sk.builtin.func(g),b.getY=new Sk.builtin.func(g);var h=function(a,b){Sk.builtin.pyCheckArgsLen(\"setred\",arguments.length,2,2),a.red=Sk.builtin.asnum$(b)};b.set_red=new Sk.builtin.func(h),b.setRed=new Sk.builtin.func(h);var i=function(a,b){Sk.builtin.pyCheckArgsLen(\"setgreen\",arguments.length,2,2),a.green=Sk.builtin.asnum$(b)};b.set_green=new Sk.builtin.func(i),b.setGreen=new Sk.builtin.func(i);var j=function(a,c){Sk.builtin.pyCheckArgsLen(\"setblue\",arguments.length,2,2),a.blue=Sk.builtin.asnum$(c)};b.set_blue=new Sk.builtin.func(j),b.setBlue=new Sk.builtin.func(j),b.__getattr__=new Sk.builtin.func(function(a,b){return(b=Sk.ffi.remapToJs(b),\"red\"===b)?Sk.builtin.assk$(a.red):\"green\"===b?Sk.builtin.assk$(a.green):\"blue\"===b?Sk.builtin.assk$(a.blue):void 0}),b.__setattr__=new Sk.builtin.func(function(a,b,c){b=Sk.ffi.remapToJs(b),(\"red\"===b||\"green\"===b||\"blue\"===b)&&(a[b]=Sk.builtin.asnum$(c))});var k=function(a,b){Sk.builtin.pyCheckArgsLen(\"setx\",arguments.length,2,2),a.x=Sk.builtin.asnum$(b)};b.set_x=new Sk.builtin.func(k),b.setX=new Sk.builtin.func(k);var l=function(a,b){Sk.builtin.pyCheckArgsLen(\"sety\",arguments.length,2,2),a.y=Sk.builtin.asnum$(b)};b.set_y=new Sk.builtin.func(l),b.setY=new Sk.builtin.func(l),b.__getitem__=new Sk.builtin.func(function(a,b){return(b=Sk.builtin.asnum$(b),0===b)?a.red:1==b?a.green:2==b?a.blue:void 0}),b.__str__=new Sk.builtin.func(function(a){return Sk.ffi.remapToPy(\"[\"+a.red+\",\"+a.green+\",\"+a.blue+\"]\")}),b.getColorTuple=new Sk.builtin.func(function(){}),b.setRange=new Sk.builtin.func(function(a,b){a.max=Sk.builtin.asnum$(b)})},h.Pixel=Sk.misceval.buildClass(h,b,\"Pixel\",[]),a=function(a,b){b.__init__=new Sk.builtin.func(function(a,b,c){var d,e,f;Sk.builtin.pyCheckArgsLen(\"__init__\",arguments.length,1,3),d=ImageMod.canvasLib[Sk.canvas],void 0===d?(e=document.createElement(\"canvas\"),f=document.getElementById(Sk.canvas),a.theScreen=e,f.appendChild(e),ImageMod.canvasLib[Sk.canvas]=e,ImageMod.canvasLib[Sk.canvas]=a.theScreen):(a.theScreen=d,a.theScreen.height=a.theScreen.height),void 0===b?(Sk.availableHeight&&(a.theScreen.height=Sk.availableHeight),Sk.availableWidth&&(a.theScreen.width=Sk.availableWidth)):(a.theScreen.height=c.v,a.theScreen.width=b.v),a.theScreen.style.display=\"block\"}),b.getWin=new Sk.builtin.func(function(a){return a.theScreen}),b.exitonclick=new Sk.builtin.func(function(a){var b=a.theScreen.id;a.theScreen.onclick=function(){document.getElementById(b).style.display=\"none\",document.getElementById(b).onclick=null,delete ImageMod.canvasLib[b]}})},h.ImageWin=Sk.misceval.buildClass(h,a,\"ImageWin\",[]),h};","src/lib/imaplib.py":"raise NotImplementedError(\"imaplib is not yet implemented in Skulpt\")\n","src/lib/imghdr.py":"raise NotImplementedError(\"imghdr is not yet implemented in Skulpt\")\n","src/lib/imputil.py":"raise NotImplementedError(\"imputil is not yet implemented in Skulpt\")\n","src/lib/io.py":"raise NotImplementedError(\"io is not yet implemented in Skulpt\")\n","src/lib/itertools.js":"Sk.builtin.itertools_gen=function(a,b,c,d,e){Sk.builtin.generator.call(this,a,b,c,d,e)},Sk.builtin.itertools_gen.prototype=Object.create(Sk.builtin.generator.prototype),Sk.builtin.itertools_gen.prototype.$r=function(){return new Sk.builtin.str(\"<itertools.\"+this.func_code.co_name.v+\" object>\")},Sk.builtin.itertools_repeat=function(a,b,c,d,e){Sk.builtin.generator.call(this,a,b,c,d,e)},Sk.builtin.itertools_repeat.prototype=Object.create(Sk.builtin.generator.prototype),Sk.builtin.itertools_repeat.prototype.$r=function(){return object_repr=this.gi$locals.object.$r().$jsstr(),times=this.gi$locals.times,times_repr=void 0===times?\"\":\", \"+times,new Sk.builtin.str(this.func_code.co_name.v+\"(\"+object_repr+times_repr+\")\")},Sk.builtin.itertools_count=function(a,b,c,d,e){Sk.builtin.generator.call(this,a,b,c,d,e)},Sk.builtin.itertools_count.prototype=Object.create(Sk.builtin.generator.prototype),Sk.builtin.itertools_count.prototype.$r=function(){return start_repr=this.gi$locals.n.$r().$jsstr(),step_repr=this.gi$locals.step.$r().$jsstr(),step_repr=\"1\"===step_repr?\"\":\", \"+step_repr,new Sk.builtin.str(this.func_code.co_name.v+\"(\"+start_repr+step_repr+\")\")};var $builtinmodule=function(){var a={},b=function(a){const b=a.gi$locals.it,c=a.gi$locals.func;let d=a.gi$locals.total;const e=a.gi$locals.initial;return e?(d=Sk.builtin.checkNone(d)?b.tp$iternext():d,a.gi$locals.total=d,a.gi$locals.initial=!1,[,d]):(element=b.tp$iternext(),void 0===element?[,]:(d=c.tp$call?c.tp$call([d,element],void 0):Sk.misceval.applyOrSuspend(c,void 0,void 0,void 0,[d,element]),a.gi$locals.total=d,[,d]))},c=function(c,d,e){Sk.builtin.pyCheckArgsLen(\"accumulate\",arguments.length,1,3,!0);const f=Sk.abstr.iter(c);return new Sk.builtin.itertools_gen(b,a,[f,d,e,!0])};const d=new Sk.builtin.func(function(c,a){return Sk.abstr.numberBinOp(c,a,\"Add\")});b.co_name=new Sk.builtin.str(\"accumulate\"),b.co_varnames=[\"it\",\"func\",\"total\",\"initial\"],c.$defaults=[d],c.co_name=new Sk.builtin.str(\"accumulate\"),c.co_argcount=2,c.co_kwonlyargcount=1,c.$kwdefs=[Sk.builtin.none.none$],c.co_varnames=[\"iterable\",\"func\",\"initial\"],a.accumulate=new Sk.builtin.func(c),_chain_gen=function(a){const b=a.gi$locals.iterables;let c,d=a.gi$locals.current_it,e=a.gi$locals.made_iter;for(;void 0===c;){if(void 0===d)return[,];e||(d=Sk.abstr.iter(d),e=!0),c=d.tp$iternext(),void 0===c&&(d=b.tp$iternext(),e=!1)}return a.gi$locals.current_it=d,a.gi$locals.made_iter=e,[,c]},_chain=function(){let b=Array.from(arguments);b=Sk.abstr.iter(Sk.builtin.list(b));const c=b.tp$iternext();return new Sk.builtin.itertools_gen(_chain_gen,a,[b,c])},_chain_from_iterable=function(b){Sk.builtin.pyCheckArgsLen(\"from_iterable\",arguments.length,1,1),b=Sk.abstr.iter(b);const c=b.tp$iternext();return new Sk.builtin.itertools_gen(_chain_gen,a,[b,c])},Sk.builtin.chain_func=function(a){Sk.builtin.func.call(this,a),this.$d.from_iterable=new Sk.builtin.func(_chain_from_iterable)},Sk.builtin.chain_func.prototype=Object.create(Sk.builtin.func.prototype),_chain_from_iterable.co_name=new Sk.builtin.str(\"from_iterable\"),_chain.co_name=new Sk.builtin.str(\"chain\"),_chain_gen.co_name=new Sk.builtin.str(\"chain\"),_chain_gen.co_varnames=[\"iterables\",\"current_it\"],a.chain=new Sk.builtin.chain_func(_chain);var e=function(a){const b=a.gi$locals.indices,c=a.gi$locals.pool,d=a.gi$locals.n,e=a.gi$locals.r,f=a.gi$locals.initial;if(e>d)return[,];if(void 0===f)return a.gi$locals.initial=!1,[,new Sk.builtin.tuple(c.slice(0,e))];let g,h=!1;for(g=e-1;0<=g;g--)if(b[g]!=g+d-e){h=!0;break}if(!h)return a.gi$locals.r=0,[,];b[g]++;for(let c=g+1;c<e;c++)b[c]=b[c-1]+1;const k=b.map(a=>c[a]);return[,new Sk.builtin.tuple(k)]},f=function(b,c){Sk.builtin.pyCheckArgsLen(\"combinations\",arguments.length,2,2);const d=Sk.misceval.arrayFromIterable(b);Sk.builtin.pyCheckType(\"r\",\"int\",Sk.builtin.checkInt(c));const f=d.length;if(c=Sk.builtin.asnum$(c),0>c)throw new Sk.builtin.ValueError(\"r must be non-negative\");const g=Array(c).fill().map((a,b)=>b);return new Sk.builtin.itertools_gen(e,a,[g,d,f,c])};e.co_name=new Sk.builtin.str(\"combinations\"),e.co_varnames=[\"indices\",\"pool\",\"n\",\"r\"],f.co_name=new Sk.builtin.str(\"combinations\"),f.co_varnames=[\"iterable\",\"r\"],a.combinations=new Sk.builtin.func(f);var g=function(a){const b=a.gi$locals.indices,c=a.gi$locals.pool,d=a.gi$locals.n,e=a.gi$locals.r,f=a.gi$locals.initial;if(e&&!d)return[,];if(void 0===f){const d=b.map(a=>c[a]);return a.gi$locals.initial=!1,[,new Sk.builtin.tuple(d)]}let g,h=!1;for(g=e-1;0<=g;g--)if(b[g]!=d-1){h=!0;break}if(!h)return a.gi$locals.r=0,[,];const k=b[g]+1;for(let c=g;c<e;c++)b[c]=k;const l=b.map(a=>c[a]);return[,new Sk.builtin.tuple(l)]},h=function(b,c){Sk.builtin.pyCheckArgsLen(\"combinations\",arguments.length,2,2);const d=Sk.misceval.arrayFromIterable(b);Sk.builtin.pyCheckType(\"r\",\"int\",Sk.builtin.checkInt(c));const e=d.length;if(c=Sk.builtin.asnum$(c),0>c)throw new Sk.builtin.ValueError(\"r must be non-negative\");const f=Array(c).fill(0);return new Sk.builtin.itertools_gen(g,a,[f,d,e,c])};g.co_name=new Sk.builtin.str(\"combinations_with_replacement\"),g.co_varnames=[\"indices\",\"pool\",\"n\",\"r\"],h.co_name=new Sk.builtin.str(\"combinations_with_replacement\"),h.co_varnames=[\"iterable\",\"r\"],a.combinations_with_replacement=new Sk.builtin.func(h),_compress_gen=function(a){const b=a.gi$locals.data,c=a.gi$locals.selectors;let e=b.tp$iternext(),f=c.tp$iternext();for(;void 0!==e&&void 0!==f;){if(Sk.misceval.isTrue(f))return[,e];e=b.tp$iternext(),f=c.tp$iternext()}return[,]},_compress=function(b,c){return Sk.builtin.pyCheckArgsLen(\"compress\",arguments.length,2,2),b=Sk.abstr.iter(b),c=Sk.abstr.iter(c),new Sk.builtin.itertools_gen(_compress_gen,a,[b,c])},_compress_gen.co_name=new Sk.builtin.str(\"compress\"),_compress_gen.co_varnames=[\"data\",\"selectors\"],_compress.co_name=new Sk.builtin.str(\"compress\"),_compress.co_varnames=[\"data\",\"selectors\"],a.compress=new Sk.builtin.func(_compress);var i=function(a){const b=a.gi$locals.n,c=a.gi$locals.step;try{return[,b]}finally{a.gi$locals.n=Sk.abstr.numberInplaceBinOp(b,c,\"Add\")}},k=function(b,c){if(Sk.builtin.pyCheckArgsLen(\"count\",arguments.length,0,2),!Sk.builtin.checkNumber(b)&&!Sk.builtin.checkComplex(b))throw new Sk.builtin.TypeError(\"a number is required\");if(!Sk.builtin.checkNumber(c)&&!Sk.builtin.checkComplex(c))throw new Sk.builtin.TypeError(\"a number is required\");return new Sk.builtin.itertools_count(i,a,[b,c])};k.co_name=new Sk.builtin.str(\"count\"),k.$defaults=[new Sk.builtin.int_(0),new Sk.builtin.int_(1)],k.co_varnames=[\"start\",\"step\"],i.co_name=new Sk.builtin.str(\"count\"),i.co_varnames=[\"n\",\"step\"],a.count=new Sk.builtin.func(k);var l=function(a){let b,c,d;return b=a.gi$locals.iter,c=a.gi$locals.saved,d=b.tp$iternext(),void 0===d?c.length?(d=c.shift(),c.push(d),[,d]):[,]:(c.push(d),[,d])},m=function(b){Sk.builtin.pyCheckArgsLen(\"cycle\",arguments.length,1,1),b=Sk.abstr.iter(b);return new Sk.builtin.itertools_gen(l,a,[b,[]])};m.co_name=new Sk.builtin.str(\"cycle\"),l.co_name=new Sk.builtin.str(\"cycle\"),l.co_varnames=[\"iter\",\"saved\"],a.cycle=new Sk.builtin.func(m);var n=function(a){const b=a.gi$locals.predicate,c=a.gi$locals.it,d=a.gi$locals.passed;let e=c.tp$iternext();for(;void 0===d&&void 0!==e;){const d=b.tp$call?b.tp$call([e],void 0):Sk.misceval.applyOrSuspend(b,void 0,void 0,void 0,[e]);if(!Sk.misceval.isTrue(d))return a.gi$locals.passed=!0,[,e];e=c.tp$iternext()}return[,e]};_dropwhile=function(b,c){Sk.builtin.pyCheckArgsLen(\"dropwhile\",arguments.length,2,2);const d=Sk.abstr.iter(c);return new Sk.builtin.itertools_gen(n,a,[b,d])},n.co_name=new Sk.builtin.str(\"dropwhile\"),n.co_varnames=[\"predicate\",\"it\"],_dropwhile.co_name=new Sk.builtin.str(\"dropwhile\"),_dropwhile.co_varnames=[\"predicate\",\"iterable\"],a.dropwhile=new Sk.builtin.func(_dropwhile);var o=function(a){const b=a.gi$locals.predicate;let c=a.gi$locals.it;const d=a.gi$locals.initial;void 0===d&&(a.gi$locals.it=Sk.abstr.iter(c),c=a.gi$locals.it,a.gi$locals.initial=!1);let e=c.tp$iternext();if(void 0===e)return[,];for(let d=b.tp$call?b.tp$call([e],void 0):Sk.misceval.applyOrSuspend(b,void 0,void 0,void 0,[e]);Sk.misceval.isTrue(d);){if(e=c.tp$iternext(),void 0===e)return[,];d=b.tp$call?b.tp$call([e],void 0):Sk.misceval.applyOrSuspend(b,void 0,void 0,void 0,[e])}return[,e]};_filterfalse=function(b,c){if(Sk.builtin.pyCheckArgsLen(\"filterfalse\",arguments.length,2,2),!Sk.builtin.checkIterable(c))throw new Sk.builtin.TypeError(\"'\"+Sk.abstr.typeName(c)+\"' object is not iterable\");return b=Sk.builtin.checkNone(b)?Sk.builtin.bool:b,new Sk.builtin.itertools_gen(o,a,[b,c])},o.co_name=new Sk.builtin.str(\"filterfalse\"),o.co_varnames=[\"predicate\",\"it\"],_filterfalse.co_name=new Sk.builtin.str(\"filterfalse\"),_filterfalse.co_varnames=[\"predicate\",\"iterable\"],a.filterfalse=new Sk.builtin.func(_filterfalse),_grouper=function(a){const b=a.gi$locals.groupby_gen,c=a.gi$locals.tgtkey,d=a.gi$locals.id;let e=b.gi$locals.currval,f=b.gi$locals.currkey;const g=b.gi$locals.keyfunc,h=b.gi$locals.it,i=b.gi$locals.id,k=Sk.misceval.richCompareBool(f,c,\"Eq\",!0);if(i===d&&k)try{return[,e]}finally{e=h.tp$iternext(),void 0!==e&&(f=g.tp$call?g.tp$call([e],void 0):Sk.misceval.applyOrSuspend(g,void 0,void 0,void 0,[e])),b.gi$locals.currkey=f,b.gi$locals.currval=e}return[,]},_groupby_gen=function(b){const c=b.gi$locals.tgtkey;let d=b.gi$locals.currval,e=b.gi$locals.currkey;const f=b.gi$locals.keyfunc,g=b.gi$locals.it;b.gi$locals.id={};for(let a=Sk.misceval.richCompareBool(e,c,\"Eq\",!0);a;){if(d=g.tp$iternext(),void 0===d)return[,];e=f.tp$call?f.tp$call([d],void 0):Sk.misceval.applyOrSuspend(f,void 0,void 0,void 0,[d]),a=Sk.misceval.richCompareBool(e,c,\"Eq\",!0)}b.gi$locals.tgtkey=b.gi$locals.currkey=e,b.gi$locals.currval=d;const h=new Sk.builtin.itertools_gen(_grouper,a,[b,b.gi$locals.tgtkey,b.gi$locals.id]);return[,new Sk.builtin.tuple([e,h])]},_groupby=function(b,c){Sk.builtin.pyCheckArgsLen(\"groupby\",arguments.length,1,2),b=Sk.abstr.iter(b),Sk.builtin.checkNone(c)&&(c=new Sk.builtin.func(function(a){return a}));const d=currkey=tgtkey=Sk.builtin.object();return new Sk.builtin.itertools_gen(_groupby_gen,a,[b,c,d,currkey,tgtkey])},_groupby_gen.co_name=new Sk.builtin.str(\"groupby\"),_groupby_gen.co_varnames=[\"it\",\"keyfunc\",\"currval\",\"currkey\",\"tgtkey\"],_groupby.co_name=new Sk.builtin.str(\"groupby\"),_groupby.$defaults=[Sk.builtin.none.none$],_groupby.co_varnames=[\"iterable\",\"key\"],_grouper.co_name=new Sk.builtin.str(\"_grouper\"),_grouper.co_varnames=[\"groupby_gen\",\"tgtkey\",\"id\"],a.groupby=new Sk.builtin.func(_groupby);var p=function(a){let b,c,d,e,f;if(b=a.gi$locals.iter,c=a.gi$locals.previt,d=a.gi$locals.stop,e=a.gi$locals.step,f=a.gi$locals.initial,void 0===f){if(a.gi$locals.initial=!1,c>=d){for(let a=0;a<d;a++)b.tp$iternext();return[,]}for(let a=0;a<c;a++)b.tp$iternext();return[,b.tp$iternext()]}if(c+e>=d){for(let f=c+1;f<d;f++)a.gi$locals.previt=c+e,b.tp$iternext();return[,]}for(let d=c+1;d<c+e;d++)b.tp$iternext();return a.gi$locals.previt=c+e,[,b.tp$iternext()]},q=function(b,c,d,e){var f=Number.MAX_SAFE_INTEGER;if(Sk.builtin.pyCheckArgsLen(\"islice\",arguments.length,2,4),b=Sk.abstr.iter(b),void 0===d?(d=c,c=Sk.builtin.none.none$,e=Sk.builtin.none.none$):void 0===e&&(e=Sk.builtin.none.none$),!(Sk.builtin.checkNone(d)||Sk.misceval.isIndex(d)))throw new Sk.builtin.ValueError(\"Stop for islice() must be None or an integer: 0 <= x <= sys.maxsize.\");else if(d=Sk.builtin.checkNone(d)?f:Sk.misceval.asIndex(d),0>d||d>f)throw new Sk.builtin.ValueError(\"Stop for islice() must be None or an integer: 0 <= x <= sys.maxsize.\");if(!(Sk.builtin.checkNone(c)||Sk.misceval.isIndex(c)))throw new Sk.builtin.ValueError(\"Indices for islice() must be None or an integer: 0 <= x <= sys.maxsize.\");else if(c=Sk.builtin.checkNone(c)?0:Sk.misceval.asIndex(c),0>c||c>f)throw new Sk.builtin.ValueError(\"Indices for islice() must be None or an integer: 0 <= x <= sys.maxsize.\");if(!(Sk.builtin.checkNone(e)||Sk.misceval.isIndex(e)))throw new Sk.builtin.ValueError(\"Step for islice() must be a positive integer or None\");else if(e=Sk.builtin.checkNone(e)?1:Sk.misceval.asIndex(e),0>=e||e>f)throw new Sk.builtin.ValueError(\"Step for islice() must be a positive integer or None.\");const g=c;return new Sk.builtin.itertools_gen(p,a,[b,g,d,e])};p.co_varnames=[\"iter\",\"previt\",\"stop\",\"step\"],p.co_name=new Sk.builtin.str(\"islice\"),q.co_name=new Sk.builtin.str(\"islice\"),a.islice=new Sk.builtin.func(q);var s=function(a){const b=a.gi$locals.indices,c=a.gi$locals.cycles,d=a.gi$locals.pool,e=a.gi$locals.n,f=a.gi$locals.r,g=a.gi$locals.initial;if(f>e)return[,];if(void 0===g)return a.gi$locals.initial=!1,[,new Sk.builtin.tuple(d.slice(0,f))];for(let g=f-1;0<=g;g--)if(c[g]--,0==c[g])b.push(b.splice(g,1)[0]),c[g]=e-g;else{j=c[g],[b[g],b[e-j]]=[b[e-j],b[g]];const a=b.map(a=>d[a]).slice(0,f);return[,new Sk.builtin.tuple(a)]}return a.gi$locals.r=0,[,]},t=function(b,c){Sk.builtin.pyCheckArgsLen(\"permutations\",arguments.length,1,2);const d=Sk.misceval.arrayFromIterable(b),e=d.length;if(c=Sk.builtin.checkNone(c)?new Sk.builtin.int_(e):c,Sk.builtin.pyCheckType(\"r\",\"int\",Sk.builtin.checkInt(c)),c=Sk.builtin.asnum$(c),0>c)throw new Sk.builtin.ValueError(\"r must be non-negative\");const f=Array(e).fill().map((a,b)=>b),g=Array(c).fill().map((a,b)=>e-b);return new Sk.builtin.itertools_gen(s,a,[f,g,d,e,c])};s.co_name=new Sk.builtin.str(\"permutations\"),s.co_varnames=[\"indices\",\"cycles\",\"pool\",\"n\",\"r\"],t.co_name=new Sk.builtin.str(\"permutations\"),t.co_varnames=[\"iterable\",\"r\"],t.$defaults=[Sk.builtin.none.none$],a.permutations=new Sk.builtin.func(t);var u=function(a){const b=a.gi$locals.pools,c=a.gi$locals.pool_sizes,d=a.gi$locals.n,e=a.gi$locals.indices,f=a.gi$locals.initial;if(void 0===f){a.gi$locals.initial=!1;const c=e.map((a,c)=>b[c][e[c]]);return c.some(a=>void 0===a)?(a.gi$locals.n=0,[,]):[,new Sk.builtin.tuple(c)]}for(let b=d-1;0<=b&&b<d;)e[b]++,e[b]>=c[b]?(e[b]=-1,b--):b++;if(!d||e.every(a=>-1===a))return a.gi$locals.n=0,[,];else{const a=e.map((a,c)=>b[c][e[c]]);return[,new Sk.builtin.tuple(a)]}},v=function(b,c){if(Sk.builtin.pyCheckArgsLen(\"product\",arguments.length,0,1/0,!0),Sk.builtin.pyCheckType(\"repeat\",\"integer\",Sk.builtin.checkInt(b)),b=Sk.builtin.asnum$(b),0>b)throw new Sk.builtin.ValueError(\"repeat argument cannot be negative\");c=c.v;for(let a=0;a<c.length;a++){if(!Sk.builtin.checkIterable(c[a]))throw new Sk.builtin.TypeError(\"'\"+Sk.abstr.typeName(c[a])+\"' object is not iterable\");c[a]=Sk.misceval.arrayFromIterable(c[a])}const d=[].concat(...Array(b).fill(c)),e=d.length,f=d.map(a=>a.length),g=Array(e).fill(0);return new Sk.builtin.itertools_gen(u,a,[d,f,e,g])};u.co_name=new Sk.builtin.str(\"product\"),u.co_varnames=[\"pools\",\"pool_sizes\",\"n\",\"indices\"],v.co_name=new Sk.builtin.str(\"product\"),v.co_kwonlyargcount=1,v.co_argcount=0,v.co_varnames=[\"repeat\"],v.co_varargs=1,v.$kwdefs=[new Sk.builtin.int_(1)],a.product=new Sk.builtin.func(v);var w=function(a){const b=a.gi$locals.times,c=a.gi$locals.object;return void 0===b?[,c]:0<b?(a.gi$locals.times=b-1,[,c]):[,]},x=function(b,c){return Sk.builtin.pyCheckArgsLen(\"repeat\",arguments.length,1,2),Sk.builtin.checkNone(c)?c=void 0:(Sk.builtin.pyCheckType(\"times\",\"integer\",Sk.builtin.checkInt(c)),c=Sk.builtin.asnum$(c),c=0>c?0:c),new Sk.builtin.itertools_repeat(w,a,[b,c])};w.co_varnames=[\"object\",\"times\"],w.co_name=new Sk.builtin.str(\"repeat\"),x.co_varnames=[\"object\",\"times\"],x.co_name=new Sk.builtin.str(\"repeat\"),x.$defaults=[Sk.builtin.none.none$],a.repeat=new Sk.builtin.func(x);var y=function(a){const b=a.gi$locals.func,c=a.gi$locals.it,d=[],e=c.tp$iternext();if(void 0===e)return[,];Sk.misceval.iterFor(Sk.abstr.iter(e),function(a){d.push(a)});const f=b.tp$call?b.tp$call(d,void 0):Sk.misceval.applyOrSuspend(b,void 0,void 0,void 0,d);return[,f]};_starmap=function(b,c){return Sk.builtin.pyCheckArgsLen(\"starmap\",arguments.length,2,2),it=Sk.abstr.iter(c),b=Sk.builtin.checkNone(b)?Sk.builtin.bool:b,new Sk.builtin.itertools_gen(y,a,[b,it])},y.co_name=new Sk.builtin.str(\"starmap\"),y.co_varnames=[\"func\",\"it\"],_starmap.co_name=new Sk.builtin.str(\"starmap\"),_starmap.co_varnames=[\"func\",\"iterable\"],a.starmap=new Sk.builtin.func(_starmap);var z=function(a){const b=a.gi$locals.predicate,c=a.gi$locals.it,d=a.gi$locals.failed;let e=c.tp$iternext();if(void 0===d&&void 0!==e){const c=b.tp$call?b.tp$call([e],void 0):Sk.misceval.applyOrSuspend(b,void 0,void 0,void 0,[e]);if(Sk.misceval.isTrue(c))return[,e];a.gi$locals.failed=!0}return[,]};return _takewhile=function(b,c){return Sk.builtin.pyCheckArgsLen(\"takewhile\",arguments.length,2,2),it=Sk.abstr.iter(c),new Sk.builtin.itertools_gen(z,a,[b,it])},z.co_name=new Sk.builtin.str(\"takewhile\"),z.co_varnames=[\"predicate\",\"it\"],_takewhile.co_name=new Sk.builtin.str(\"takewhile\"),_takewhile.co_varnames=[\"predicate\",\"iterable\"],a.takewhile=new Sk.builtin.func(_takewhile),a.tee=new Sk.builtin.func(function(){throw new Sk.builtin.NotImplementedError(\"tee is not yet implemented in Skulpt\")}),_zip_longest_gen=function(a){const b=a.gi$locals.its;let c=a.gi$locals.active;const d=a.gi$locals.fillvalue;if(!c)return[,];values=[];for(let e=0;e<b.length;e++){if(val=b[e].tp$iternext(),void 0===val){if(c--,a.gi$locals.active=c,!c)return[,];b[e]=x(d,Sk.builtin.none.none$),val=d}values.push(val)}return[,new Sk.builtin.tuple(values)]},_zip_longest=function(b,c){Sk.builtin.pyCheckArgsLen(\"zip_longest\",arguments.length,0,1/0),c=c.v;for(let a=0;a<c.length;a++){const b=c[a];if(!Sk.builtin.checkIterable(b))throw new Sk.builtin.TypeError(\"zip_longest argument #\"+a+\" must support iteration\");c[a]=Sk.abstr.iter(b)}const d=c.length;return new Sk.builtin.itertools_gen(_zip_longest_gen,a,[c,d,b])},_zip_longest_gen.co_name=new Sk.builtin.str(\"zip_longest\"),_zip_longest_gen.co_varnames=[\"its\",\"active\",\"fillvalue\"],_zip_longest.co_name=new Sk.builtin.str(\"zip_longest\"),_zip_longest.co_argcount=0,_zip_longest.co_kwonlyargcount=1,_zip_longest.$kwdefs=[Sk.builtin.none.none$],_zip_longest.co_varnames=[\"fillvalue\"],_zip_longest.co_varargs=1,a.zip_longest=new Sk.builtin.func(_zip_longest),a.__doc__=new Sk.builtin.str(\"An implementation of the python itertools module in Skulpt\"),a.__package__=new Sk.builtin.str(\"\"),a};","src/lib/json/__init__.js":"var $builtinmodule=function(){\"use strict\";var a={},b=function(a){Sk.builtin.pyCheckArgs(\"dumps\",arguments,1,1/0,!0,!1);var b,c,d,e=Array.prototype.slice.call(arguments,1),f=new Sk.builtins.dict(a),g=!1;return b={ascii:!0,separators:{item_separator:\", \",key_separator:\": \"}},f=Sk.ffi.remapToJs(f),c=Sk.ffi.remapToJs(e[0]),\"boolean\"==typeof f.ensure_ascii&&!1===f.ensure_ascii&&(b.ascii=!1),\"boolean\"==typeof f.sort_keys&&f.sort_keys&&(g=!0),g||(b.cmp=function(){return 0}),\"object\"==typeof f.separators&&2==f.separators.length&&(b.separators.item_separator=f.separators[0],b.separators.key_separator=f.separators[1]),f.indent&&(b.space=f.indent),f.default,d=stringify(c,b),new Sk.builtin.str(d)};b.co_kwargs=!0,a.dumps=new Sk.builtin.func(b);var c=function(a){Sk.builtin.pyCheckArgs(\"loads\",arguments,1,1/0,!0,!1);var b,c,d=Array.prototype.slice.call(arguments,1),e=new Sk.builtins.dict(a);return e=Sk.ffi.remapToJs(e),b=d[0].v,c=JSON.parse(b),Sk.ffi.remapToPy(c)};return c.co_kwargs=!0,a.loads=new Sk.builtin.func(c),a};(function(){function r(b,d,e){function o(h,i){if(!d[h]){if(!b[h]){var f=\"function\"==typeof require&&require;if(!i&&f)return f(h,!0);if(g)return g(h,!0);var c=new Error(\"Cannot find module '\"+h+\"'\");throw c.code=\"MODULE_NOT_FOUND\",c}var a=d[h]={exports:{}};b[h][0].call(a.exports,function(a){var c=b[h][1][a];return o(c||a)},a,a.exports,r,b,d,e)}return d[h].exports}for(var g=\"function\"==typeof require&&require,a=0;a<e.length;a++)o(e[a]);return o}return r})()({1:[function(a,b){var c=\"undefined\"==typeof JSON?a(\"jsonify\"):JSON;b.exports=function(a,b){b||(b={}),\"function\"==typeof b&&(b={cmp:b});var f=b.cmp&&function(c){return function(d){return function(e,a){var b={key:e,value:d[e]},f={key:a,value:d[a]};return c(b,f)}}}(b.cmp);return function stringify(a){if(\"object\"!=typeof a||null===a)return c.stringify(a);if(d(a)){for(var b=[],g=0;g<a.length;g++)b.push(stringify(a[g]));return\"[\"+b.join(\",\")+\"]\"}for(var h,j=e(a).sort(f&&f(a)),b=[],g=0;g<j.length;g++)h=j[g],b.push(stringify(h)+\":\"+stringify(a[h]));return\"{\"+b.join(\",\")+\"}\"}(a)};var d=Array.isArray||function(a){return\"[object Array]\"==={}.toString.call(a)},e=Object.keys||function(a){var b=Object.prototype.hasOwnProperty||function(){return!0},c=[];for(var d in a)b.call(a,d)&&c.push(d);return c}},{jsonify:2}],2:[function(a,b,c){c.parse=a(\"./lib/parse\"),c.stringify=a(\"./lib/stringify\")},{\"./lib/parse\":3,\"./lib/stringify\":4}],3:[function(a,b){var d,e,f,g,h={'\"':\"\\\"\",\"\\\\\":\"\\\\\",\"/\":\"/\",b:\"\\b\",f:\"\\f\",n:\"\\n\",r:\"\\r\",t:\"\\t\"},j=function(a){throw{name:\"SyntaxError\",message:a,at:d,text:f}},k=function(a){return a&&a!==e&&j(\"Expected '\"+a+\"' instead of '\"+e+\"'\"),e=f.charAt(d),d+=1,e},l=function(){var a,b=\"\";for(\"-\"===e&&(b=\"-\",k(\"-\"));\"0\"<=e&&\"9\">=e;)b+=e,k();if(\".\"===e)for(b+=\".\";k()&&\"0\"<=e&&\"9\">=e;)b+=e;if(\"e\"===e||\"E\"===e)for(b+=e,k(),(\"-\"===e||\"+\"===e)&&(b+=e,k());\"0\"<=e&&\"9\">=e;)b+=e,k();return a=+b,isFinite(a)?a:void j(\"Bad number\")},m=function(){var b,c,d,a=String.fromCharCode,f=\"\";if(\"\\\"\"===e)for(;k();){if(\"\\\"\"===e)return k(),f;if(\"\\\\\"!==e)f+=e;else if(k(),\"u\"===e){for(d=0,c=0;4>c&&(b=parseInt(k(),16),!!isFinite(b));c+=1)d=16*d+b;f+=a(d)}else if(\"string\"==typeof h[e])f+=h[e];else break}j(\"Bad string\")},n=function(){for(;e&&\" \">=e;)k()},o=function(){switch(e){case\"t\":return k(\"t\"),k(\"r\"),k(\"u\"),k(\"e\"),!0;case\"f\":return k(\"f\"),k(\"a\"),k(\"l\"),k(\"s\"),k(\"e\"),!1;case\"n\":return k(\"n\"),k(\"u\"),k(\"l\"),k(\"l\"),null;}j(\"Unexpected '\"+e+\"'\")},p=function(){var a=[];if(\"[\"===e){if(k(\"[\"),n(),\"]\"===e)return k(\"]\"),a;for(;e;){if(a.push(g()),n(),\"]\"===e)return k(\"]\"),a;k(\",\"),n()}}j(\"Bad array\")},q=function(){var a,b={};if(\"{\"===e){if(k(\"{\"),n(),\"}\"===e)return k(\"}\"),b;for(;e;){if(a=m(),n(),k(\":\"),Object.hasOwnProperty.call(b,a)&&j(\"Duplicate key \\\"\"+a+\"\\\"\"),b[a]=g(),n(),\"}\"===e)return k(\"}\"),b;k(\",\"),n()}}j(\"Bad object\")};g=function(){return n(),\"{\"===e?q():\"[\"===e?p():\"\\\"\"===e?m():\"-\"===e?l():\"0\"<=e&&\"9\">=e?l():o()},b.exports=function(a,b){var c;return f=a,d=0,e=\" \",c=g(),n(),e&&j(\"Syntax error\"),\"function\"==typeof b?function walk(a,c){var d,e,f=a[c];if(f&&\"object\"==typeof f)for(d in f)Object.prototype.hasOwnProperty.call(f,d)&&(e=walk(f,d),void 0===e?delete f[d]:f[d]=e);return b.call(a,c,f)}({\"\":c},\"\"):c}},{}],4:[function(a,b){function quote(a){return g.lastIndex=0,g.test(a)?\"\\\"\"+a.replace(g,function(b){var a=h[b];return\"string\"==typeof a?a:\"\\\\u\"+(\"0000\"+b.charCodeAt(0).toString(16)).slice(-4)})+\"\\\"\":\"\\\"\"+a+\"\\\"\"}function str(a,b){var f,g,h,j,l,m=c,n=b[a];switch(n&&\"object\"==typeof n&&\"function\"==typeof n.toJSON&&(n=n.toJSON(a)),\"function\"==typeof e&&(n=e.call(b,a,n)),typeof n){case\"string\":return quote(n);case\"number\":return isFinite(n)?n+\"\":\"null\";case\"boolean\":case\"null\":return n+\"\";case\"object\":if(!n)return\"null\";if(c+=d,l=[],\"[object Array]\"===Object.prototype.toString.apply(n)){for(j=n.length,f=0;f<j;f+=1)l[f]=str(f,n)||\"null\";return h=0===l.length?\"[]\":c?\"[\\n\"+c+l.join(\",\\n\"+c)+\"\\n\"+m+\"]\":\"[\"+l.join(\",\")+\"]\",c=m,h}if(e&&\"object\"==typeof e)for(j=e.length,f=0;f<j;f+=1)g=e[f],\"string\"==typeof g&&(h=str(g,n),h&&l.push(quote(g)+(c?\": \":\":\")+h));else for(g in n)Object.prototype.hasOwnProperty.call(n,g)&&(h=str(g,n),h&&l.push(quote(g)+(c?\": \":\":\")+h));return h=0===l.length?\"{}\":c?\"{\\n\"+c+l.join(\",\\n\"+c)+\"\\n\"+m+\"}\":\"{\"+l.join(\",\")+\"}\",c=m,h;}}var c,d,e,f=/[\\u0000\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g,g=/[\\\\\\\"\\x00-\\x1f\\x7f-\\x9f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g,h={\"\b\":\"\\\\b\",\"\t\":\"\\\\t\",\"\\n\":\"\\\\n\",\"\f\":\"\\\\f\",\"\\r\":\"\\\\r\",'\"':\"\\\\\\\"\",\"\\\\\":\"\\\\\\\\\"};b.exports=function(a,b,f){var g;if(c=\"\",d=\"\",\"number\"==typeof f)for(g=0;g<f;g+=1)d+=\" \";else\"string\"==typeof f&&(d=f);if(e=b,b&&\"function\"!=typeof b&&(\"object\"!=typeof b||\"number\"!=typeof b.length))throw new Error(\"JSON.stringify\");return str(\"\",{\"\":a})}},{}],5:[function(a,b){function defaultSetTimout(){throw new Error(\"setTimeout has not been defined\")}function defaultClearTimeout(){throw new Error(\"clearTimeout has not been defined\")}function runTimeout(a){if(c===setTimeout)return setTimeout(a,0);if((c===defaultSetTimout||!c)&&setTimeout)return c=setTimeout,setTimeout(a,0);try{return c(a,0)}catch(b){try{return c.call(null,a,0)}catch(b){return c.call(this,a,0)}}}function runClearTimeout(a){if(d===clearTimeout)return clearTimeout(a);if((d===defaultClearTimeout||!d)&&clearTimeout)return d=clearTimeout,clearTimeout(a);try{return d(a)}catch(b){try{return d.call(null,a)}catch(b){return d.call(this,a)}}}function cleanUpNextTick(){h&&f&&(h=!1,f.length?g=f.concat(g):i=-1,g.length&&drainQueue())}function drainQueue(){if(!h){var a=runTimeout(cleanUpNextTick);h=!0;for(var b=g.length;b;){for(f=g,g=[];++i<b;)f&&f[i].run();i=-1,b=g.length}f=null,h=!1,runClearTimeout(a)}}function Item(a,b){this.fun=a,this.array=b}function noop(){}var c,d,e=b.exports={};(function(){try{c=\"function\"==typeof setTimeout?setTimeout:defaultSetTimout}catch(a){c=defaultSetTimout}try{d=\"function\"==typeof clearTimeout?clearTimeout:defaultClearTimeout}catch(a){d=defaultClearTimeout}})();var f,g=[],h=!1,i=-1;e.nextTick=function(a){var b=Array(arguments.length-1);if(1<arguments.length)for(var c=1;c<arguments.length;c++)b[c-1]=arguments[c];g.push(new Item(a,b)),1!==g.length||h||runTimeout(drainQueue)},Item.prototype.run=function(){this.fun.apply(null,this.array)},e.title=\"browser\",e.browser=!0,e.env={},e.argv=[],e.version=\"\",e.versions={},e.on=noop,e.addListener=noop,e.once=noop,e.off=noop,e.removeListener=noop,e.removeAllListeners=noop,e.emit=noop,e.prependListener=noop,e.prependOnceListener=noop,e.listeners=function(){return[]},e.binding=function(){throw new Error(\"process.binding is not supported\")},e.cwd=function(){return\"/\"},e.chdir=function(){throw new Error(\"process.chdir is not supported\")},e.umask=function(){return 0}},{}],6:[function(a,b){(function(c){var d=a(\"json-stable-stringify\");c.browser?window.stringify=d:b.exports=d}).call(this,a(\"_process\"))},{_process:5,\"json-stable-stringify\":1}]},{},[6]);","src/lib/json/stringify.js":"(function(){function r(b,d,e){function o(h,i){if(!d[h]){if(!b[h]){var f=\"function\"==typeof require&&require;if(!i&&f)return f(h,!0);if(g)return g(h,!0);var c=new Error(\"Cannot find module '\"+h+\"'\");throw c.code=\"MODULE_NOT_FOUND\",c}var a=d[h]={exports:{}};b[h][0].call(a.exports,function(a){var c=b[h][1][a];return o(c||a)},a,a.exports,r,b,d,e)}return d[h].exports}for(var g=\"function\"==typeof require&&require,a=0;a<e.length;a++)o(e[a]);return o}return r})()({1:[function(a,b){var c=\"undefined\"==typeof JSON?a(\"jsonify\"):JSON;b.exports=function(a,b){b||(b={}),\"function\"==typeof b&&(b={cmp:b});var f=b.cmp&&function(c){return function(d){return function(e,a){var b={key:e,value:d[e]},f={key:a,value:d[a]};return c(b,f)}}}(b.cmp);return function stringify(a){if(\"object\"!=typeof a||null===a)return c.stringify(a);if(d(a)){for(var b=[],g=0;g<a.length;g++)b.push(stringify(a[g]));return\"[\"+b.join(\",\")+\"]\"}for(var h,j=e(a).sort(f&&f(a)),b=[],g=0;g<j.length;g++)h=j[g],b.push(stringify(h)+\":\"+stringify(a[h]));return\"{\"+b.join(\",\")+\"}\"}(a)};var d=Array.isArray||function(a){return\"[object Array]\"==={}.toString.call(a)},e=Object.keys||function(a){var b=Object.prototype.hasOwnProperty||function(){return!0},c=[];for(var d in a)b.call(a,d)&&c.push(d);return c}},{jsonify:2}],2:[function(a,b,c){c.parse=a(\"./lib/parse\"),c.stringify=a(\"./lib/stringify\")},{\"./lib/parse\":3,\"./lib/stringify\":4}],3:[function(a,b){var d,e,f,g,h={'\"':\"\\\"\",\"\\\\\":\"\\\\\",\"/\":\"/\",b:\"\\b\",f:\"\\f\",n:\"\\n\",r:\"\\r\",t:\"\\t\"},j=function(a){throw{name:\"SyntaxError\",message:a,at:d,text:f}},k=function(a){return a&&a!==e&&j(\"Expected '\"+a+\"' instead of '\"+e+\"'\"),e=f.charAt(d),d+=1,e},l=function(){var a,b=\"\";for(\"-\"===e&&(b=\"-\",k(\"-\"));\"0\"<=e&&\"9\">=e;)b+=e,k();if(\".\"===e)for(b+=\".\";k()&&\"0\"<=e&&\"9\">=e;)b+=e;if(\"e\"===e||\"E\"===e)for(b+=e,k(),(\"-\"===e||\"+\"===e)&&(b+=e,k());\"0\"<=e&&\"9\">=e;)b+=e,k();return a=+b,isFinite(a)?a:void j(\"Bad number\")},m=function(){var b,c,d,a=String.fromCharCode,f=\"\";if(\"\\\"\"===e)for(;k();){if(\"\\\"\"===e)return k(),f;if(\"\\\\\"!==e)f+=e;else if(k(),\"u\"===e){for(d=0,c=0;4>c&&(b=parseInt(k(),16),!!isFinite(b));c+=1)d=16*d+b;f+=a(d)}else if(\"string\"==typeof h[e])f+=h[e];else break}j(\"Bad string\")},n=function(){for(;e&&\" \">=e;)k()},o=function(){switch(e){case\"t\":return k(\"t\"),k(\"r\"),k(\"u\"),k(\"e\"),!0;case\"f\":return k(\"f\"),k(\"a\"),k(\"l\"),k(\"s\"),k(\"e\"),!1;case\"n\":return k(\"n\"),k(\"u\"),k(\"l\"),k(\"l\"),null;}j(\"Unexpected '\"+e+\"'\")},p=function(){var a=[];if(\"[\"===e){if(k(\"[\"),n(),\"]\"===e)return k(\"]\"),a;for(;e;){if(a.push(g()),n(),\"]\"===e)return k(\"]\"),a;k(\",\"),n()}}j(\"Bad array\")},q=function(){var a,b={};if(\"{\"===e){if(k(\"{\"),n(),\"}\"===e)return k(\"}\"),b;for(;e;){if(a=m(),n(),k(\":\"),Object.hasOwnProperty.call(b,a)&&j(\"Duplicate key \\\"\"+a+\"\\\"\"),b[a]=g(),n(),\"}\"===e)return k(\"}\"),b;k(\",\"),n()}}j(\"Bad object\")};g=function(){return n(),\"{\"===e?q():\"[\"===e?p():\"\\\"\"===e?m():\"-\"===e?l():\"0\"<=e&&\"9\">=e?l():o()},b.exports=function(a,b){var c;return f=a,d=0,e=\" \",c=g(),n(),e&&j(\"Syntax error\"),\"function\"==typeof b?function walk(a,c){var d,e,f=a[c];if(f&&\"object\"==typeof f)for(d in f)Object.prototype.hasOwnProperty.call(f,d)&&(e=walk(f,d),void 0===e?delete f[d]:f[d]=e);return b.call(a,c,f)}({\"\":c},\"\"):c}},{}],4:[function(a,b){function quote(a){return g.lastIndex=0,g.test(a)?\"\\\"\"+a.replace(g,function(b){var a=h[b];return\"string\"==typeof a?a:\"\\\\u\"+(\"0000\"+b.charCodeAt(0).toString(16)).slice(-4)})+\"\\\"\":\"\\\"\"+a+\"\\\"\"}function str(a,b){var f,g,h,j,l,m=c,n=b[a];switch(n&&\"object\"==typeof n&&\"function\"==typeof n.toJSON&&(n=n.toJSON(a)),\"function\"==typeof e&&(n=e.call(b,a,n)),typeof n){case\"string\":return quote(n);case\"number\":return isFinite(n)?n+\"\":\"null\";case\"boolean\":case\"null\":return n+\"\";case\"object\":if(!n)return\"null\";if(c+=d,l=[],\"[object Array]\"===Object.prototype.toString.apply(n)){for(j=n.length,f=0;f<j;f+=1)l[f]=str(f,n)||\"null\";return h=0===l.length?\"[]\":c?\"[\\n\"+c+l.join(\",\\n\"+c)+\"\\n\"+m+\"]\":\"[\"+l.join(\",\")+\"]\",c=m,h}if(e&&\"object\"==typeof e)for(j=e.length,f=0;f<j;f+=1)g=e[f],\"string\"==typeof g&&(h=str(g,n),h&&l.push(quote(g)+(c?\": \":\":\")+h));else for(g in n)Object.prototype.hasOwnProperty.call(n,g)&&(h=str(g,n),h&&l.push(quote(g)+(c?\": \":\":\")+h));return h=0===l.length?\"{}\":c?\"{\\n\"+c+l.join(\",\\n\"+c)+\"\\n\"+m+\"}\":\"{\"+l.join(\",\")+\"}\",c=m,h;}}var c,d,e,f=/[\\u0000\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g,g=/[\\\\\\\"\\x00-\\x1f\\x7f-\\x9f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g,h={\"\b\":\"\\\\b\",\"\t\":\"\\\\t\",\"\\n\":\"\\\\n\",\"\f\":\"\\\\f\",\"\\r\":\"\\\\r\",'\"':\"\\\\\\\"\",\"\\\\\":\"\\\\\\\\\"};b.exports=function(a,b,f){var g;if(c=\"\",d=\"\",\"number\"==typeof f)for(g=0;g<f;g+=1)d+=\" \";else\"string\"==typeof f&&(d=f);if(e=b,b&&\"function\"!=typeof b&&(\"object\"!=typeof b||\"number\"!=typeof b.length))throw new Error(\"JSON.stringify\");return str(\"\",{\"\":a})}},{}],5:[function(a,b){function defaultSetTimout(){throw new Error(\"setTimeout has not been defined\")}function defaultClearTimeout(){throw new Error(\"clearTimeout has not been defined\")}function runTimeout(a){if(c===setTimeout)return setTimeout(a,0);if((c===defaultSetTimout||!c)&&setTimeout)return c=setTimeout,setTimeout(a,0);try{return c(a,0)}catch(b){try{return c.call(null,a,0)}catch(b){return c.call(this,a,0)}}}function runClearTimeout(a){if(d===clearTimeout)return clearTimeout(a);if((d===defaultClearTimeout||!d)&&clearTimeout)return d=clearTimeout,clearTimeout(a);try{return d(a)}catch(b){try{return d.call(null,a)}catch(b){return d.call(this,a)}}}function cleanUpNextTick(){h&&f&&(h=!1,f.length?g=f.concat(g):i=-1,g.length&&drainQueue())}function drainQueue(){if(!h){var a=runTimeout(cleanUpNextTick);h=!0;for(var b=g.length;b;){for(f=g,g=[];++i<b;)f&&f[i].run();i=-1,b=g.length}f=null,h=!1,runClearTimeout(a)}}function Item(a,b){this.fun=a,this.array=b}function noop(){}var c,d,e=b.exports={};(function(){try{c=\"function\"==typeof setTimeout?setTimeout:defaultSetTimout}catch(a){c=defaultSetTimout}try{d=\"function\"==typeof clearTimeout?clearTimeout:defaultClearTimeout}catch(a){d=defaultClearTimeout}})();var f,g=[],h=!1,i=-1;e.nextTick=function(a){var b=Array(arguments.length-1);if(1<arguments.length)for(var c=1;c<arguments.length;c++)b[c-1]=arguments[c];g.push(new Item(a,b)),1!==g.length||h||runTimeout(drainQueue)},Item.prototype.run=function(){this.fun.apply(null,this.array)},e.title=\"browser\",e.browser=!0,e.env={},e.argv=[],e.version=\"\",e.versions={},e.on=noop,e.addListener=noop,e.once=noop,e.off=noop,e.removeListener=noop,e.removeAllListeners=noop,e.emit=noop,e.prependListener=noop,e.prependOnceListener=noop,e.listeners=function(){return[]},e.binding=function(){throw new Error(\"process.binding is not supported\")},e.cwd=function(){return\"/\"},e.chdir=function(){throw new Error(\"process.chdir is not supported\")},e.umask=function(){return 0}},{}],6:[function(a,b){(function(c){var d=a(\"json-stable-stringify\");c.browser?window.stringify=d:b.exports=d}).call(this,a(\"_process\"))},{_process:5,\"json-stable-stringify\":1}]},{},[6]);","src/lib/json/tests/__init__.py":"raise NotImplementedError(\"tests is not yet implemented in Skulpt\")\n","src/lib/keyword.py":"\n__all__ = [\"iskeyword\", \"kwlist\"]\n\nkwlist = [\n#--start keywords--\n        'and',\n        'as',\n        'assert',\n        'break',\n        'class',\n        'continue',\n        'def',\n        'del',\n        'elif',\n        'else',\n        'except',\n        'exec',\n        'finally',\n        'for',\n        'from',\n        'global',\n        'if',\n        'import',\n        'in',\n        'is',\n        'lambda',\n        'not',\n        'or',\n        'pass',\n        'print',\n        'raise',\n        'return',\n        'try',\n        'while',\n        'with',\n        'yield',\n#--end keywords--\n        ]\n\niskeyword = frozenset(kwlist).__contains__\n\n","src/lib/lib-dynload/__init__.py":"raise NotImplementedError(\"lib-dynload is not yet implemented in Skulpt\")\n","src/lib/lib-tk/__init__.py":"raise NotImplementedError(\"lib-tk is not yet implemented in Skulpt\")\n","src/lib/lib2to3/__init__.py":"raise NotImplementedError(\"lib2to3 is not yet implemented in Skulpt\")\n","src/lib/lib2to3/fixes/__init__.py":"raise NotImplementedError(\"fixes is not yet implemented in Skulpt\")\n","src/lib/lib2to3/pgen2/__init__.py":"raise NotImplementedError(\"pgen2 is not yet implemented in Skulpt\")\n","src/lib/lib2to3/tests/__init__.py":"raise NotImplementedError(\"tests is not yet implemented in Skulpt\")\n","src/lib/linecache.py":"raise NotImplementedError(\"linecache is not yet implemented in Skulpt\")\n","src/lib/locale.py":"raise NotImplementedError(\"locale is not yet implemented in Skulpt\")\n","src/lib/logging/__init__.py":"raise NotImplementedError(\"logging is not yet implemented in Skulpt\")\n","src/lib/macpath.py":"raise NotImplementedError(\"macpath is not yet implemented in Skulpt\")\n","src/lib/macurl2path.py":"raise NotImplementedError(\"macurl2path is not yet implemented in Skulpt\")\n","src/lib/mailbox.py":"raise NotImplementedError(\"mailbox is not yet implemented in Skulpt\")\n","src/lib/mailcap.py":"raise NotImplementedError(\"mailcap is not yet implemented in Skulpt\")\n","src/lib/markupbase.py":"raise NotImplementedError(\"markupbase is not yet implemented in Skulpt\")\n","src/lib/math.js":"var $builtinmodule=function(){var a=Math.sqrt,b=Number.MAX_SAFE_INTEGER,c=Math.log,d=Math.exp,e=Math.pow,f=Math.log2,g=Number.isFinite,h=Math.floor,i=Math.E,j=Math.PI,k=Math.abs;function get_sign(a){return a=a?0>a?-1:1:0>1/a?-1:1,a}function isclose(c,a,b,d){Sk.builtin.pyCheckArgsLen(\"isclose\",arguments.length,2,4,!0),Sk.builtin.pyCheckType(\"a\",\"number\",Sk.builtin.checkNumber(c)),Sk.builtin.pyCheckType(\"b\",\"number\",Sk.builtin.checkNumber(a)),Sk.builtin.pyCheckType(\"rel_tol\",\"number\",Sk.builtin.checkNumber(b)),Sk.builtin.pyCheckType(\"abs_tol\",\"number\",Sk.builtin.checkNumber(d));const e=Sk.builtin.asnum$(c),f=Sk.builtin.asnum$(a),g=Sk.builtin.asnum$(b),h=Sk.builtin.asnum$(d);if(0>g||0>h)throw new Sk.builtin.ValueError(\"tolerances must be non-negative\");if(e==f)return Sk.builtin.bool.true$;if(e==1/0||e==-Infinity||f==1/0||f==-Infinity)return Sk.builtin.bool.false$;const i=k(f-e),j=i<=k(g*f)||i<=k(g*e)||i<=h;return new Sk.builtin.bool(j)}var l={pi:new Sk.builtin.float_(j),e:new Sk.builtin.float_(i),tau:new Sk.builtin.float_(2*j),nan:new Sk.builtin.float_(NaN),inf:new Sk.builtin.float_(1/0),ceil:new Sk.builtin.func(function ceil(a){var b=Math.ceil;Sk.builtin.pyCheckArgsLen(\"ceil\",arguments.length,1,1),Sk.builtin.pyCheckType(\"\",\"real number\",Sk.builtin.checkNumber(a));const c=Sk.builtin.asnum$(a);return Sk.__future__.ceil_floor_int?new Sk.builtin.int_(b(c)):new Sk.builtin.float_(b(c))}),comb:new Sk.builtin.func(function(){throw new Sk.builtin.NotImplementedError(\"math.comb() is not yet implemented in Skulpt\")}),copysign:new Sk.builtin.func(function copysign(a,b){Sk.builtin.pyCheckArgsLen(\"copysign\",arguments.length,2,2),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(a)),Sk.builtin.pyCheckType(\"y\",\"number\",Sk.builtin.checkNumber(b));const c=Sk.builtin.asnum$(b),d=Sk.builtin.asnum$(a),e=get_sign(d),f=get_sign(c);return new Sk.builtin.float_(d*(e*f))}),fabs:new Sk.builtin.func(function fabs(a){Sk.builtin.pyCheckArgsLen(\"fabs\",arguments.length,1,1),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(a));let b=Sk.builtin.asnum$(new Sk.builtin.float_(a));return b=k(b),new Sk.builtin.float_(b)})};const m=18;return l.factorial=new Sk.builtin.func(function factorial(a){function bigup(a){Sk.builtin.asnum$nofloat(a);return new Sk.builtin.biginteger(a)}Sk.builtin.pyCheckArgsLen(\"factorial\",arguments.length,1,1),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(a));let b=Sk.builtin.asnum$(a);if(a=h(b),a!=b)throw new Sk.builtin.ValueError(\"factorial() only accepts integral values\");if(0>a)throw new Sk.builtin.ValueError(\"factorial() not defined for negative numbers\");let c=1;for(let b=2;b<=a&&b<=m;b++)c*=b;if(a<=m)return new Sk.builtin.int_(c);c=bigup(c);for(var d,e=19;e<=a;e++)d=bigup(e),c=c.multiply(d);return new Sk.builtin.lng(c)}),l.floor=new Sk.builtin.func(function floor(a){return Sk.builtin.pyCheckArgsLen(\"floor\",arguments.length,1,1),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(a)),Sk.__future__.ceil_floor_int?new Sk.builtin.int_(h(Sk.builtin.asnum$(a))):new Sk.builtin.float_(h(Sk.builtin.asnum$(a)))}),l.fmod=new Sk.builtin.func(function fmod(a,b){Sk.builtin.pyCheckArgsLen(\"fmod\",arguments.length,2,2),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(a)),Sk.builtin.pyCheckType(\"y\",\"number\",Sk.builtin.checkNumber(b));const c=Sk.builtin.asnum$(new Sk.builtin.float_(a)),d=Sk.builtin.asnum$(new Sk.builtin.float_(b));if((d==1/0||d==-Infinity)&&isFinite(c))return new Sk.builtin.float_(c);const e=c%d;if(isNaN(e)&&!isNaN(c)&&!isNaN(d))throw new Sk.builtin.ValueError(\"math domain error\");return new Sk.builtin.float_(e)}),l.frexp=new Sk.builtin.func(function frexp(a){var b=Math.max;Sk.builtin.pyCheckArgsLen(\"frexp\",arguments.length,1,1),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(a));const c=Sk.builtin.asnum$(a),d=[c,0];if(0!==c&&g(c)){const a=k(c);let g=b(-1023,h(f(a))+1),i=a*e(2,-g);for(;.5>i;)i*=2,g--;for(;1<=i;)i*=.5,g++;0>c&&(i=-i),d[0]=i,d[1]=g}return d[0]=new Sk.builtin.float_(d[0]),d[1]=new Sk.builtin.int_(d[1]),new Sk.builtin.tuple(d)}),l.fsum=new Sk.builtin.func(function fsum(a){if(Sk.builtin.pyCheckArgsLen(\"fsum\",arguments.length,1,1),!Sk.builtin.checkIterable(a))throw new Sk.builtin.TypeError(\"'\"+Sk.abstr.typeName(a)+\"' object is not iterable\");let b=[];a=Sk.abstr.iter(a);let c,d,e;for(let f=a.tp$iternext();void 0!==f;f=a.tp$iternext()){Sk.builtin.pyCheckType(\"\",\"real number\",Sk.builtin.checkNumber(f)),c=0,f=Sk.builtin.asnum$(new Sk.builtin.float_(f));for(let a,g=0,h=b.length;g<h;g++){if(a=b[g],k(f)<k(a)){let b=f;f=a,a=b}d=f+a,e=a-(d-f),e&&(b[c]=e,c++),f=d}b=b.slice(0,c).concat([f])}const f=b.reduce(function(c,a){return c+a},0);return new Sk.builtin.float_(f)}),l.gcd=new Sk.builtin.func(function gcd(c,a){function _gcd(c,a){return 0==a?c:_gcd(a,c%a)}function _biggcd(c,a){return 0===a.trueCompare(Sk.builtin.biginteger.ZERO)?c:_biggcd(a,c.remainder(a))}if(Sk.builtin.pyCheckArgsLen(\"gcd\",arguments.length,2,2),Sk.builtin.pyCheckType(\"a\",\"integer\",Sk.builtin.checkInt(c)),Sk.builtin.pyCheckType(\"b\",\"integer\",Sk.builtin.checkInt(a)),c instanceof Sk.builtin.lng||a instanceof Sk.builtin.lng){let b=Sk.builtin.lng(c).biginteger,d=Sk.builtin.lng(a).biginteger,e=_biggcd(b,d);return e=e.abs(),new Sk.builtin.lng(e)}else{let b=k(Sk.builtin.asnum$(c)),d=k(Sk.builtin.asnum$(a)),e=_gcd(b,d);return e=0>e?-e:e,new Sk.builtin.int_(e)}}),(isclose.co_varnames=[\"a\",\"b\",\"rel_tol\",\"abs_tol\"],isclose.co_argcount=2,isclose.co_kwonlyargcount=2,isclose.$kwdefs=[1e-9,0],l.isclose=new Sk.builtin.func(isclose),l.isfinite=new Sk.builtin.func(function isfinite(a){Sk.builtin.pyCheckArgsLen(\"isfinite\",arguments.length,1,1),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(a));const b=Sk.builtin.asnum$(a);return Sk.builtin.checkInt(a)?Sk.builtin.bool.true$:isFinite(b)?Sk.builtin.bool.true$:Sk.builtin.bool.false$}),l.isinf=new Sk.builtin.func(function isinf(a){Sk.builtin.pyCheckArgsLen(\"isinf\",arguments.length,1,1),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(a));const b=Sk.builtin.asnum$(a);return Sk.builtin.checkInt(a)?Sk.builtin.bool.false$:isFinite(b)||isNaN(b)?Sk.builtin.bool.false$:Sk.builtin.bool.true$}),l.isnan=new Sk.builtin.func(function isnan(a){Sk.builtin.pyCheckArgsLen(\"isnan\",arguments.length,1,1),Sk.builtin.pyCheckType(\"x\",\"float\",Sk.builtin.checkFloat(a));const b=Sk.builtin.asnum$(a);return isNaN(b)?Sk.builtin.bool.true$:Sk.builtin.bool.false$}),l.isqrt=new Sk.builtin.func(function issqrt(){throw new Sk.builtin.NotImplementedError(\"math.isqrt() is not yet implemented in Skulpt\")}),l.ldexp=new Sk.builtin.func(function ldexp(a,b){Sk.builtin.pyCheckArgsLen(\"ldexp\",arguments.length,2,2),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(a)),Sk.builtin.pyCheckType(\"i\",\"integer\",Sk.builtin.checkInt(b));const c=Sk.builtin.asnum$(new Sk.builtin.float_(a)),d=Sk.builtin.asnum$(b);if(c==1/0||c==-Infinity||0==c||isNaN(c))return a;const f=c*e(2,d);if(!isFinite(f))throw new Sk.builtin.OverflowError(\"math range error\");return new Sk.builtin.float_(f)}),l.modf=new Sk.builtin.func(function modf(a){Sk.builtin.pyCheckArgsLen(\"modf\",arguments.length,1,1),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(a));let b=Sk.builtin.asnum$(a);if(!isFinite(b)){if(b==1/0)return new Sk.builtin.tuple([new Sk.builtin.float_(0),new Sk.builtin.float_(b)]);if(b==-Infinity)return new Sk.builtin.tuple([new Sk.builtin.float_(-0),new Sk.builtin.float_(b)]);if(isNaN(b))return new Sk.builtin.tuple([new Sk.builtin.float_(b),new Sk.builtin.float_(b)])}const c=get_sign(b);b=k(b);const e=c*h(b),f=c*(b-h(b));return new Sk.builtin.tuple([new Sk.builtin.float_(f),new Sk.builtin.float_(e)])}),l.perm=new Sk.builtin.func(function perm(){throw new Sk.builtin.NotImplementedError(\"math.perm() is not yet implemented in Skulpt\")}),l.prod=new Sk.builtin.func(function prod(){throw new Sk.builtin.NotImplementedError(\"math.prod() is not yet implemented in Skulpt\")}),l.remainder=new Sk.builtin.func(function remainder(a,b){if(Sk.builtin.pyCheckArgsLen(\"reamainder\",arguments.length,2,2),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(a)),Sk.builtin.pyCheckType(\"y\",\"number\",Sk.builtin.checkNumber(b)),_x=Sk.builtin.asnum$(new Sk.builtin.float_(a)),_y=Sk.builtin.asnum$(new Sk.builtin.float_(b)),isFinite(_x)&&isFinite(_y)){let a,b,d,c,e;if(0==_y)throw new Sk.builtin.ValueError(\"math domain error\");if(a=k(_x),b=k(_y),c=a%b,d=b-c,c<d)e=c;else if(c>d)e=-d;else{if(c!=d)throw new Sk.builtin.AssertionError;e=c-2*(.5*(a-c)%b)}return new Sk.builtin.float_(get_sign(_x)*e)}if(isNaN(_x))return a;if(isNaN(_y))return b;if(_x==1/0||_x==-Infinity)throw new Sk.builtin.ValueError(\"math domain error\");if(_y!=1/0&&_y!=-Infinity)throw new Sk.builtin.AssertionError;return new Sk.builtin.float_(_x)}),l.trunc=new Sk.builtin.func(function trunc(a){return Sk.builtin.pyCheckArgsLen(\"trunc\",arguments.length,1,1),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(a)),Sk.builtin.checkInt(a)?a:new Sk.builtin.int_(0|Sk.builtin.asnum$(a))}),l.exp=new Sk.builtin.func(function exp(a){Sk.builtin.pyCheckArgsLen(\"exp\",arguments.length,1,1),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(a));const b=Sk.builtin.asnum$(new Sk.builtin.float_(a));if(b==1/0||b==-Infinity||isNaN(b))return new Sk.builtin.float_(d(b));const c=d(b);if(!isFinite(c))throw new Sk.builtin.OverflowError(\"math range error\");return new Sk.builtin.float_(c)}),l.expm1=new Sk.builtin.func(function expm1(a){Sk.builtin.pyCheckArgsLen(\"expm1\",arguments.length,1,1),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(a));const b=Sk.builtin.asnum$(a);if(.7>k(b)){const a=d(b);if(1==a)return new Sk.builtin.float_(b);else{const d=(a-1)*b/c(a);return new Sk.builtin.float_(d)}}else{const a=d(b)-1;return new Sk.builtin.float_(a)}}),l.log=new Sk.builtin.func(function log(a,d){Sk.builtin.pyCheckArgsLen(\"log\",arguments.length,1,2),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(a));let e,f,g=Sk.builtin.asnum$(a);if(0>=g)throw new Sk.builtin.ValueError(\"math domain error\");if(void 0===d?e=i:(Sk.builtin.pyCheckType(\"base\",\"number\",Sk.builtin.checkNumber(d)),e=Sk.builtin.asnum$(d)),0>=e)throw new Sk.builtin.ValueError(\"math domain error\");else if(Sk.builtin.checkFloat(a)||g<b)f=c(g)/c(e);else{g=new Sk.builtin.str(a).$jsstr();const b=g.length,d=parseFloat(\"0.\"+g);f=(b*c(10)+c(d))/c(e)}return new Sk.builtin.float_(f)}),l.log1p=new Sk.builtin.func(function log1p(a){var b=Number.EPSILON;Sk.builtin.pyCheckArgsLen(\"log1p\",arguments.length,1,1),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(a));const d=Sk.builtin.asnum$(new Sk.builtin.float_(a));if(-1>=d)throw new Sk.builtin.ValueError(\"math domain error\");else{if(0==d)return new Sk.builtin.float_(d);if(k(d)<b/2)return new Sk.builtin.float_(d);if(-.5<=d&&1>=d){const a=1+d,b=c(a)-(a-1-d)/a;return new Sk.builtin.float_(b)}else{const a=c(1+d);return new Sk.builtin.float_(a)}}}),l.log2=new Sk.builtin.func(function log2(a){Sk.builtin.pyCheckArgsLen(\"log2\",arguments.length,1,1),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(a));let c,d=Sk.builtin.asnum$(a);if(0>d)throw new Sk.builtin.ValueError(\"math domain error\");else if(Sk.builtin.checkFloat(a)||d<b)c=f(d);else{d=new Sk.builtin.str(a).$jsstr();const b=d.length,e=parseFloat(\"0.\"+d);c=b*f(10)+f(e)}return new Sk.builtin.float_(c)}),l.log10=new Sk.builtin.func(function log10(a){var c=Math.log10;Sk.builtin.pyCheckArgsLen(\"log10\",arguments.length,1,1),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(a));let d,e=Sk.builtin.asnum$(a);if(0>e)throw new Sk.builtin.ValueError(\"math domain error\");else if(Sk.builtin.checkFloat(a)||e<b)d=c(e);else{e=new Sk.builtin.str(a).$jsstr();const b=e.length,f=parseFloat(\"0.\"+e);d=b+c(f)}return new Sk.builtin.float_(d)}),l.pow=new Sk.builtin.func(function pow(a,b){var c=Number.isInteger;Sk.builtin.pyCheckArgsLen(\"pow\",arguments.length,2,2),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(a)),Sk.builtin.pyCheckType(\"y\",\"number\",Sk.builtin.checkNumber(b));const d=Sk.builtin.asnum$(new Sk.builtin.float_(a)),f=Sk.builtin.asnum$(new Sk.builtin.float_(b));if(0==d&&0>f)throw new Sk.builtin.ValueError(\"math domain error\");else{if(1==d)return new Sk.builtin.float_(1);if(g(d)&&g(f)&&0>d&&!c(f))throw new Sk.builtin.ValueError(\"math domain error\");else if(-1==d&&(f==-Infinity||f==1/0))return new Sk.builtin.float_(1)}const h=e(d,f);if(!g(d)||!g(f))return new Sk.builtin.float_(h);if(h==1/0||h==-Infinity)throw new Sk.builtin.OverflowError(\"math range error\");return new Sk.builtin.float_(h)}),l.sqrt=new Sk.builtin.func(function sqrt(b){return Sk.builtin.pyCheckArgsLen(\"sqrt\",arguments.length,1,1),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(b)),new Sk.builtin.float_(a(Sk.builtin.asnum$(b)))}),l.asin=new Sk.builtin.func(function asin(a){var b=Math.asin;return Sk.builtin.pyCheckArgsLen(\"asin\",arguments.length,1,1),Sk.builtin.pyCheckType(\"rad\",\"number\",Sk.builtin.checkNumber(a)),new Sk.builtin.float_(b(Sk.builtin.asnum$(a)))}),l.acos=new Sk.builtin.func(function acos(a){var b=Math.acos;return Sk.builtin.pyCheckArgsLen(\"acos\",arguments.length,1,1),Sk.builtin.pyCheckType(\"rad\",\"number\",Sk.builtin.checkNumber(a)),new Sk.builtin.float_(b(Sk.builtin.asnum$(a)))}),l.atan=new Sk.builtin.func(function atan(a){var b=Math.atan;return Sk.builtin.pyCheckArgsLen(\"atan\",arguments.length,1,1),Sk.builtin.pyCheckType(\"rad\",\"number\",Sk.builtin.checkNumber(a)),new Sk.builtin.float_(b(Sk.builtin.asnum$(a)))}),l.atan2=new Sk.builtin.func(function atan2(a,b){var c=Math.atan2;return Sk.builtin.pyCheckArgsLen(\"atan2\",arguments.length,2,2),Sk.builtin.pyCheckType(\"y\",\"number\",Sk.builtin.checkNumber(a)),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(b)),new Sk.builtin.float_(c(Sk.builtin.asnum$(a),Sk.builtin.asnum$(b)))}),l.sin=new Sk.builtin.func(function(a){var b=Math.sin;return Sk.builtin.pyCheckArgsLen(\"sin\",arguments.length,1,1),Sk.builtin.pyCheckType(\"rad\",\"number\",Sk.builtin.checkNumber(a)),new Sk.builtin.float_(b(Sk.builtin.asnum$(a)))}),l.cos=new Sk.builtin.func(function cos(a){var b=Math.cos;return Sk.builtin.pyCheckArgsLen(\"cos\",arguments.length,1,1),Sk.builtin.pyCheckType(\"rad\",\"number\",Sk.builtin.checkNumber(a)),new Sk.builtin.float_(b(Sk.builtin.asnum$(a)))}),l.tan=new Sk.builtin.func(function tan(a){var b=Math.tan;return Sk.builtin.pyCheckArgsLen(\"tan\",arguments.length,1,1),Sk.builtin.pyCheckType(\"rad\",\"number\",Sk.builtin.checkNumber(a)),new Sk.builtin.float_(b(Sk.builtin.asnum$(a)))}),l.dist=new Sk.builtin.func(function dist(){throw new Sk.builtin.NotImplementedError(\"math.dist() is not yet implemented in Skulpt\")}),l.hypot=new Sk.builtin.func(function hypot(b,c){return Sk.builtin.pyCheckArgsLen(\"hypot\",arguments.length,2,2),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(b)),Sk.builtin.pyCheckType(\"y\",\"number\",Sk.builtin.checkNumber(c)),b=Sk.builtin.asnum$(b),c=Sk.builtin.asnum$(c),new Sk.builtin.float_(a(b*b+c*c))}),l.asinh=new Sk.builtin.func(function asinh(b){Sk.builtin.pyCheckArgsLen(\"asinh\",arguments.length,1,1),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(b)),b=Sk.builtin.asnum$(b);const d=b+a(b*b+1);return new Sk.builtin.float_(c(d))}),l.acosh=new Sk.builtin.func(function acosh(b){Sk.builtin.pyCheckArgsLen(\"acosh\",arguments.length,1,1),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(b)),b=Sk.builtin.asnum$(b);const d=b+a(b*b-1);return new Sk.builtin.float_(c(d))}),l.atanh=new Sk.builtin.func(function atanh(a){Sk.builtin.pyCheckArgsLen(\"atanh\",arguments.length,1,1),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(a)),a=Sk.builtin.asnum$(a);const b=(1+a)/(1-a);return new Sk.builtin.float_(c(b)/2)}),l.sinh=new Sk.builtin.func(function sinh(a){Sk.builtin.pyCheckArgsLen(\"sinh\",arguments.length,1,1),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(a)),a=Sk.builtin.asnum$(a);const b=e(i,a);return new Sk.builtin.float_((b-1/b)/2)}),l.cosh=new Sk.builtin.func(function cosh(a){Sk.builtin.pyCheckArgsLen(\"cosh\",arguments.length,1,1),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(a)),a=Sk.builtin.asnum$(a);const b=e(i,a);return new Sk.builtin.float_((b+1/b)/2)}),l.tanh=new Sk.builtin.func(function tanh(a){Sk.builtin.pyCheckArgsLen(\"tanh\",arguments.length,1,1),Sk.builtin.pyCheckType(\"x\",\"number\",Sk.builtin.checkNumber(a));const b=Sk.builtin.asnum$(a);if(0===b)return new Sk.builtin.float_(b);const c=e(i,b),d=1/c;return new Sk.builtin.float_((c-d)/2/((c+d)/2))}),l.radians=new Sk.builtin.func(function radians(a){Sk.builtin.pyCheckArgsLen(\"radians\",arguments.length,1,1),Sk.builtin.pyCheckType(\"deg\",\"number\",Sk.builtin.checkNumber(a));const b=j/180*Sk.builtin.asnum$(a);return new Sk.builtin.float_(b)}),l.degrees=new Sk.builtin.func(function degrees(a){Sk.builtin.pyCheckArgsLen(\"degrees\",arguments.length,1,1),Sk.builtin.pyCheckType(\"rad\",\"number\",Sk.builtin.checkNumber(a));const b=180/j*Sk.builtin.asnum$(a);return new Sk.builtin.float_(b)}),l.erf=new Sk.builtin.func(function erf(){throw new Sk.builtin.NotImplementedError(\"math.erf() is not yet implemented in Skulpt\")}),l.erfc=new Sk.builtin.func(function erfc(){throw new Sk.builtin.NotImplementedError(\"math.erfc() is not yet implemented in Skulpt\")}),l.gamma=new Sk.builtin.func(function gamma(){throw new Sk.builtin.NotImplementedError(\"math.gamma() is not yet implemented in Skulpt\")}),l.lgamma=new Sk.builtin.func(function lgamma(){throw new Sk.builtin.NotImplementedError(\"math.lgamma() is not yet implemented in Skulpt\")}),l)};","src/lib/md5.py":"raise NotImplementedError(\"md5 is not yet implemented in Skulpt\")\n","src/lib/mhlib.py":"raise NotImplementedError(\"mhlib is not yet implemented in Skulpt\")\n","src/lib/mimetools.py":"raise NotImplementedError(\"mimetools is not yet implemented in Skulpt\")\n","src/lib/mimetypes.py":"raise NotImplementedError(\"mimetypes is not yet implemented in Skulpt\")\n","src/lib/mimify.py":"raise NotImplementedError(\"mimify is not yet implemented in Skulpt\")\n","src/lib/modulefinder.py":"raise NotImplementedError(\"modulefinder is not yet implemented in Skulpt\")\n","src/lib/multifile.py":"raise NotImplementedError(\"multifile is not yet implemented in Skulpt\")\n","src/lib/multiprocessing/__init__.py":"raise NotImplementedError(\"multiprocessing is not yet implemented in Skulpt\")\n","src/lib/multiprocessing/dummy/__init__.py":"raise NotImplementedError(\"dummy is not yet implemented in Skulpt\")\n","src/lib/mutex.py":"raise NotImplementedError(\"mutex is not yet implemented in Skulpt\")\n","src/lib/netrc.py":"raise NotImplementedError(\"netrc is not yet implemented in Skulpt\")\n","src/lib/new.py":"raise NotImplementedError(\"new is not yet implemented in Skulpt\")\n","src/lib/nntplib.py":"raise NotImplementedError(\"nntplib is not yet implemented in Skulpt\")\n","src/lib/ntpath.py":"raise NotImplementedError(\"ntpath is not yet implemented in Skulpt\")\n","src/lib/nturl2path.py":"raise NotImplementedError(\"nturl2path is not yet implemented in Skulpt\")\n","src/lib/numbers.py":"Number = (int, float, complex)\nIntegral = int\nComplex = complex\n","src/lib/opcode.py":"raise NotImplementedError(\"opcode is not yet implemented in Skulpt\")\n","src/lib/operator.js":"var $builtinmodule=function(){var a={};return a.lt=new Sk.builtin.func(function(c,a){return Sk.builtin.bool(Sk.misceval.richCompareBool(c,a,\"Lt\"))}),a.__lt__=a.lt,a.le=new Sk.builtin.func(function(c,a){return Sk.builtin.bool(Sk.misceval.richCompareBool(c,a,\"LtE\"))}),a.__le__=a.le,a.eq=new Sk.builtin.func(function(c,a){return Sk.builtin.bool(Sk.misceval.richCompareBool(c,a,\"Eq\"))}),a.__eq__=a.eq,a.ne=new Sk.builtin.func(function(c,a){return Sk.builtin.bool(Sk.misceval.richCompareBool(c,a,\"NotEq\"))}),a.__ne__=a.ne,a.ge=new Sk.builtin.func(function(c,a){return Sk.builtin.bool(Sk.misceval.richCompareBool(c,a,\"GtE\"))}),a.__ge__=a.ge,a.gt=new Sk.builtin.func(function(c,a){return Sk.builtin.bool(Sk.misceval.richCompareBool(c,a,\"Gt\"))}),a.__gt__=a.gt,a.not_=new Sk.builtin.func(function(){throw new Sk.builtin.NotImplementedError(\"operator.not_() is not yet implemented in Skulpt\")}),a.truth=new Sk.builtin.func(function(a){return Sk.builtin.bool(a)}),a.is_=new Sk.builtin.func(function(c,a){return Sk.builtin.bool(Sk.misceval.richCompareBool(c,a,\"Is\"))}),a.is_not=new Sk.builtin.func(function(c,a){return Sk.builtin.bool(Sk.misceval.richCompareBool(c,a,\"IsNot\"))}),a.abs=new Sk.builtin.func(function(a){return Sk.builtin.abs(a)}),a.__abs__=a.abs,a.add=new Sk.builtin.func(function(c,a){return Sk.abstr.objectAdd(c,a)}),a.__add__=a.add,a.and_=new Sk.builtin.func(function(c,a){return Sk.abstr.binary_op_(c,a,\"BitAnd\")}),a.__and__=a.and_,a.div=new Sk.builtin.func(function(c,a){return Sk.abstr.binary_op_(c,a,\"Div\")}),a.__div__=a.div,a.floordiv=new Sk.builtin.func(function(c,a){return Sk.abstr.binary_op_(c,a,\"FloorDiv\")}),a.__floordiv__=a.floordiv,a.index=new Sk.builtin.func(function(b){return new Sk.builtin.int_(Sk.misceval.asIndex(b))}),a.__index__=a.index,a.inv=new Sk.builtin.func(function(a){return Sk.abstr.numberUnaryOp(a,\"Invert\")}),a.__inv__=a.inv,a.invert=a.inv,a.__invert__=a.inv,a.lshift=new Sk.builtin.func(function(c,a){return Sk.abstr.binary_op_(c,a,\"LShift\")}),a.__lshift__=a.lshift,a.mod=new Sk.builtin.func(function(c,a){return Sk.abstr.binary_op_(c,a,\"Mod\")}),a.__mod__=a.mod,a.divmod=new Sk.builtin.func(function(c,a){return Sk.abstr.binary_op_(c,a,\"DivMod\")}),a.__divmod__=a.divmod,a.mul=new Sk.builtin.func(function(c,a){return Sk.abstr.binary_op_(c,a,\"Mult\")}),a.__mul__=a.mul,a.neg=new Sk.builtin.func(function(a){return Sk.abstr.objectNegative(a)}),a.__neg__=a.neg,a.or_=new Sk.builtin.func(function(c,a){return Sk.abstr.binary_op_(c,a,\"BitOr\")}),a.__or__=a.or_,a.pos=new Sk.builtin.func(function(a){return Sk.abstr.objectPositive(a)}),a.__pos__=a.pos,a.pow=new Sk.builtin.func(function(c,a){return Sk.abstr.binary_op_(c,a,\"Pow\")}),a.__pow__=a.pow,a.rshift=new Sk.builtin.func(function(c,a){return Sk.abstr.binary_op_(c,a,\"RShift\")}),a.__rshift__=a.rshift,a.sub=new Sk.builtin.func(function(c,a){return Sk.abstr.binary_op_(c,a,\"Sub\")}),a.__sub__=a.sub,a.truediv=a.div,a.__truediv__=a.div,a.xor=new Sk.builtin.func(function(c,a){return Sk.abstr.binary_op_(c,a,\"BitXor\")}),a.__xor__=a.xor,a.concat=new Sk.builtin.func(function(c,a){return Sk.abstr.sequenceConcat(c,a)}),a.__concat__=a.concat,a.contains=new Sk.builtin.func(function(c,a){return Sk.builtin.bool(Sk.abstr.sequenceContains(c,a))}),a.__contains__=a.contains,a.countOf=new Sk.builtin.func(function(c,a){return Sk.abstr.sequenceGetCountOf(c,a)}),a.delitem=new Sk.builtin.func(function(c,a){return Sk.abstr.sequenceDelItem(c,a)}),a.__delitem__=a.delitem,a.getitem=new Sk.builtin.func(function(c,a){return Sk.abstr.sequenceGetItem(c,a)}),a.__getitem__=a.getitem,a.indexOf=new Sk.builtin.func(function(c,a){return Sk.abstr.sequenceGetIndexOf(c,a)}),a.setitem=new Sk.builtin.func(function(d,a,b){return Sk.abstr.sequenceSetItem(d,a,b)}),a.__setitem__=a.setitem,a};","src/lib/optparse.py":"raise NotImplementedError(\"optparse is not yet implemented in Skulpt\")\n","src/lib/os.py":"raise NotImplementedError(\"os is not yet implemented in Skulpt\")\n","src/lib/os2emxpath.py":"raise NotImplementedError(\"os2emxpath is not yet implemented in Skulpt\")\n","src/lib/pdb.py":"raise NotImplementedError(\"pdb is not yet implemented in Skulpt\")\n","src/lib/pickle.py":"raise NotImplementedError(\"pickle is not yet implemented in Skulpt\")\n","src/lib/pickletools.py":"raise NotImplementedError(\"pickletools is not yet implemented in Skulpt\")\n","src/lib/pipes.py":"raise NotImplementedError(\"pipes is not yet implemented in Skulpt\")\n","src/lib/pkgutil.py":"raise NotImplementedError(\"pkgutil is not yet implemented in Skulpt\")\n","src/lib/platform.js":"var $builtinmodule=function(){var a={},b=\"undefined\"!=typeof window&&\"undefined\"!=typeof window.navigator;return a.python_implementation=new Sk.builtin.func(function(){return Sk.builtin.pyCheckArgsLen(\"python_implementation\",arguments.length,0,0),new Sk.builtin.str(\"Skulpt\")}),a.node=new Sk.builtin.func(function(){return Sk.builtin.pyCheckArgsLen(\"node\",arguments.length,0,0),new Sk.builtin.str(\"\")}),a.version=new Sk.builtin.func(function(){return Sk.builtin.pyCheckArgsLen(\"version\",arguments.length,0,0),new Sk.builtin.str(\"\")}),a.python_version=new Sk.builtin.func(function(){var a;return Sk.builtin.pyCheckArgsLen(\"python_version\",arguments.length,0,0),a=Sk.__future__.python_version?\"3.2.0\":\"2.7.0\",new Sk.builtin.str(a)}),a.system=new Sk.builtin.func(function(){var a;return Sk.builtin.pyCheckArgsLen(\"system\",arguments.length,0,0),a=b?window.navigator.appCodeName:\"\",new Sk.builtin.str(a)}),a.machine=new Sk.builtin.func(function(){var a;return Sk.builtin.pyCheckArgsLen(\"machine\",arguments.length,0,0),a=b?window.navigator.platform:\"\",new Sk.builtin.str(a)}),a.release=new Sk.builtin.func(function(){var a;return Sk.builtin.pyCheckArgsLen(\"release\",arguments.length,0,0),a=b?window.navigator.appVersion:\"\",new Sk.builtin.str(a)}),a.architecture=new Sk.builtin.func(function(){return Sk.builtin.pyCheckArgsLen(\"architecture\",arguments.length,0,0),new Sk.builtin.tuple([new Sk.builtin.str(\"64bit\"),new Sk.builtin.str(\"\")])}),a.processor=new Sk.builtin.func(function(){return Sk.builtin.pyCheckArgsLen(\"processor\",arguments.length,0,0),new Sk.builtin.str(\"\")}),a};","src/lib/platform.py":"raise NotImplementedError(\"platform is not yet implemented in Skulpt\")\n","src/lib/plistlib.py":"raise NotImplementedError(\"plistlib is not yet implemented in Skulpt\")\n","src/lib/popen2.py":"raise NotImplementedError(\"popen2 is not yet implemented in Skulpt\")\n","src/lib/poplib.py":"raise NotImplementedError(\"poplib is not yet implemented in Skulpt\")\n","src/lib/posixfile.py":"raise NotImplementedError(\"posixfile is not yet implemented in Skulpt\")\n","src/lib/posixpath.py":"raise NotImplementedError(\"posixpath is not yet implemented in Skulpt\")\n","src/lib/pprint.py":"raise NotImplementedError(\"pprint is not yet implemented in Skulpt\")\n","src/lib/processing.js":"var $builtinmodule=function(){var b,c,d,e,f,g,h,a=Math.PI,j={__name__:new Sk.builtin.str(\"processing\")},k=[],l=!0,m=null;return j.processing=null,j.p=null,j.X=new Sk.builtin.int_(0),j.Y=new Sk.builtin.int_(1),j.Z=new Sk.builtin.int_(2),j.R=new Sk.builtin.int_(3),j.G=new Sk.builtin.int_(4),j.B=new Sk.builtin.int_(5),j.A=new Sk.builtin.int_(6),j.U=new Sk.builtin.int_(7),j.V=new Sk.builtin.int_(8),j.NX=new Sk.builtin.int_(9),j.NY=new Sk.builtin.int_(10),j.NZ=new Sk.builtin.int_(11),j.EDGE=new Sk.builtin.int_(12),j.SR=new Sk.builtin.int_(13),j.SG=new Sk.builtin.int_(14),j.SB=new Sk.builtin.int_(15),j.SA=new Sk.builtin.int_(16),j.SW=new Sk.builtin.int_(17),j.TX=new Sk.builtin.int_(18),j.TY=new Sk.builtin.int_(19),j.TZ=new Sk.builtin.int_(20),j.VX=new Sk.builtin.int_(21),j.VY=new Sk.builtin.int_(22),j.VZ=new Sk.builtin.int_(23),j.VW=new Sk.builtin.int_(24),j.AR=new Sk.builtin.int_(25),j.AG=new Sk.builtin.int_(26),j.AB=new Sk.builtin.int_(27),j.DR=new Sk.builtin.int_(3),j.DG=new Sk.builtin.int_(4),j.DB=new Sk.builtin.int_(5),j.DA=new Sk.builtin.int_(6),j.SPR=new Sk.builtin.int_(28),j.SPG=new Sk.builtin.int_(29),j.SPB=new Sk.builtin.int_(30),j.SHINE=new Sk.builtin.int_(31),j.ER=new Sk.builtin.int_(32),j.EG=new Sk.builtin.int_(33),j.EB=new Sk.builtin.int_(34),j.BEEN_LIT=new Sk.builtin.int_(35),j.VERTEX_FIELD_COUNT=new Sk.builtin.int_(36),j.CENTER=new Sk.builtin.int_(3),j.RADIUS=new Sk.builtin.int_(2),j.CORNERS=new Sk.builtin.int_(1),j.CORNER=new Sk.builtin.int_(0),j.DIAMETER=new Sk.builtin.int_(3),j.BASELINE=new Sk.builtin.int_(0),j.TOP=new Sk.builtin.int_(101),j.BOTTOM=new Sk.builtin.int_(102),j.NORMAL=new Sk.builtin.int_(1),j.NORMALIZED=new Sk.builtin.int_(1),j.IMAGE=new Sk.builtin.int_(2),j.MODEL=new Sk.builtin.int_(4),j.SHAPE=new Sk.builtin.int_(5),j.AMBIENT=new Sk.builtin.int_(0),j.DIRECTIONAL=new Sk.builtin.int_(1),j.SPOT=new Sk.builtin.int_(3),j.RGB=new Sk.builtin.int_(1),j.ARGB=new Sk.builtin.int_(2),j.HSB=new Sk.builtin.int_(3),j.ALPHA=new Sk.builtin.int_(4),j.CMYK=new Sk.builtin.int_(5),j.TIFF=new Sk.builtin.int_(0),j.TARGA=new Sk.builtin.int_(1),j.JPEG=new Sk.builtin.int_(2),j.GIF=new Sk.builtin.int_(3),j.MITER=new Sk.builtin.str(\"miter\"),j.BEVEL=new Sk.builtin.str(\"bevel\"),j.ROUND=new Sk.builtin.str(\"round\"),j.SQUARE=new Sk.builtin.str(\"butt\"),j.PROJECT=new Sk.builtin.str(\"square\"),j.P2D=new Sk.builtin.int_(1),j.JAVA2D=new Sk.builtin.int_(1),j.WEBGL=new Sk.builtin.int_(2),j.P3D=new Sk.builtin.int_(2),j.OPENGL=new Sk.builtin.int_(2),j.PDF=new Sk.builtin.int_(0),j.DXF=new Sk.builtin.int_(0),j.OTHER=new Sk.builtin.int_(0),j.WINDOWS=new Sk.builtin.int_(1),j.MAXOSX=new Sk.builtin.int_(2),j.LINUX=new Sk.builtin.int_(3),j.EPSILON=new Sk.builtin.float_(1e-4),j.MAX_FLOAT=new Sk.builtin.float_(34028235e31),j.MIN_FLOAT=new Sk.builtin.float_(-34028235e31),j.MAX_INT=new Sk.builtin.int_(2147483647),j.MIN_INT=new Sk.builtin.int_(-2147483648),j.HALF_PI=new Sk.builtin.float_(a/2),j.THIRD_PI=new Sk.builtin.float_(a/3),j.PI=new Sk.builtin.float_(a),j.TWO_PI=new Sk.builtin.float_(2*a),j.TAU=new Sk.builtin.float_(2*a),j.QUARTER_PI=new Sk.builtin.float_(a/4),j.DEG_TO_RAD=new Sk.builtin.float_(a/180),j.RAD_TO_DEG=new Sk.builtin.float_(180/a),j.WHITESPACE=new Sk.builtin.str(\" \\t\\n\\r\\f\\xA0\"),j.POINT=new Sk.builtin.int_(2),j.POINTS=new Sk.builtin.int_(2),j.LINE=new Sk.builtin.int_(4),j.LINES=new Sk.builtin.int_(4),j.TRIANGLE=new Sk.builtin.int_(8),j.TRIANGLES=new Sk.builtin.int_(9),j.TRIANGLE_FAN=new Sk.builtin.int_(11),j.TRIANGLE_STRIP=new Sk.builtin.int_(10),j.QUAD=new Sk.builtin.int_(16),j.QUADS=new Sk.builtin.int_(16),j.QUAD_STRIP=new Sk.builtin.int_(17),j.POLYGON=new Sk.builtin.int_(20),j.PATH=new Sk.builtin.int_(21),j.RECT=new Sk.builtin.int_(30),j.ELLIPSE=new Sk.builtin.int_(31),j.ARC=new Sk.builtin.int_(32),j.SPHERE=new Sk.builtin.int_(40),j.BOX=new Sk.builtin.int_(41),j.GROUP=new Sk.builtin.int_(0),j.PRIMITIVE=new Sk.builtin.int_(1),j.GEOMETRY=new Sk.builtin.int_(3),j.VERTEX=new Sk.builtin.int_(0),j.BEZIER_VERTEX=new Sk.builtin.int_(1),j.CURVE_VERTEX=new Sk.builtin.int_(2),j.BREAK=new Sk.builtin.int_(3),j.CLOSESHAPE=new Sk.builtin.int_(4),j.REPLACE=new Sk.builtin.int_(0),j.BLEND=new Sk.builtin.int_(1),j.ADD=new Sk.builtin.int_(2),j.SUBTRACT=new Sk.builtin.int_(4),j.LIGHTEST=new Sk.builtin.int_(8),j.DARKEST=new Sk.builtin.int_(16),j.DIFFERENCE=new Sk.builtin.int_(32),j.EXCLUSION=new Sk.builtin.int_(64),j.MULTIPLY=new Sk.builtin.int_(128),j.SCREEN=new Sk.builtin.int_(256),j.OVERLAY=new Sk.builtin.int_(512),j.HARD_LIGHT=new Sk.builtin.int_(1024),j.SOFT_LIGHT=new Sk.builtin.int_(2048),j.DODGE=new Sk.builtin.int_(4096),j.BURN=new Sk.builtin.int_(8192),j.ALPHA_MASK=new Sk.builtin.int_(4278190080),j.RED_MASK=new Sk.builtin.int_(16711680),j.GREEN_MASK=new Sk.builtin.int_(65280),j.BLUE_MASK=new Sk.builtin.int_(255),j.CUSTOM=new Sk.builtin.int_(0),j.ORTHOGRAPHIC=new Sk.builtin.int_(2),j.PERSPECTIVE=new Sk.builtin.int_(3),j.ARROW=new Sk.builtin.str(\"default\"),j.CROSS=new Sk.builtin.str(\"crosshair\"),j.HAND=new Sk.builtin.str(\"pointer\"),j.MOVE=new Sk.builtin.str(\"move\"),j.TEXT=new Sk.builtin.str(\"text\"),j.WAIT=new Sk.builtin.str(\"wait\"),j.NOCURSOR=Sk.builtin.assk$(\"url('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='), auto\"),j.DISABLE_OPENGL_2X_SMOOTH=new Sk.builtin.int_(1),j.ENABLE_OPENGL_2X_SMOOTH=new Sk.builtin.int_(-1),j.ENABLE_OPENGL_4X_SMOOTH=new Sk.builtin.int_(2),j.ENABLE_NATIVE_FONTS=new Sk.builtin.int_(3),j.DISABLE_DEPTH_TEST=new Sk.builtin.int_(4),j.ENABLE_DEPTH_TEST=new Sk.builtin.int_(-4),j.ENABLE_DEPTH_SORT=new Sk.builtin.int_(5),j.DISABLE_DEPTH_SORT=new Sk.builtin.int_(-5),j.DISABLE_OPENGL_ERROR_REPORT=new Sk.builtin.int_(6),j.ENABLE_OPENGL_ERROR_REPORT=new Sk.builtin.int_(-6),j.ENABLE_ACCURATE_TEXTURES=new Sk.builtin.int_(7),j.DISABLE_ACCURATE_TEXTURES=new Sk.builtin.int_(-7),j.HINT_COUNT=new Sk.builtin.int_(10),j.OPEN=new Sk.builtin.int_(1),j.CLOSE=new Sk.builtin.int_(2),j.BLUR=new Sk.builtin.int_(11),j.GRAY=new Sk.builtin.int_(12),j.INVERT=new Sk.builtin.int_(13),j.OPAQUE=new Sk.builtin.int_(14),j.POSTERIZE=new Sk.builtin.int_(15),j.THRESHOLD=new Sk.builtin.int_(16),j.ERODE=new Sk.builtin.int_(17),j.DILATE=new Sk.builtin.int_(18),j.BACKSPACE=new Sk.builtin.int_(8),j.TAB=new Sk.builtin.int_(9),j.ENTER=new Sk.builtin.int_(10),j.RETURN=new Sk.builtin.int_(13),j.ESC=new Sk.builtin.int_(27),j.DELETE=new Sk.builtin.int_(127),j.CODED=new Sk.builtin.int_(65535),j.SHIFT=new Sk.builtin.int_(16),j.CONTROL=new Sk.builtin.int_(17),j.ALT=new Sk.builtin.int_(18),j.CAPSLK=new Sk.builtin.int_(20),j.PGUP=new Sk.builtin.int_(33),j.PGDN=new Sk.builtin.int_(34),j.END=new Sk.builtin.int_(35),j.HOME=new Sk.builtin.int_(36),j.LEFT=new Sk.builtin.int_(37),j.UP=new Sk.builtin.int_(38),j.RIGHT=new Sk.builtin.int_(39),j.DOWN=new Sk.builtin.int_(40),j.F1=new Sk.builtin.int_(112),j.F2=new Sk.builtin.int_(113),j.F3=new Sk.builtin.int_(114),j.F4=new Sk.builtin.int_(115),j.F5=new Sk.builtin.int_(116),j.F6=new Sk.builtin.int_(117),j.F7=new Sk.builtin.int_(118),j.F8=new Sk.builtin.int_(119),j.F9=new Sk.builtin.int_(120),j.F10=new Sk.builtin.int_(121),j.F11=new Sk.builtin.int_(122),j.F12=new Sk.builtin.int_(123),j.NUMLK=new Sk.builtin.int_(144),j.META=new Sk.builtin.int_(157),j.INSERT=new Sk.builtin.int_(155),j.SINCOS_LENGTH=new Sk.builtin.int_(720),j.PRECISIONB=new Sk.builtin.int_(15),j.PRECISIONF=new Sk.builtin.int_(32768),j.PREC_MAXVAL=new Sk.builtin.int_(32767),j.PREC_ALPHA_SHIFT=new Sk.builtin.int_(9),j.PREC_RED_SHIFT=new Sk.builtin.int_(1),j.NORMAL_MODE_AUTO=new Sk.builtin.int_(0),j.NORMAL_MODE_SHAPE=new Sk.builtin.int_(1),j.NORMAL_MODE_VERTEX=new Sk.builtin.int_(2),j.MAX_LIGHTS=new Sk.builtin.int_(8),j.line=new Sk.builtin.func(function(a,b,c,d){j.processing.line(a.v,b.v,c.v,d.v)}),j.ellipse=new Sk.builtin.func(function(a,b,c,d){j.processing.ellipse(a.v,b.v,c.v,d.v)}),j.text=new Sk.builtin.func(function(a,b,c){j.processing.text(a.v,b.v,c.v)}),j.point=new Sk.builtin.func(function(a,b){j.processing.point(a.v,b.v)}),j.arc=new Sk.builtin.func(function(a,b,c,d,e,f){j.processing.arc(a.v,b.v,c.v,d.v,e.v,f.v)}),j.quad=new Sk.builtin.func(function(a,b,c,d,e,f,g,h){j.processing.quad(a.v,b.v,c.v,d.v,e.v,f.v,g.v,h.v)}),j.rect=new Sk.builtin.func(function(a,b,c,d,e){\"undefined\"==typeof e?j.processing.rect(a.v,b.v,c.v,d.v):j.processing.rect(a.v,b.v,c.v,d.v,e.v)}),j.triangle=new Sk.builtin.func(function(a,b,c,d,e,f){j.processing.triangle(a.v,b.v,c.v,d.v,e.v,f.v)}),j.bezier=new Sk.builtin.func(function(a,b,c,d,e,f,g,h,i,k,l,m){\"undefined\"==typeof i?j.processing.bezier(a.v,b.v,c.v,d.v,e.v,f.v,g.v,h.v):j.processing.bezier(a.v,b.v,c.v,d.v,e.v,f.v,g.v,h.v,i.v,k.v,l.v,m.v)}),j.alpha=new Sk.builtin.func(function(a,c,d){return\"undefined\"==typeof c?new Sk.builtin.float_(j.processing.alpha(a.v)):\"undefined\"==typeof d?new Sk.builtin.float_(j.processing.alpha(a.v,c.v)):new Sk.builtin.float_(j.processing.alpha(a.v,c.v,d.v))}),j.ambient=new Sk.builtin.func(function(a,c,d){\"undefined\"==typeof c?j.processing.ambient(a.v):\"undefined\"==typeof d?j.processing.ambient(a.v,c.v):j.processing.ambient(a.v,c.v,d.v)}),j.ambientLight=new Sk.builtin.func(function(a,b,c,d,e,f){\"undefined\"==typeof d?j.processing.ambientLight(a.v,b.v,c.v):\"undefined\"==typeof e?j.processing.ambientLight(a.v,b.v,c.v,d.v):\"undefined\"==typeof f?j.processing.ambientLight(a.v,b.v,c.v,d.v,e.v):j.processing.ambientLight(a.v,b.v,c.v,d.v,e.v,f.v)}),j.beginCamera=new Sk.builtin.func(function(){j.processing.beginCamera()}),j.beginShape=new Sk.builtin.func(function(a){\"undefined\"==typeof a&&(a=j.POLYGON),j.processing.beginShape(a.v)}),j.bezierDetail=new Sk.builtin.func(function(a){a=\"undefined\"==typeof a?20:a.v,j.processing.bezierDetail(a)}),j.bezierPoint=new Sk.builtin.func(function(e,a,b,c,d){j.processing.bezierPoint(e.v,a.v,b.v,c.v,d.v)}),j.bezierTangent=new Sk.builtin.func(function(e,a,b,c,d){j.processing.bezierTangent(e.v,a.v,b.v,c.v,d.v)}),j.bezierVertex=new Sk.builtin.func(function(a,b,c,d,e,f,g,h,i){\"undefined\"==typeof g?j.processing.bezierVertex(a.v,b.v,c.v,d.v,e.v,f.v):\"undefined\"==typeof h?j.processing.bezierVertex(a.v,b.v,c.v,d.v,e.v,f.v,g.v):\"undefined\"==typeof i?j.processing.bezierVertex(a.v,b.v,c.v,d.v,e.v,f.v,g.v,h.v):j.processing.bezierVertex(a.v,b.v,c.v,d.v,e.v,f.v,g.v,h.v,i.v)}),j.blend=new Sk.builtin.func(function(a,b,c,d,e,f,g,h,i,k){other instanceof Sk.builtin.int_||other instanceof Sk.builtin.float_?j.processing.blend(a.v,b.v,c.v,d.v,e.v,f.v,g.v,h.v,i.v):j.processing.blend(a.v,b.v,c.v,d.v,e.v,f.v,g.v,h.v,i.v,k.v)}),j.blendColor=new Sk.builtin.func(function(a,b,d){var e=Sk.misceval.callsimArray(j.color,[new Sk.builtin.int_(0),new Sk.builtin.int_(0),new Sk.builtin.int_(0)]);return e.v=j.processing.blendColor(a.v,b.v,d.v),e}),j.brightness=new Sk.builtin.func(function(a,c,d){return\"undefined\"==typeof c?new Sk.builtin.float_(j.processing.brightness(a.v)):\"undefined\"==typeof d?new Sk.builtin.float_(j.processing.brightness(a.v,c.v)):new Sk.builtin.float_(j.processing.brightness(a.v,c.v,d.v))}),j.camera=new Sk.builtin.func(function(a,b,c,d,e,f,g,h,i){\"undefined\"==typeof a?j.processing.camera():j.processing.camera(a.v,b.v,c.v,d.v,e.v,f.v,g.v,h.v,i.v)}),j.constrain=new Sk.builtin.func(function(a,b,c){return new Sk.builtin.float_(j.processing.constrain(a.v,b.v,c.v))}),j.copy=new Sk.builtin.func(function(a,b,c,d,e,f,g,h,i){other instanceof Sk.builtin.int_||other instanceof Sk.builtin.float_?j.processing.copy(a.v,b.v,c.v,d.v,e.v,f.v,g.v,h.v):j.processing.copy(a.v,b.v,c.v,d.v,e.v,f.v,g.v,h.v,i.v)}),j.createFont=new Sk.builtin.func(function(a,b,c,d){var e=Sk.misceval.callsimArray(j.PFont);return e.v=\"undefined\"==typeof c?j.processing.createFont(a.v,b.v):\"undefined\"==typeof d?j.processing.createFont(a.v,b.v,c.v):j.processing.createFont(a.v,b.v,c.v,d.v),e}),j.createGraphics=new Sk.builtin.func(function(a,b,c,d){var e=Sk.misceval.callsimArray(j.PGraphics);return e.v=\"undefined\"==typeof d?j.processing.createGraphics(a.v,b.v,c.v):j.processing.createGraphics(a.v,b.v,c.v,d.v),e}),j.createImage=new Sk.builtin.func(function(a,b,c){var d=Sk.misceval.callsimArray(j.PImage);return d.v=j.processing.createImage(a.v,b.v,c.v),d}),j.cursor=new Sk.builtin.func(function(a,b,c){\"undefined\"==typeof a?j.processing.cursor():\"undefined\"==typeof b?j.processing.cursor(a.v):\"undefined\"==typeof c?j.processing.cursor(a.v,b.v):j.processing.cursor(a.v,b.v,c.v)}),j.curve=new Sk.builtin.func(function(a,b,c,d,e,f,g,h,i,k,l,m){\"undefined\"==typeof i?j.processing.curve(a.v,b.v,c.v,d.v,e.v,f.v,g.v,h.v):\"undefined\"==typeof k?j.processing.curve(a.v,b.v,c.v,d.v,e.v,f.v,g.v,h.v,i.v):\"undefined\"==typeof l?j.processing.curve(a.v,b.v,c.v,d.v,e.v,f.v,g.v,h.v,i.v,k.v):\"undefined\"==typeof m?j.processing.curve(a.v,b.v,c.v,d.v,e.v,f.v,g.v,h.v,i.v,k.v,l.v):j.processing.curve(a.v,b.v,c.v,d.v,e.v,f.v,g.v,h.v,i.v,k.v,l.v,m.v)}),j.curveDetail=new Sk.builtin.func(function(a){j.processing.curveDetail(a.v)}),j.curvePoint=new Sk.builtin.func(function(e,a,b,c,d){j.processing.curvePoint(e.v,a.v,b.v,c.v,d.v)}),j.curveTangent=new Sk.builtin.func(function(e,a,b,c,d){j.processing.curveTangent(e.v,a.v,b.v,c.v,d.v)}),j.curveTightness=new Sk.builtin.func(function(a){j.processing.curveTightness(a.v)}),j.curveVertex=new Sk.builtin.func(function(a,b,c){\"undefined\"==typeof c?j.processing.curveVertex(a.v,b.v):j.processing.curveVertex(a.v,b.v,c.v)}),j.day=new Sk.builtin.func(function(){return new Sk.builtin.int_(j.processing.day())}),j.degrees=new Sk.builtin.func(function(a){return new Sk.builtin.float_(j.processing.degrees(a.v))}),j.directionalLight=new Sk.builtin.func(function(a,b,c,d,e,f){j.processing.directionalLight(a.v,b.v,c.v,d.v,e.v,f.v)}),j.dist=new Sk.builtin.func(function(a,b,c,d,e,f){return\"undefined\"==typeof e?new Sk.builtin.float_(j.processing.dist(a.v,b.v,c.v,d.v)):\"undefined\"==typeof f?new Sk.builtin.float_(j.processing.dist(a.v,b.v,c.v,d.v,e.v)):new Sk.builtin.float_(j.processing.dist(a.v,b.v,c.v,d.v,e.v,f.v))}),j.emissive=new Sk.builtin.func(function(a,b,c){\"undefined\"==typeof b?j.processing.emissive(a.v):\"undefined\"==typeof c?j.processing.emissive(a.v,b.v):j.processing.emissive(a.v,b.v,c.v)}),j.endCamera=new Sk.builtin.func(function(){j.processing.endCamera()}),j.endShape=new Sk.builtin.func(function(a){\"undefined\"==typeof a?j.processing.endShape():j.processing.endShape(a.v)}),j.filter=new Sk.builtin.func(function(a,b){\"undefined\"==typeof b?j.processing.filter(a.v):j.processing.filter(a.v,b.v)}),j.frustum=new Sk.builtin.func(function(a,b,c,d,e,f){j.processing.frustum(a,b,c,d,e,f)}),j.hint=new Sk.builtin.func(function(a){j.processing.hint(a)}),j.hour=new Sk.builtin.func(function(){return new Sk.builtin.int_(j.processing.hour())}),j.hue=new Sk.builtin.func(function(a){return new Sk.builtin.float_(j.processing.hue(a.v))}),j.imageMode=new Sk.builtin.func(function(a){j.processing.imageMode(a.v)}),j.lerp=new Sk.builtin.func(function(a,b,c){return new Sk.builtin.float_(j.processing.lerp(a.v,b.v,c.v))}),j.lerpColor=new Sk.builtin.func(function(a,b,d){var e=Sk.misceval.callsimArray(j.color,[new Sk.builtin.int_(0),new Sk.builtin.int_(0),new Sk.builtin.int_(0)]);return e.v=j.processing.lerpColor(a.v,b.v,d.v),e}),j.lightFalloff=new Sk.builtin.func(function(a,b,c){j.processing.lightFalloff(a.v,b.v,c.v)}),j.lights=new Sk.builtin.func(function(){j.processing.lights()}),j.lightSpecular=new Sk.builtin.func(function(a,b,c){j.processing.lightSpecular(a.v,b.v,c.v)}),j.loadBytes=new Sk.builtin.func(function(a){return new Sk.builtin.list(j.processing.loadBytes(a.v))}),j.loadFont=new Sk.builtin.func(function(a){var b=Sk.misceval.callsimArray(j.PFont);return b.v=j.processing.loadFont(a.v),b}),j.loadShape=new Sk.builtin.func(function(a){var b=Sk.misceval.callsimArray(j.PShapeSVG,[new Sk.builtin.str(\"string\"),a]);return b}),j.loadStrings=new Sk.builtin.func(function(a){return new Sk.builtin.list(j.processing.loadStrings(a.v))}),j.mag=new Sk.builtin.func(function(d,a,b){return\"undefined\"==typeof b?new Sk.builtin.float_(j.processing.mag(d.v,a.v)):new Sk.builtin.float_(j.processing.mag(d.v,a.v,b.v))}),j.map=new Sk.builtin.func(function(a,b,c,d,e){return new Sk.builtin.float_(j.processing.map(a.v,b.v,c.v,d.v,e.v))}),j.millis=new Sk.builtin.func(function(){return new Sk.builtin.int_(j.processing.millis())}),j.minute=new Sk.builtin.func(function(){return new Sk.builtin.int_(j.processing.minute())}),j.modelX=new Sk.builtin.func(function(a,b,c){return new Sk.builtin.float_(j.processing.modelX(a.v,b.v,c.v))}),j.modelY=new Sk.builtin.func(function(a,b,c){return new Sk.builtin.float_(j.processing.modelY(a.v,b.v,c.v))}),j.modelZ=new Sk.builtin.func(function(a,b,c){return new Sk.builtin.float_(j.processing.modelZ(a.v,b.v,c.v))}),j.month=new Sk.builtin.func(function(){return new Sk.builtin.int_(j.processing.month())}),j.noCursor=new Sk.builtin.func(function(){j.processing.noCursor()}),j.noise=new Sk.builtin.func(function(a,b,c){return\"undefined\"==typeof b?new Sk.builtin.float_(j.processing.noise(a.v)):\"undefined\"==typeof c?new Sk.builtin.float_(j.processing.noise(a.v,b.v)):new Sk.builtin.float_(j.processing.noise(a.v,b.v,c.v))}),j.noiseDetail=new Sk.builtin.func(function(a,b){j.processing.noiseDetail(a.v,b.v)}),j.noiseSeed=new Sk.builtin.func(function(a){return new Sk.builtin.float_(j.processing.noiseSeed(a.v))}),j.noLights=new Sk.builtin.func(function(){j.processing.noLights()}),j.norm=new Sk.builtin.func(function(a,b,c){return new Sk.builtin.float_(j.processing.norm(a.v,b.v,c.v))}),j.normal=new Sk.builtin.func(function(a,b,c){j.processing.normal(a.v,b.v,c.v)}),j.noTint=new Sk.builtin.func(function(){j.processing.noTint()}),j.ortho=new Sk.builtin.func(function(a,b,c,d,e,f){j.processing.ortho(a.v,b.v,c.v,d.v,e.v,f.v)}),j.perspective=new Sk.builtin.func(function(a,b,c,d){\"undefined\"==typeof a?j.processing.perspective():\"undefined\"==typeof b?j.processing.perspective(a.v):\"undefined\"==typeof c?j.processing.perspective(a.v,b.v):\"undefined\"==typeof d?j.processing.perspective(a.v,b.v,c.v):j.processing.perspective(a.v,b.v,c.v,d.v)}),j.pointLight=new Sk.builtin.func(function(a,b,c,d,e,f){j.processing.pointLight(a.v,b.v,c.v,d.v,e.v,f.v)}),j.printCamera=new Sk.builtin.func(function(){j.processing.printCamera()}),j.println=new Sk.builtin.func(function(a){j.processing.println(a.v)}),j.printProjection=new Sk.builtin.func(function(){j.processing.printProjection()}),j.radians=new Sk.builtin.func(function(a){return new Sk.builtin.float_(j.processing.radians(a.v))}),j.randomSeed=new Sk.builtin.func(function(a){return new Sk.builtin.float_(j.processing.randomSeed(a.v))}),j.random=new Sk.builtin.func(function(a,b){return\"undefined\"==typeof a?new Sk.builtin.float_(j.processing.random()):\"undefined\"==typeof b?new Sk.builtin.float_(j.processing.random(a.v)):new Sk.builtin.float_(j.processing.random(a.v,b.v))}),j.requestImage=new Sk.builtin.func(function(a,b){var c=Sk.misceval.callsimArray(j.PImage);return c.v=\"undefined\"==typeof b?j.processing.requestImage(a.v):j.processing.requestImage(a.v,b.v),c}),j.saturation=new Sk.builtin.func(function(a){return new Sk.builtin.float_(j.processing.saturation(a.v))}),j.save=new Sk.builtin.func(function(a){j.processing.save(a.v)}),j.saveFrame=new Sk.builtin.func(function(a){\"undefined\"==typeof a?j.processing.saveFrame():j.processing.saveFrame(a.v)}),j.saveStrings=new Sk.builtin.func(function(a,b){j.processing.saveStrings(a.v,b.v)}),j.screenX=new Sk.builtin.func(function(a,b,c){return new Sk.builtin.float_(j.processing.screenX(a.v,b.v,c.v))}),j.screenY=new Sk.builtin.func(function(a,b,c){return new Sk.builtin.float_(j.processing.screenY(a.v,b.v,c.v))}),j.screenZ=new Sk.builtin.func(function(a,b,c){return new Sk.builtin.float_(j.processing.screenZ(a.v,b.v,c.v))}),j.second=new Sk.builtin.func(function(){return new Sk.builtin.int_(j.processing.second())}),j.shape=new Sk.builtin.func(function(a,b,c,d,e){\"undefined\"==typeof b?j.processing.shape(a.v):\"undefined\"==typeof c?j.processing.shape(a.v,b.v):\"undefined\"==typeof d?j.processing.shape(a.v,b.v,c.v):\"undefined\"==typeof e?j.processing.shape(a.v,b.v,c.v,d.v):j.processing.shape(a.v,b.v,c.v,d.v,e.v)}),j.shapeMode=new Sk.builtin.func(function(a){j.processing.shapeMode(a.v)}),j.shininess=new Sk.builtin.func(function(a){j.processing.shininess(a.v)}),j.specular=new Sk.builtin.func(function(a,b,c){\"undefined\"==typeof b?j.processing.specular(a.v):\"undefined\"==typeof c?j.processing.specular(a.v,b.v):j.processing.specular(a.v,b.v,c.v)}),j.spotLight=new Sk.builtin.func(function(a,b,c,d,e,f,g,h){j.processing.spotLight(a.v,b.v,c.v,d.v,e.v,f.v,g.v,h.v)}),j.sq=new Sk.builtin.func(function(a){return new Sk.builtin.float_(j.processing.sq(a))}),j.status=new Sk.builtin.func(function(a){j.processing.status(a.v)}),j.textAlign=new Sk.builtin.func(function(a,b){\"undefined\"==typeof b?j.processing.textAlign(a.v):j.processing.textAlign(a.v,b.v)}),j.textAscent=new Sk.builtin.func(function(){return new Sk.builtin.float_(j.processing.textAscent())}),j.textDescent=new Sk.builtin.func(function(){return new Sk.builtin.float_(j.processing.textDescent())}),j.textFont=new Sk.builtin.func(function(a,b){\"undefined\"==typeof b?j.processing.textFont(a.v):j.processing.textFont(a.v,b.v)}),j.textLeading=new Sk.builtin.func(function(a){j.processing.textLeading(a.v)}),j.textMode=new Sk.builtin.func(function(a){j.processing.textMode(a.v)}),j.textSize=new Sk.builtin.func(function(a){j.processing.textSize(a.v)}),j.texture=new Sk.builtin.func(function(a){j.processing.texture(a.v)}),j.textureMode=new Sk.builtin.func(function(a){j.processing.textureMode(a.v)}),j.textWidth=new Sk.builtin.func(function(a){return new Sk.builtin.float_(j.processing.textWidth(a.v))}),j.tint=new Sk.builtin.func(function(a,b,c,d){\"undefined\"==typeof b?j.processing.tint(a.v):\"undefined\"==typeof c?j.processing.tint(a.v,b.v):\"undefined\"==typeof d?j.processing.tint(a.v,b.v,c.v):j.processing.tint(a.v,b.v,c.v,d.v)}),j.updatePixels=new Sk.builtin.func(function(){j.processing.updatePixels()}),j.vertex=new Sk.builtin.func(function(a,b,c,d,e){\"undefined\"==typeof c?j.processing.vertex(a.v,b.v):\"undefined\"==typeof d?j.processing.vertex(a.v,b.v,c.v):\"undefined\"==typeof e?j.processing.vertex(a.v,b.v,c.v,d.v):j.processing.vertex(a.v,b.v,c.v,d.v,e.v)}),j.year=new Sk.builtin.func(function(){return new Sk.builtin.int_(j.processing.year())}),j.box=new Sk.builtin.func(function(a){j.processing.box(a.v)}),j.sphere=new Sk.builtin.func(function(a){j.processing.sphere(a.v)}),j.sphereDetail=new Sk.builtin.func(function(a,b){\"undefined\"==typeof b?j.processing.sphereDetail(a.v):j.processing.sphereDetail(a.v,b.v)}),j.background=new Sk.builtin.func(function(a,c,d){\"undefined\"!=typeof c&&(c=c.v),\"undefined\"!=typeof d&&(d=d.v),j.processing.background(a.v,c,d)}),j.fill=new Sk.builtin.func(function(a,c,d,e){\"undefined\"!=typeof c&&(c=c.v),\"undefined\"!=typeof d&&(d=d.v),\"undefined\"!=typeof e&&(e=e.v),j.processing.fill(a.v,c,d,e)}),j.stroke=new Sk.builtin.func(function(a,c,d,e){\"undefined\"!=typeof c&&(c=c.v),\"undefined\"!=typeof d&&(d=d.v),\"undefined\"!=typeof e&&(e=e.v),j.processing.stroke(a.v,c,d,e)}),j.noStroke=new Sk.builtin.func(function(){j.processing.noStroke()}),j.colorMode=new Sk.builtin.func(function(a,b,c,d,e){b=\"undefined\"==typeof b?255:b.v,\"undefined\"!=typeof c&&(c=c.v),\"undefined\"!=typeof d&&(d=d.v),\"undefined\"!=typeof e&&(e=e.v),j.processing.colorMode(a.v,b,c,d,e)}),j.noFill=new Sk.builtin.func(function(){j.processing.noFill()}),j.loop=new Sk.builtin.func(function(){if(null===j.processing)throw new Sk.builtin.Exception(\"loop() should be called after run()\");l=!0,j.processing.loop()}),j.noLoop=new Sk.builtin.func(function(){if(null===j.processing)throw new Sk.builtin.Exception(\"noLoop() should be called after run()\");l=!1,j.processing.noLoop()}),j.frameRate=new Sk.builtin.func(function(a){j.processing.frameRate(a.v)}),j.width=new Sk.builtin.int_(0),j.height=new Sk.builtin.int_(0),j.renderMode=j.P2D,j.size=new Sk.builtin.func(function(a,b,c){\"undefined\"==typeof c&&(c=j.P2D),j.processing.size(a.v,b.v,c.v),j.width=new Sk.builtin.int_(j.processing.width),j.height=new Sk.builtin.int_(j.processing.height),j.renderMode=c}),j.exitp=new Sk.builtin.func(function(){j.processing.exit()}),j.mouseX=new Sk.builtin.func(function(){return new Sk.builtin.int_(j.processing.mouseX)}),j.mouseY=new Sk.builtin.func(function(){return new Sk.builtin.int_(j.processing.mouseY)}),j.pmouseX=new Sk.builtin.func(function(){return new Sk.builtin.int_(j.processing.pmouseX)}),j.pmouseY=new Sk.builtin.func(function(){return new Sk.builtin.int_(j.processing.pmouseY)}),j.rectMode=new Sk.builtin.func(function(a){j.processing.rectMode(a.v)}),j.strokeWeight=new Sk.builtin.func(function(a){j.processing.strokeWeight(a.v)}),j.smooth=new Sk.builtin.func(function(){j.processing.smooth()}),j.noSmooth=new Sk.builtin.func(function(){j.processing.noSmooth()}),j.ellipseMode=new Sk.builtin.func(function(a){j.processing.ellipseMode(a.v)}),j.strokeCap=new Sk.builtin.func(function(a){j.processing.strokeCap(a.v)}),j.strokeJoin=new Sk.builtin.func(function(a){j.processing.strokeJoin(a.v)}),j.rotate=new Sk.builtin.func(function(a){j.processing.rotate(a.v)}),j.rotateX=new Sk.builtin.func(function(a){j.processing.rotateX(a.v)}),j.rotateY=new Sk.builtin.func(function(a){j.processing.rotateY(a.v)}),j.rotateZ=new Sk.builtin.func(function(a){j.processing.rotateZ(a.v)}),j.scale=new Sk.builtin.func(function(a,b,c){b=\"undefined\"==typeof b?1:b.v,c=\"undefined\"==typeof c?1:c.v,j.processing.scale(a.v,b,c)}),j.translate=new Sk.builtin.func(function(a,b,c){b=\"undefined\"==typeof b?1:b.v,c=\"undefined\"==typeof c?1:c.v,j.processing.translate(a.v,b,c)}),j.popMatrix=new Sk.builtin.func(function(){j.processing.popMatrix()}),j.pushMatrix=new Sk.builtin.func(function(){j.processing.pushMatrix()}),j.applyMatrix=new Sk.builtin.func(function(){var a,b=Array.prototype.slice.call(arguments,0,16);for(a=0;a<b.length;a++)b[a]=\"undefined\"==typeof b[a]?0:b[a].v;j.processing.applyMatrix.apply(j.processing,b)}),j.resetMatrix=new Sk.builtin.func(function(){j.processing.resetMatrix()}),j.printMatrix=new Sk.builtin.func(function(){return Sk.ffi.remapToPy(j.processing.printMatrix())}),j.run=new Sk.builtin.func(function(){var a=document.getElementById(Sk.canvas);if(!a)throw new Error(\"Processing module: Canvas element not specified\");if(window.Processing.logger={log:function(a){Sk.misceval.print_(a)}},m=window.Processing.getInstanceById(Sk.canvas),m&&m.exit(),j.p=new window.Processing(a,function sketchProc(a){j.processing=a,a.draw=function(){var b=!1;for(var c in k)0===k[c].width&&(b=!0);if(!0==b)return!0===l?void 0:void a.loop();if(!1===l&&a.noLoop(),j.frameCount=a.frameCount,Sk.globals.draw)try{Sk.misceval.callsimArray(Sk.globals.draw)}catch(a){Sk.uncaughtException(a)}};var b=[\"setup\",\"mouseMoved\",\"mouseClicked\",\"mouseDragged\",\"mouseMoved\",\"mouseOut\",\"mouseOver\",\"mousePressed\",\"mouseReleased\",\"keyPressed\",\"keyReleased\",\"keyTyped\"];for(var c in b)Sk.globals[b[c]]&&(a[b[c]]=new Function(\"try {Sk.misceval.callsimArray(Sk.globals['\"+b[c]+\"']);} catch(e) {Sk.uncaughtException(e);}\"))}),0===j.width.v&&0===j.height.v){var b=a.offsetWidth,c=a.offsetHeight;Sk.misceval.callsimArray(j.size,[new Sk.builtin.int_(b),new Sk.builtin.int_(c),j.renderMode])}}),g=function(a,b){b.__getattr__=new Sk.builtin.func(function(a,b){return(b=Sk.ffi.remapToJs(b),\"x\"===b)?Sk.builtin.assk$(j.processing.mouseX):\"y\"===b?Sk.builtin.assk$(j.processing.mouseY):\"px\"===b?Sk.builtin.assk$(j.processing.pmouseX):\"py\"===b?Sk.builtin.assk$(j.processing.pmouseY):\"pressed\"===b?new Sk.builtin.bool(j.processing.__mousePressed):\"button\"===b?Sk.builtin.assk$(j.processing.mouseButton):void 0})},j.Mouse=Sk.misceval.buildClass(j,g,\"Mouse\",[]),j.mouse=Sk.misceval.callsimArray(j.Mouse),f=function(a,b){b.__getattr__=new Sk.builtin.func(function(a,b){return(b=Sk.ffi.remapToJs(b),\"key\"===b)?new Sk.builtin.str(j.processing.key.toString()):\"keyCode\"===b?Sk.builtin.assk$(j.processing.keyCode):\"keyPressed\"===b?new Sk.builtin.str(j.processing.keyPressed):void 0})},j.Keyboard=Sk.misceval.buildClass(j,f,\"Keyboard\",[]),j.keyboard=Sk.misceval.callsimArray(j.Keyboard),e=function(a,b){b.__getattr__=new Sk.builtin.func(function(a,b){return(b=Sk.ffi.remapToJs(b),\"frameCount\"===b)?Sk.builtin.assk$(j.processing.frameCount):\"frameRate\"===b?Sk.builtin.assk$(j.processing.frameRate):\"height\"===b?Sk.builtin.assk$(j.processing.height):\"width\"===b?Sk.builtin.assk$(j.processing.width):\"online\"===b?new Sk.builtin.bool(j.processing.online):\"focused\"===b?new Sk.builtin.bool(j.processing.focused):void 0})},j.Environment=Sk.misceval.buildClass(j,e,\"Environment\",[]),j.environment=Sk.misceval.callsimArray(j.Environment),d=function(a,b){b.__init__=new Sk.builtin.func(function(a){a.pixels=null}),b.__getattr__=new Sk.builtin.func(function(a,b){return(b=Sk.ffi.remapToJs(b),\"height\"===b)?Sk.builtin.assk$(j.processing.height):\"width\"===b?Sk.builtin.assk$(j.processing.width):(\"pixels\"===b&&null==a.pixels&&(a.pixels=new Sk.builtin.list(j.processing.pixels.toArray())),a.pixels)})},j.Screen=Sk.misceval.buildClass(j,d,\"Screen\",[]),j.screen=Sk.misceval.callsimArray(j.Screen),j.loadPixels=new Sk.builtin.func(function(){j.processing.loadPixels()}),c=function(a,b){b.__init__=new Sk.builtin.func(function(a,b,c,d,e){\"undefined\"!=typeof c&&(c=c.v),\"undefined\"!=typeof d&&(d=d.v),\"undefined\"!=typeof e&&(e=e.v),a.v=j.processing.color(b.v,c,d,e)})},j.color=Sk.misceval.buildClass(j,c,\"color\",[]),j.red=new Sk.builtin.func(function(a){return new Sk.builtin.int_(j.processing.red(a.v))}),j.green=new Sk.builtin.func(function(a){return new Sk.builtin.int_(j.processing.green(a.v))}),j.blue=new Sk.builtin.func(function(a){return new Sk.builtin.int_(j.processing.blue(a.v))}),b=function(a,b){b.__init__=new Sk.builtin.func(function(a,b,c,d){a.v=\"undefined\"==typeof b?new j.processing.PImage:\"undefined\"==typeof c?new j.processing.PImage(b.v):\"undefined\"==typeof d?new j.processing.PImage(b.v,c.v):new j.processing.PImage(b.v,c.v,d.v)}),b.__getattr__=new Sk.builtin.func(function(a,b){return b=Sk.ffi.remapToJs(b),\"width\"===b?Sk.builtin.assk$(a.v.width):\"height\"===b?Sk.builtin.assk$(a.v.height):void 0})},j.loadImage=new Sk.builtin.func(function(a){var b=j.processing.loadImage(a.v);k.push(b);var c=Sk.misceval.callsimArray(j.PImage);return c.v=b,c}),j.image=new Sk.builtin.func(function(a,b,c,d,e){\"undefined\"==typeof d?j.processing.image(a.v,b.v,c.v):j.processing.image(a.v,b.v,c.v,d.v,e.v)}),j.get=new Sk.builtin.func(function(a,b){var c=j.processing.get(a.v,b.v);return Sk.misceval.callsimArray(j.color,[new Sk.builtin.int_(j.processing.red(c)),new Sk.builtin.int_(j.processing.green(c)),new Sk.builtin.int_(j.processing.blue(c))])}),j.set=new Sk.builtin.func(function(a,b,c){j.processing.set(a.v,b.v,c.v)}),h=function(a,b){b.__init__=new Sk.builtin.func(function(a,b,c,d){a.v=\"undefined\"==typeof b?new j.processing.PVector:\"undefined\"==typeof d?new j.processing.PVector(b.v,c.v):new j.processing.PVector(b.v,c.v,d.v)}),b.__getattr__=new Sk.builtin.func(function(a,b){return(b=Sk.ffi.remapToJs(b),\"x\"===b)?Sk.builtin.assk$(a.v.x):\"y\"===b?Sk.builtin.assk$(a.v.y):\"z\"===b?Sk.builtin.assk$(a.v.z):void 0}),b.get=new Sk.builtin.func(function(a){var b=Sk.misceval.callsimArray(j.PVector);return b.v=a.v.get(),b}),b.set=new Sk.builtin.func(function(a,b,c,b){\"undefined\"==typeof z?a.v.set(b.v,c.v):a.v.set(b.v,c.v,z.v)}),b.mag=new Sk.builtin.func(function(a){return Sk.builtin.assk$(a.v.mag())}),b.add=new Sk.builtin.func(function(a,b){var c=Sk.misceval.callsimArray(j.PVector);return c.v=a.v.add(b.v),c}),b.sub=new Sk.builtin.func(function(a,b){var c=Sk.misceval.callsimArray(j.PVector);return c.v=a.v.sub(b.v),c}),b.mult=new Sk.builtin.func(function(a,b){var c=Sk.misceval.callsimArray(j.PVector);return c.v=a.v.mult(b.v),c}),b.div=new Sk.builtin.func(function(a,b){var c=Sk.misceval.callsimArray(j.PVector);return c.v=a.v.dic(b.v),c}),b.dist=new Sk.builtin.func(function(a,b){return Sk.builtin.assk$(a.v.dist(b.v))}),b.dot=new Sk.builtin.func(function(a,b,c,d){return\"undefined\"==typeof c?Sk.builtin.assk$(a.v.dot(b.v)):Sk.builtin.assk$(a.v.dot(b.v,c.v,d.v))}),b.cross=new Sk.builtin.func(function(a,b){var c=Sk.misceval.callsimArray(j.PVector);return c.v=a.v.cross(b.v),c}),b.normalize=new Sk.builtin.func(function(a){a.v.normalize()}),b.limit=new Sk.builtin.func(function(a,b){a.v.limit(b.v)}),b.angleBetween=new Sk.builtin.func(function(a,b){return Sk.builtin.assk$(a.v.angleBetween(b.v))}),b.array=new Sk.builtin.func(function(a){return new Sk.builtin.list(a.v.array())})},fontClass=function(a,b){b.__init__=new Sk.builtin.func(function(a,b){a.v=\"undefined\"==typeof b?new j.processing.PFont:new j.processing.PVector(b.v)}),b.list=new Sk.builtin.func(function(a){return new Sk.builtin.list(a.v.list())})},graphicsClass=function(a,b){b.__init__=new Sk.builtin.func(function(a,b,c,d){a.v=\"undefined\"==typeof b?new j.processing.PVector:\"undefined\"==typeof d?new j.processing.PVector(b.v,c.v):new j.processing.PVector(b.v,c.v,d.v)}),b.beginDraw=new Sk.builtin.func(function(a){a.v.beginDraw()}),b.endDraw=new Sk.builtin.func(function(a){a.v.endDraw()})},shapeClass=function(a,b){b.__init__=new Sk.builtin.func(function(a,b,c,d){a.v=\"undefined\"==typeof b?null:\"undefined\"==typeof c?new j.processing.PShapeSVG(b.v):\"undefined\"==typeof d?new j.processing.PShapeSVG(b.v,c.v):new j.processing.PShapeSVG(b.v,c.v,d.v)}),b.__getattr__=new Sk.builtin.func(function(a,b){return(b=Sk.ffi.remapToJs(b),\"width\"===b)?Sk.builtin.assk$(a.v.width):\"height\"===b?Sk.builtin.assk$(a.v.height):void 0}),b.isVisible=new Sk.builtin.func(function(a){return new Sk.builtin.bool(a.v.isVisible())}),b.setVisible=new Sk.builtin.func(function(a,b){a.v.setVisible(b.v)}),b.disableStyle=new Sk.builtin.func(function(a){a.v.disableStyle()}),b.enableStyle=new Sk.builtin.func(function(a){a.v.enableStyle()}),b.getChild=new Sk.builtin.func(function(a,b){var c=a.v.getChild(b.v);if(null!=c){var d=Sk.misceval.callsimArray(j.PShapeSVG);return d.v=c,d}return null}),b.translate=new Sk.builtin.func(function(a,b,c,d){\"undefined\"==typeof d?a.v.translate(b.v,c.v):a.v.translate(b.v,c.v,d.v)}),b.rotate=new Sk.builtin.func(function(a,b){a.v.rotate(b.v)}),b.rotateX=new Sk.builtin.func(function(a,b){a.v.rotateX(b.v)}),b.rotateY=new Sk.builtin.func(function(a){a.v.rotateY(angle.v)}),b.rotateZ=new Sk.builtin.func(function(a){a.v.rotateZ(angle.v)}),b.scale=new Sk.builtin.func(function(a,b,c,d){\"undefined\"==typeof c?a.v.scale(b.v):\"undefined\"==typeof d?a.v.scale(b.v,c.v):a.v.scale(b.v,c.v,d.v)})},j.PFont=Sk.misceval.buildClass(j,fontClass,\"PFont\",[]),j.PGraphics=Sk.misceval.buildClass(j,graphicsClass,\"PGraphics\",[]),j.PShapeSVG=Sk.misceval.buildClass(j,shapeClass,\"PShapeSVG\",[]),j.PVector=Sk.misceval.buildClass(j,h,\"PVector\",[]),j.PImage=Sk.misceval.buildClass(j,b,\"PImage\",[]),j};","src/lib/profile.py":"raise NotImplementedError(\"profile is not yet implemented in Skulpt\")\n","src/lib/pstats.py":"raise NotImplementedError(\"pstats is not yet implemented in Skulpt\")\n","src/lib/pty.py":"raise NotImplementedError(\"pty is not yet implemented in Skulpt\")\n","src/lib/py_compile.py":"raise NotImplementedError(\"py_compile is not yet implemented in Skulpt\")\n","src/lib/pyclbr.py":"raise NotImplementedError(\"pyclbr is not yet implemented in Skulpt\")\n","src/lib/pydoc.py":"raise NotImplementedError(\"pydoc is not yet implemented in Skulpt\")\n","src/lib/pydoc_topics.py":"raise NotImplementedError(\"pydoc_topics is not yet implemented in Skulpt\")\n","src/lib/pythonds/__init__.py":"","src/lib/pythonds/basic/__init__.py":"\n#__all__ = [\"stack\"]\n\n\nfrom .stack import Stack\nfrom .queue import Queue\nfrom .deque import Deque\n\n\n","src/lib/pythonds/basic/deque.py":"# Bradley N. Miller, David L. Ranum\n# Introduction to Data Structures and Algorithms in Python\n# Copyright 2005\n#\n# deque.py\n\n\nclass Deque:\n    def __init__(self):\n        self.items = []\n\n    def isEmpty(self):\n        return self.items == []\n\n    def addFront(self, item):\n        self.items.append(item)\n\n    def addRear(self, item):\n        self.items.insert(0, item)\n\n    def removeFront(self):\n        return self.items.pop()\n\n    def removeRear(self):\n        return self.items.pop(0)\n\n    def size(self):\n        return len(self.items)\n","src/lib/pythonds/basic/queue.py":"# Bradley N. Miller, David L. Ranum\n# Introduction to Data Structures and Algorithms in Python\n# Copyright 2005\n#\n# queue.py\n\n\nclass Queue:\n    def __init__(self):\n        self.items = []\n\n    def isEmpty(self):\n        return self.items == []\n\n    def enqueue(self, item):\n        self.items.insert(0, item)\n\n    def dequeue(self):\n        return self.items.pop()\n\n    def size(self):\n        return len(self.items)\n","src/lib/pythonds/basic/stack.py":"# Bradley N. Miller, David L. Ranum\n# Introduction to Data Structures and Algorithms in Python\n# Copyright 2005\n#\n# stack.py\n\n\nclass Stack:\n    def __init__(self):\n        self.items = []\n\n    def isEmpty(self):\n        return self.items == []\n\n    def push(self, item):\n        self.items.append(item)\n\n    def pop(self):\n        return self.items.pop()\n\n    def peek(self):\n        return self.items[len(self.items) - 1]\n\n    def size(self):\n        return len(self.items)\n","src/lib/pythonds/graphs/__init__.py":"\n\nfrom .adjGraph import Graph\nfrom .adjGraph import Vertex\nfrom .priorityQueue import PriorityQueue\n","src/lib/pythonds/graphs/adjGraph.py":"#\n#  adjGraph\n#\n#  Created by Brad Miller on 2005-02-24.\n#  Copyright (c) 2005 Brad Miller, David Ranum, Luther College. All rights reserved.\n#\n\nimport sys\nimport os\nimport unittest\n\nclass Graph:\n    def __init__(self):\n        self.vertices = {}\n        self.numVertices = 0\n        \n    def addVertex(self,key):\n        self.numVertices = self.numVertices + 1\n        newVertex = Vertex(key)\n        self.vertices[key] = newVertex\n        return newVertex\n    \n    def getVertex(self,n):\n        if n in self.vertices:\n            return self.vertices[n]\n        else:\n            return None\n\n    def __contains__(self,n):\n        return n in self.vertices\n    \n    def addEdge(self,f,t,cost=0):\n            if f not in self.vertices:\n                nv = self.addVertex(f)\n            if t not in self.vertices:\n                nv = self.addVertex(t)\n            self.vertices[f].addNeighbor(self.vertices[t],cost)\n    \n    def getVertices(self):\n        return list(self.vertices.keys())\n        \n    def __iter__(self):\n        return iter(self.vertices.values())\n                \nclass Vertex:\n    def __init__(self,num):\n        self.id = num\n        self.connectedTo = {}\n        self.color = 'white'\n        self.dist = sys.maxsize\n        self.pred = None\n        self.disc = 0\n        self.fin = 0\n\n    # def __lt__(self,o):\n    #     return self.id < o.id\n    \n    def addNeighbor(self,nbr,weight=0):\n        self.connectedTo[nbr] = weight\n        \n    def setColor(self,color):\n        self.color = color\n        \n    def setDistance(self,d):\n        self.dist = d\n\n    def setPred(self,p):\n        self.pred = p\n\n    def setDiscovery(self,dtime):\n        self.disc = dtime\n        \n    def setFinish(self,ftime):\n        self.fin = ftime\n        \n    def getFinish(self):\n        return self.fin\n        \n    def getDiscovery(self):\n        return self.disc\n        \n    def getPred(self):\n        return self.pred\n        \n    def getDistance(self):\n        return self.dist\n        \n    def getColor(self):\n        return self.color\n    \n    def getConnections(self):\n        return self.connectedTo.keys()\n        \n    def getWeight(self,nbr):\n        return self.connectedTo[nbr]\n                \n    def __str__(self):\n        return str(self.id) + \":color \" + self.color + \":disc \" + str(self.disc) + \":fin \" + str(self.fin) + \":dist \" + str(self.dist) + \":pred \\n\\t[\" + str(self.pred)+ \"]\\n\"\n    \n    def getId(self):\n        return self.id\n\nclass adjGraphTests(unittest.TestCase):\n    def setUp(self):\n        self.tGraph = Graph()\n        \n    def testMakeGraph(self):\n        gFile = open(\"test.dat\")\n        for line in gFile:\n            fVertex, tVertex = line.split('|')\n            fVertex = int(fVertex)\n            tVertex = int(tVertex)\n            self.tGraph.addEdge(fVertex,tVertex)\n        for i in self.tGraph:\n            adj = i.getAdj()\n            for k in adj:\n                print(i, k)\n\n        \nif __name__ == '__main__':\n    unittest.main()\n              \n","src/lib/pythonds/graphs/priorityQueue.py":"# Bradley N. Miller, David L. Ranum\n# Introduction to Data Structures and Algorithms in Python\n# Copyright 2005\n# \nimport unittest\n\n# this implementation of binary heap takes key value pairs,\n# we will assume that the keys are all comparable\n\nclass PriorityQueue:\n    def __init__(self):\n        self.heapArray = [(0,0)]\n        self.currentSize = 0\n\n    def buildHeap(self,alist):\n        self.currentSize = len(alist)\n        self.heapArray = [(0,0)]\n        for i in alist:\n            self.heapArray.append(i)\n        i = len(alist) // 2            \n        while (i > 0):\n            self.percDown(i)\n            i = i - 1\n                        \n    def percDown(self,i):\n        while (i * 2) <= self.currentSize:\n            mc = self.minChild(i)\n            if self.heapArray[i][0] > self.heapArray[mc][0]:\n                tmp = self.heapArray[i]\n                self.heapArray[i] = self.heapArray[mc]\n                self.heapArray[mc] = tmp\n            i = mc\n                \n    def minChild(self,i):\n        if i*2 > self.currentSize:\n            return -1\n        else:\n            if i*2 + 1 > self.currentSize:\n                return i*2\n            else:\n                if self.heapArray[i*2][0] < self.heapArray[i*2+1][0]:\n                    return i*2\n                else:\n                    return i*2+1\n\n    def percUp(self,i):\n        while i // 2 > 0:\n            if self.heapArray[i][0] < self.heapArray[i//2][0]:\n               tmp = self.heapArray[i//2]\n               self.heapArray[i//2] = self.heapArray[i]\n               self.heapArray[i] = tmp\n            i = i//2\n \n    def add(self,k):\n        self.heapArray.append(k)\n        self.currentSize = self.currentSize + 1\n        self.percUp(self.currentSize)\n\n    def delMin(self):\n        retval = self.heapArray[1][1]\n        self.heapArray[1] = self.heapArray[self.currentSize]\n        self.currentSize = self.currentSize - 1\n        self.heapArray.pop()\n        self.percDown(1)\n        return retval\n        \n    def isEmpty(self):\n        if self.currentSize == 0:\n            return True\n        else:\n            return False\n\n    def decreaseKey(self,val,amt):\n        # this is a little wierd, but we need to find the heap thing to decrease by\n        # looking at its value\n        done = False\n        i = 1\n        myKey = 0\n        while not done and i <= self.currentSize:\n            if self.heapArray[i][1] == val:\n                done = True\n                myKey = i\n            else:\n                i = i + 1\n        if myKey > 0:\n            self.heapArray[myKey] = (amt,self.heapArray[myKey][1])\n            self.percUp(myKey)\n            \n    def __contains__(self,vtx):\n        for pair in self.heapArray:\n            if pair[1] == vtx:\n                return True\n        return False\n        \nclass TestBinHeap(unittest.TestCase):\n    def setUp(self):\n        self.theHeap = PriorityQueue()\n        self.theHeap.add((2,'x'))\n        self.theHeap.add((3,'y'))\n        self.theHeap.add((5,'z'))\n        self.theHeap.add((6,'a'))\n        self.theHeap.add((4,'d'))\n\n\n    def testInsert(self):\n        assert self.theHeap.currentSize == 5\n\n    def testDelmin(self):\n        assert self.theHeap.delMin() == 'x'\n        assert self.theHeap.delMin() == 'y'\n    \n    def testDecKey(self):\n        self.theHeap.decreaseKey('d',1)\n        assert self.theHeap.delMin() == 'd'\n        \nif __name__ == '__main__':\n    unittest.main()\n","src/lib/pythonds/trees/__init__.py":"\nfrom .binaryTree import BinaryTree\nfrom .balance import AVLTree\nfrom .bst import BinarySearchTree\nfrom .binheap import BinHeap\n\n\n","src/lib/pythonds/trees/balance.py":"#!/bin/env python3.1\n# Bradley N. Miller, David L. Ranum\n# Introduction to Data Structures and Algorithms in Python\n# Copyright 2005, 2010\n# \n\nfrom .bst import BinarySearchTree, TreeNode\n\nclass AVLTree(BinarySearchTree):\n    '''\n    Author:  Brad Miller\n    Date:  1/15/2005\n    Description:  Imlement a binary search tree with the following interface\n                  functions:  \n                  __contains__(y) <==> y in x\n                  __getitem__(y) <==> x[y]\n                  __init__()\n                  __len__() <==> len(x)\n                  __setitem__(k,v) <==> x[k] = v\n                  clear()\n                  get(k)\n                  has_key(k)\n                  items() \n                  keys() \n                  values()\n                  put(k,v)\n    '''\n\n\n    def _put(self,key,val,currentNode):\n        if key < currentNode.key:\n            if currentNode.hasLeftChild():\n                self._put(key,val,currentNode.leftChild)\n            else:\n                currentNode.leftChild = TreeNode(key,val,parent=currentNode)\n                self.updateBalance(currentNode.leftChild)\n        else:\n            if currentNode.hasRightChild():\n                self._put(key,val,currentNode.rightChild)\n            else:\n                currentNode.rightChild = TreeNode(key,val,parent=currentNode)\n                self.updateBalance(currentNode.rightChild)                \n\n    def updateBalance(self,node):\n        if node.balanceFactor > 1 or node.balanceFactor < -1:\n            self.rebalance(node)\n            return\n        if node.parent != None:\n            if node.isLeftChild():\n                node.parent.balanceFactor += 1\n            elif node.isRightChild():\n                node.parent.balanceFactor -= 1\n\n            if node.parent.balanceFactor != 0:\n                self.updateBalance(node.parent)\n\n    def rebalance(self,node):\n        if node.balanceFactor < 0:\n            if node.rightChild.balanceFactor > 0:\n                # Do an LR Rotation\n                self.rotateRight(node.rightChild)\n                self.rotateLeft(node)\n            else:\n                # single left\n                self.rotateLeft(node)\n        elif node.balanceFactor > 0:\n            if node.leftChild.balanceFactor < 0:\n                # Do an RL Rotation\n                self.rotateLeft(node.leftChild)\n                self.rotateRight(node)\n            else:\n                # single right\n                self.rotateRight(node)\n\n    def rotateLeft(self,rotRoot):\n        newRoot = rotRoot.rightChild\n        rotRoot.rightChild = newRoot.leftChild\n        if newRoot.leftChild != None:\n            newRoot.leftChild.parent = rotRoot\n        newRoot.parent = rotRoot.parent\n        if rotRoot.isRoot():\n            self.root = newRoot\n        else:\n            if rotRoot.isLeftChild():\n                rotRoot.parent.leftChild = newRoot\n            else:\n                rotRoot.parent.rightChild = newRoot\n        newRoot.leftChild = rotRoot\n        rotRoot.parent = newRoot\n        rotRoot.balanceFactor = rotRoot.balanceFactor + 1 - min(newRoot.balanceFactor, 0)\n        newRoot.balanceFactor = newRoot.balanceFactor + 1 + max(rotRoot.balanceFactor, 0)\n\n\n    def rotateRight(self,rotRoot):\n        newRoot = rotRoot.leftChild\n        rotRoot.leftChild = newRoot.rightChild\n        if newRoot.rightChild != None:\n            newRoot.rightChild.parent = rotRoot\n        newRoot.parent = rotRoot.parent\n        if rotRoot.isRoot():\n            self.root = newRoot\n        else:\n            if rotRoot.isRightChild():\n                rotRoot.parent.rightChild = newRoot\n            else:\n                rotRoot.parent.leftChild = newRoot\n        newRoot.rightChild = rotRoot\n        rotRoot.parent = newRoot\n        rotRoot.balanceFactor = rotRoot.balanceFactor - 1 - max(newRoot.balanceFactor, 0)\n        newRoot.balanceFactor = newRoot.balanceFactor - 1 + min(rotRoot.balanceFactor, 0)\n        \n","src/lib/pythonds/trees/binaryTree.py":"# Bradley N. Miller, David L. Ranum\n# Introduction to Data Structures and Algorithms in Python\n# Copyright 2005\n# \n\nclass BinaryTree:\n    \"\"\"\n    A recursive implementation of Binary Tree\n    Using links and Nodes approach.\n    \"\"\"    \n    def __init__(self,rootObj):\n        self.key = rootObj\n        self.leftChild = None\n        self.rightChild = None\n\n    def insertLeft(self,newNode):\n        if self.leftChild == None:\n            self.leftChild = BinaryTree(newNode)\n        else:\n            t = BinaryTree(newNode)\n            t.left = self.leftChild\n            self.leftChild = t\n    \n    def insertRight(self,newNode):\n        if self.rightChild == None:\n            self.rightChild = BinaryTree(newNode)\n        else:\n            t = BinaryTree(newNode)\n            t.right = self.rightChild\n            self.rightChild = t\n\n    def isLeaf(self):\n        return ((not self.leftChild) and (not self.rightChild))\n\n    def getRightChild(self):\n        return self.rightChild\n\n    def getLeftChild(self):\n        return self.leftChild\n\n    def setRootVal(self,obj):\n        self.key = obj\n\n    def getRootVal(self,):\n        return self.key\n\n    def inorder(self):\n        if self.leftChild:\n            self.leftChild.inorder()\n        print(self.key)\n        if self.rightChild:\n            self.rightChild.inorder()\n\n    def postorder(self):\n        if self.leftChild:\n            self.leftChild.postorder()\n        if self.rightChild:\n            self.rightChild.postorder()\n        print(self.key)\n\n\n    def preorder(self):\n        print(self.key)\n        if self.leftChild:\n            self.leftChild.preorder()\n        if self.rightChild:\n            self.rightChild.preorder()\n\n    def printexp(self):\n        if self.leftChild:\n            print('(')\n            self.leftChild.printexp()\n        print(self.key)\n        if self.rightChild:\n            self.rightChild.printexp()\n            print(')')\n\n    def postordereval(self):\n        opers = {'+':operator.add, '-':operator.sub, '*':operator.mul, '/':operator.truediv}\n        res1 = None\n        res2 = None\n        if self.leftChild:\n            res1 = self.leftChild.postordereval()  #// \\label{peleft}\n        if self.rightChild:\n            res2 = self.rightChild.postordereval() #// \\label{peright}\n        if res1 and res2:\n            return opers[self.key](res1,res2) #// \\label{peeval}\n        else:\n            return self.key\n\ndef inorder(tree):\n    if tree != None:\n        inorder(tree.getLeftChild())\n        print(tree.getRootVal())\n        inorder(tree.getRightChild())\n\ndef printexp(tree):\n    if tree.leftChild:\n        print('(')\n        printexp(tree.getLeftChild())\n    print(tree.getRootVal())\n    if tree.rightChild:\n        printexp(tree.getRightChild())\n        print(')') \n\ndef printexp(tree):\n    sVal = \"\"\n    if tree:\n        sVal = '(' + printexp(tree.getLeftChild())\n        sVal = sVal + str(tree.getRootVal())\n        sVal = sVal + printexp(tree.getRightChild()) + ')'\n    return sVal\n\ndef postordereval(tree):\n    opers = {'+':operator.add, '-':operator.sub, '*':operator.mul, '/':operator.truediv}\n    res1 = None\n    res2 = None\n    if tree:\n        res1 = postordereval(tree.getLeftChild())  #// \\label{peleft}\n        res2 = postordereval(tree.getRightChild()) #// \\label{peright}\n        if res1 and res2:\n            return opers[tree.getRootVal()](res1,res2) #// \\label{peeval}\n        else:\n            return tree.getRootVal()\n\ndef height(tree):\n    if tree == None:\n        return -1\n    else:\n        return 1 + max(height(tree.leftChild),height(tree.rightChild))\n\n# t = BinaryTree(7)\n# t.insertLeft(3)\n# t.insertRight(9)\n# inorder(t)\n# import operator\n# x = BinaryTree('*')\n# x.insertLeft('+')\n# l = x.getLeftChild()\n# l.insertLeft(4)\n# l.insertRight(5)\n# x.insertRight(7)\n# print(printexp(x))\n# print(postordereval(x))\n# print(height(x))\n","src/lib/pythonds/trees/binheap.py":"# Bradley N. Miller, David L. Ranum\n# Introduction to Data Structures and Algorithms in Python\n# Copyright 2005\n# \n\n# this heap takes key value pairs, we will assume that the keys are integers\nclass BinHeap:\n    def __init__(self):\n        self.heapList = [0]\n        self.currentSize = 0\n\n\n    def buildHeap(self,alist):\n        i = len(alist) // 2\n        self.currentSize = len(alist)\n        self.heapList = [0] + alist[:]\n        print(len(self.heapList), i)\n        while (i > 0):\n            print(self.heapList, i)\n            self.percDown(i)\n            i = i - 1\n        print(self.heapList,i)\n                        \n    def percDown(self,i):\n        while (i * 2) <= self.currentSize:\n            mc = self.minChild(i)\n            if self.heapList[i] > self.heapList[mc]:\n                tmp = self.heapList[i]\n                self.heapList[i] = self.heapList[mc]\n                self.heapList[mc] = tmp\n            i = mc\n                \n    def minChild(self,i):\n        if i * 2 + 1 > self.currentSize:\n            return i * 2\n        else:\n            if self.heapList[i * 2] < self.heapList[i * 2 + 1]:\n                return i * 2\n            else:\n                return i * 2 + 1\n\n    def percUp(self,i):\n        while i // 2 > 0:\n            if self.heapList[i] < self.heapList[i//2]:\n               tmp = self.heapList[i // 2]\n               self.heapList[i // 2] = self.heapList[i]\n               self.heapList[i] = tmp\n            i = i // 2\n \n    def insert(self,k):\n        self.heapList.append(k)\n        self.currentSize = self.currentSize + 1\n        self.percUp(self.currentSize)\n\n    def delMin(self):\n        retval = self.heapList[1]\n        self.heapList[1] = self.heapList[self.currentSize]\n        self.currentSize = self.currentSize - 1\n        self.heapList.pop()\n        self.percDown(1)\n        return retval\n        \n    def isEmpty(self):\n        if currentSize == 0:\n            return True\n        else:\n            return False\n","src/lib/pythonds/trees/bst.py":"#!/bin/env python3.1\n# Bradley N. Miller, David L. Ranum\n# Introduction to Data Structures and Algorithms in Python\n# Copyright 2005, 2010\n# \n\nclass BinarySearchTree:\n    '''\n    Author:  Brad Miller\n    Date:  1/15/2005\n    Description:  Imlement a binary search tree with the following interface\n                  functions:  \n                  __contains__(y) <==> y in x\n                  __getitem__(y) <==> x[y]\n                  __init__()\n                  __len__() <==> len(x)\n                  __setitem__(k,v) <==> x[k] = v\n                  clear()\n                  get(k)\n                  items() \n                  keys() \n                  values()\n                  put(k,v)\n                  in\n                  del <==> \n    '''\n\n    def __init__(self):\n        self.root = None\n        self.size = 0\n    \n    def put(self,key,val):\n        if self.root:\n            self._put(key,val,self.root)\n        else:\n            self.root = TreeNode(key,val)\n        self.size = self.size + 1\n\n    def _put(self,key,val,currentNode):\n        if key < currentNode.key:\n            if currentNode.hasLeftChild():\n                self._put(key,val,currentNode.leftChild)\n            else:\n                currentNode.leftChild = TreeNode(key,val,parent=currentNode)\n        else:\n            if currentNode.hasRightChild():\n                self._put(key,val,currentNode.rightChild)\n            else:\n                currentNode.rightChild = TreeNode(key,val,parent=currentNode)\n            \n    def __setitem__(self,k,v):\n        self.put(k,v)\n\n    def get(self,key):\n        if self.root:\n            res = self._get(key,self.root)\n            if res:\n                return res.payload\n            else:\n                return None\n        else:\n            return None\n        \n    def _get(self,key,currentNode):\n        if not currentNode:\n            return None\n        elif currentNode.key == key:\n            return currentNode\n        elif key < currentNode.key:\n            return self._get(key,currentNode.leftChild)\n        else:\n            return self._get(key,currentNode.rightChild)\n            \n        \n    def __getitem__(self,key):\n        res = self.get(key)\n        if res:\n            return res\n        else:\n            raise KeyError('Error, key not in tree')\n            \n\n    def __contains__(self,key):\n        if self._get(key,self.root):\n            return True\n        else:\n            return False\n        \n    def length(self):\n        return self.size\n\n    def __len__(self):\n        return self.size\n\n    def __iter__(self):\n        return self.root.__iter__()\n    \n    def delete(self,key):\n        if self.size > 1:\n            nodeToRemove = self._get(key,self.root)\n            if nodeToRemove:\n                self.remove(nodeToRemove)\n                self.size = self.size-1\n            else:\n                raise KeyError('Error, key not in tree')\n        elif self.size == 1 and self.root.key == key:\n            self.root = None\n            self.size = self.size - 1\n        else:\n            raise KeyError('Error, key not in tree')\n\n    def __delitem__(self,key):\n        self.delete(key)\n    \n    def remove(self,currentNode):\n        if currentNode.isLeaf(): #leaf\n            if currentNode == currentNode.parent.leftChild:\n                currentNode.parent.leftChild = None\n            else:\n                currentNode.parent.rightChild = None\n        elif currentNode.hasBothChildren(): #interior\n            succ = currentNode.findSuccessor()\n            succ.spliceOut()\n            currentNode.key = succ.key\n            currentNode.payload = succ.payload\n        else: # this node has one child\n            if currentNode.hasLeftChild():\n                if currentNode.isLeftChild():\n                    currentNode.leftChild.parent = currentNode.parent\n                    currentNode.parent.leftChild = currentNode.leftChild\n                elif currentNode.isRightChild():\n                    currentNode.leftChild.parent = currentNode.parent\n                    currentNode.parent.rightChild = currentNode.leftChild\n                else:\n                    currentNode.replaceNodeData(currentNode.leftChild.key,\n                                       currentNode.leftChild.payload,\n                                       currentNode.leftChild.leftChild,\n                                       currentNode.leftChild.rightChild)\n            else:\n                if currentNode.isLeftChild():\n                    currentNode.rightChild.parent = currentNode.parent\n                    currentNode.parent.leftChild = currentNode.rightChild\n                elif currentNode.isRightChild():\n                    currentNode.rightChild.parent = currentNode.parent\n                    currentNode.parent.rightChild = currentNode.rightChild\n                else:\n                    currentNode.replaceNodeData(currentNode.rightChild.key,\n                                       currentNode.rightChild.payload,\n                                       currentNode.rightChild.leftChild,\n                                       currentNode.rightChild.rightChild)\n\n    def inorder(self):\n        self._inorder(self.root)\n\n    def _inorder(self,tree):\n        if tree != None:\n            self._inorder(tree.leftChild)\n            print(tree.key)\n            self._inorder(tree.rightChild)\n\n    def postorder(self):\n        self._postorder(self.root)\n\n    def _postorder(self, tree):\n        if tree:\n            self._postorder(tree.rightChild)\n            self._postorder(tree.leftChild)\n            print(tree.key)            \n\n    def preorder(self):\n        self._preorder(self,self.root)\n\n    def _preorder(self,tree):\n        if tree:\n            print(tree.key)            \n            self._preorder(tree.leftChild)\n            self._preorder(tree.rightChild)\n\n                \nclass TreeNode:\n    def __init__(self,key,val,left=None,right=None,parent=None):\n        self.key = key\n        self.payload = val\n        self.leftChild = left\n        self.rightChild = right\n        self.parent = parent\n        self.balanceFactor = 0\n        \n    def hasLeftChild(self):\n        return self.leftChild\n\n    def hasRightChild(self):\n        return self.rightChild\n    \n    def isLeftChild(self):\n        return self.parent and self.parent.leftChild == self\n\n    def isRightChild(self):\n        return self.parent and self.parent.rightChild == self\n\n    def isRoot(self):\n        return not self.parent\n\n    def isLeaf(self):\n        return not (self.rightChild or self.leftChild)\n\n    def hasAnyChildren(self):\n        return self.rightChild or self.leftChild\n\n    def hasBothChildren(self):\n        return self.rightChild and self.leftChild\n    \n    def replaceNodeData(self,key,value,lc,rc):\n        self.key = key\n        self.payload = value\n        self.leftChild = lc\n        self.rightChild = rc\n        if self.hasLeftChild():\n            self.leftChild.parent = self\n        if self.hasRightChild():\n            self.rightChild.parent = self\n        \n    def findSuccessor(self):\n        succ = None\n        if self.hasRightChild():\n            succ = self.rightChild.findMin()\n        else:\n            if self.parent:\n                if self.isLeftChild():\n                    succ = self.parent\n                else:\n                    self.parent.rightChild = None\n                    succ = self.parent.findSuccessor()\n                    self.parent.rightChild = self\n        return succ\n\n\n    def spliceOut(self):\n        if self.isLeaf():\n            if self.isLeftChild():\n                self.parent.leftChild = None\n            else:\n                self.parent.rightChild = None\n        elif self.hasAnyChildren():\n            if self.hasLeftChild():\n                if self.isLeftChild():\n                    self.parent.leftChild = self.leftChild\n                else:\n                    self.parent.rightChild = self.leftChild\n                self.leftChild.parent = self.parent\n            else:\n                if self.isLeftChild():\n                    self.parent.leftChild = self.rightChild\n                else:\n                    self.parent.rightChild = self.rightChild\n                self.rightChild.parent = self.parent\n\n    def findMin(self):\n        current = self\n        while current.hasLeftChild():\n            current = current.leftChild\n        return current\n\n    def __iter__(self):\n        \"\"\"The standard inorder traversal of a binary tree.\"\"\"\n        if self:\n            if self.hasLeftChild():\n                for elem in self.leftChild:\n                    yield elem\n            yield self.key\n            if self.hasRightChild():\n                for elem in self.rightChild:\n                    yield elem\n\n            \n","src/lib/pythonds3/__init__.py":"#!/usr/bin/env python3\n\"\"\"\npythonds3 import statement\n\"\"\"\n\nfrom pythonds3.basic import Stack, Queue, Deque\nfrom pythonds3.graphs import Graph, Vertex\nfrom pythonds3.searching import HashTable\nfrom pythonds3.trees import (\n    BinaryTree,\n    AVLTree,\n    BinarySearchTree,\n    BinaryHeap,\n    PriorityQueue,\n)\n\n__all__ = [\n    \"Stack\",\n    \"Queue\",\n    \"Deque\",\n    \"Graph\",\n    \"Vertex\",\n    \"HashTable\",\n    \"BinaryTree\",\n    \"AVLTree\",\n    \"BinarySearchTree\",\n    \"BinaryHeap\",\n    \"BinaryTree\",\n    \"PriorityQueue\",\n]\n","src/lib/pythonds3/basic/__init__.py":"#!/usr/bin/env python3\n\"\"\"\npythonds3.basic import statement\n\"\"\"\nfrom pythonds3.basic.stack import Stack\nfrom pythonds3.basic.queue import Queue\nfrom pythonds3.basic.deque import Deque\nfrom pythonds3.basic.linked_list import OrderedList\nfrom pythonds3.basic.linked_list import UnorderedList\n\n__all__ = [\"Stack\", \"Queue\", \"Deque\", \"OrderedList\", \"UnorderedList\"]\n","src/lib/pythonds3/basic/deque.py":"#!/usr/bin/env python3\n\"\"\"\nBradley N. Miller, David L. Ranum\nProblem Solving with Algorithms and Data Structures using Python\nCopyright 2005\nUpdated by Roman Yasinovskyy, 2017\n\"\"\"\n\n\nclass Deque:\n    \"\"\"Queue implementation as a list\"\"\"\n\n    def __init__(self):\n        \"\"\"Create new deque\"\"\"\n        self._items = []\n\n    def is_empty(self):\n        \"\"\"Check if the deque is empty\"\"\"\n        return not bool(self._items)\n\n    def add_front(self, item):\n        \"\"\"Add an item to the front of the deque\"\"\"\n        self._items.append(item)\n\n    def add_rear(self, item):\n        \"\"\"Add an item to the rear of the deque\"\"\"\n        self._items.insert(0, item)\n\n    def remove_front(self):\n        \"\"\"Remove an item from the front of the deque\"\"\"\n        return self._items.pop()\n\n    def remove_rear(self):\n        \"\"\"Remove an item from the rear of the deque\"\"\"\n        return self._items.pop(0)\n\n    def size(self):\n        \"\"\"Get the number of items in the deque\"\"\"\n        return len(self._items)\n","src/lib/pythonds3/basic/linked_list.py":"#!/usr/bin/env python3\n\"\"\"\nBradley N. Miller, David L. Ranum\nProblem Solving with Algorithms and Data Structures using Python\nCopyright 2005\nUpdated by Roman Yasinovskyy, 2017\n\"\"\"\n\n\nclass LinkedListNode:\n    \"\"\"A node of a linked list\"\"\"\n\n    def __init__(self, node_data):\n        self._data = node_data\n        self._next = None\n\n    def get_data(self):\n        \"\"\"Get node data\"\"\"\n        return self._data\n\n    def set_data(self, node_data):\n        \"\"\"Set node data\"\"\"\n        self._data = node_data\n\n    data = property(get_data, set_data)\n\n    def get_next(self):\n        \"\"\"Get next node\"\"\"\n        return self._next\n\n    def set_next(self, node_next):\n        \"\"\"Set next node\"\"\"\n        self._next = node_next\n\n    next = property(get_next, set_next)\n\n    def __str__(self):\n        \"\"\"String\"\"\"\n        return str(self._data)\n\n\nclass LinkedList:\n    \"\"\"Linked List class implementation\"\"\"\n\n    def __init__(self):\n        \"\"\"Create a linked list\"\"\"\n        self._head = None\n        self._count = 0\n\n    def is_empty(self):\n        \"\"\"Is the list empty\"\"\"\n        return self._head is None\n\n    def size(self):\n        \"\"\"Size of the list\"\"\"\n        return self._count\n\n    def __len__(self):\n        \"\"\"Size of the list\"\"\"\n        return self._count\n\n    def __str__(self):\n        \"\"\"List as a string\"\"\"\n        list_str = \"[\"\n        current = self._head\n\n        while current:\n            list_str += str(current)\n            if current.next:\n                list_str += \", \"\n            current = current.next\n        list_str += \"]\"\n        return list_str\n\n    def add(self, value):\n        \"\"\"Add a new node\"\"\"\n        pass\n\n    def remove(self, value):\n        \"\"\"Remove a node with a specific value\"\"\"\n        pass\n\n    def search(self, value):\n        \"\"\"Search for a node with a specific value\"\"\"\n        pass\n\n\nclass UnorderedList(LinkedList):\n    \"\"\"Unordered linked list implementation\"\"\"\n\n    def __init__(self):\n        \"\"\"Create an unordered linked list\"\"\"\n        LinkedList.__init__(self)\n\n    def add(self, value):\n        \"\"\"Add a new node\"\"\"\n        new_node = LinkedListNode(value)\n        new_node.set_next(self._head)\n        self._head = new_node\n        self._count = self._count + 1\n\n    def remove(self, value):\n        \"\"\"Remove a node with a specific value\"\"\"\n        current = self._head\n        prev = None\n\n        while current:\n            if current.data == value:\n                if prev is None:\n                    self._head = current.next\n                else:\n                    prev.next = current.next\n                self._count = self._count - 1\n                return\n            prev = current\n            current = current.next\n        raise ValueError(\"{} is not in the list\".format(value))\n\n    def search(self, value):\n        \"\"\"Search for a node with a specific value\"\"\"\n        current = self._head\n\n        while current:\n            if current.data == value:\n                return True\n            current = current.next\n        return False\n\n\nclass OrderedList(LinkedList):\n    \"\"\"Ordered linked list implementation\"\"\"\n\n    def __init__(self):\n        \"\"\"Create an Ordered linked list\"\"\"\n        LinkedList.__init__(self)\n\n    def add(self, value):\n        \"\"\"Add a new node\"\"\"\n        current = self._head\n        prev = None\n        new_node = LinkedListNode(value)\n\n        while current and current.data < value:\n            prev = current\n            current = current.next\n\n        if prev is None:\n            new_node.next = self._head\n            self._head = new_node\n        else:\n            new_node.next = current\n            prev.next = new_node\n        self._count = self._count + 1\n\n    def remove(self, value):\n        \"\"\"Remove a node with a specific value\"\"\"\n        # This is an exercise\n        pass\n\n    def search(self, value):\n        \"\"\"Search for a node with a specific value\"\"\"\n        # This is an exercise\n        pass\n","src/lib/pythonds3/basic/queue.py":"#!/usr/bin/env python3\n\"\"\"\nBradley N. Miller, David L. Ranum\nProblem Solving with Algorithms and Data Structures using Python\nCopyright 2005\nUpdated by Roman Yasinovskyy, 2017\n\"\"\"\n\n\nclass Queue:\n    \"\"\"Queue implementation as a list\"\"\"\n\n    def __init__(self):\n        \"\"\"Create new queue\"\"\"\n        self._items = []\n\n    def is_empty(self):\n        \"\"\"Check if the queue is empty\"\"\"\n        return not bool(self._items)\n\n    def enqueue(self, item):\n        \"\"\"Add an item to the queue\"\"\"\n        self._items.insert(0, item)\n\n    def dequeue(self):\n        \"\"\"Remove an item from the queue\"\"\"\n        return self._items.pop()\n\n    def size(self):\n        \"\"\"Get the number of items in the queue\"\"\"\n        return len(self._items)\n","src/lib/pythonds3/basic/stack.py":"#!/usr/bin/env python3\n\"\"\"\nBradley N. Miller, David L. Ranum\nProblem Solving with Algorithms and Data Structures using Python\nCopyright 2005\nUpdated by Roman Yasinovskyy, 2017\n\"\"\"\n\n\nclass Stack:\n    \"\"\"Stack implementation as a list\"\"\"\n\n    def __init__(self):\n        \"\"\"Create new stack\"\"\"\n        self._items = []\n\n    def is_empty(self):\n        \"\"\"Check if the stack is empty\"\"\"\n        return not bool(self._items)\n\n    def push(self, item):\n        \"\"\"Add an item to the stack\"\"\"\n        self._items.append(item)\n\n    def pop(self):\n        \"\"\"Remove an item from the stack\"\"\"\n        return self._items.pop()\n\n    def peek(self):\n        \"\"\"Get the value of the top item in the stack\"\"\"\n        return self._items[-1]\n\n    def size(self):\n        \"\"\"Get the number of items in the stack\"\"\"\n        return len(self._items)\n","src/lib/pythonds3/graphs/__init__.py":"#!/usr/bin/env python3\n\"\"\"\npythonds3.graphs import statement\n\"\"\"\nfrom pythonds3.graphs.adjacency_graph import Graph\nfrom pythonds3.graphs.adjacency_graph import Vertex\n\n__all__ = [\"Graph\"]\n","src/lib/pythonds3/graphs/adjacency_graph.py":"#!/usr/bin/env python3\n\"\"\"\nBradley N. Miller, David L. Ranum\nProblem Solving with Algorithms and Data Structures using Python\nCopyright 2005\nUpdated by Roman Yasinovskyy, 2017\n\"\"\"\n\nimport heapq\nimport sys\n\n\nclass Vertex:\n    \"\"\"Graph vertex class\"\"\"\n\n    def __init__(self, key):\n        \"\"\"Create new vertex\"\"\"\n        self._key = key\n        self._neighbors = {}\n        self._color = \"white\"\n        self._distance = sys.maxsize\n        self._previous = None\n        self._discovery_time = 0\n        self._closing_time = 0\n\n    def __lt__(self, other):\n        \"\"\"Less than operator required for heapify\"\"\"\n        return self.key < other.key\n\n    def get_key(self):\n        \"\"\"Get vertex key\"\"\"\n        return self._key\n\n    key = property(get_key)\n\n    def get_neighbor(self, other):\n        \"\"\"Get the distance (edge weight) to an adjacent node (neighbor)\"\"\"\n        return self._neighbors.get(other, None)\n\n    def set_neighbor(self, other, weight=0):\n        \"\"\"Set the distance (add an edge) to an adjacent node (neighbor)\"\"\"\n        self._neighbors[other] = weight\n\n    def get_neighbors(self):\n        \"\"\"Get all adjacent nodes (neighbors)\"\"\"\n        return self._neighbors.keys()\n\n    def get_color(self):\n        \"\"\"Get vertex color\"\"\"\n        return self._color\n\n    def set_color(self, color):\n        \"\"\"Set vertex color\"\"\"\n        self._color = color\n\n    color = property(get_color, set_color)\n\n    def get_distance(self):\n        \"\"\"Get distance\"\"\"\n        return self._distance\n\n    def set_distance(self, distance):\n        \"\"\"Set distance\"\"\"\n        self._distance = distance\n\n    distance = property(get_distance, set_distance)\n\n    def get_previous(self):\n        \"\"\"Get previous\"\"\"\n        return self._previous\n\n    def set_previous(self, previous):\n        \"\"\"Set previous\"\"\"\n        self._previous = previous\n\n    previous = property(get_previous, set_previous)\n\n    def get_discovery_time(self):\n        \"\"\"Get discovery time\"\"\"\n        return self._discovery_time\n\n    def set_discovery_time(self, discovery_time):\n        \"\"\"Set discovery time\"\"\"\n        self._discovery_time = discovery_time\n\n    discovery_time = property(get_discovery_time, set_discovery_time)\n\n    def get_closing_time(self):\n        \"\"\"Get closing time\"\"\"\n        return self._closing_time\n\n    def set_closing_time(self, closing_time):\n        \"\"\"Set closing time\"\"\"\n        self._closing_time = closing_time\n\n    closing_time = property(get_closing_time, set_closing_time)\n\n    def __str__(self):\n        return \"{:^8}|{:^8}|{:^8}|{:^8}|{:^8}| {}\".format(\n            self._key,\n            self._color,\n            self._distance,\n            self._discovery_time,\n            self._closing_time,\n            self._previous,\n        )\n\n\nclass Graph:\n    \"\"\"Graph as an adjacency matrix\"\"\"\n\n    def __init__(self):\n        self._vertices = {}\n        self._edges = {}\n        self._time = 0\n\n    def __iter__(self):\n        \"\"\"Iterator\"\"\"\n        return iter(self._vertices.values())\n\n    def size(self):\n        \"\"\"Graph's size\"\"\"\n        return len(self._vertices)\n\n    def __len__(self):\n        \"\"\"Graph's size\"\"\"\n        return len(self._vertices)\n\n    def __contains__(self, key):\n        \"\"\"in operator override\"\"\"\n        return key in self._vertices\n\n    def get_vertex(self, key):\n        \"\"\"Find the vertex in the graph named vert_key\"\"\"\n        return self._vertices.get(key, None)\n\n    def set_vertex(self, key):\n        \"\"\"Add an instance of Vertex to the graph\"\"\"\n        self._vertices[key] = Vertex(key)\n\n    def add_edge(self, from_vertex, to_vertex, weight=0):\n        \"\"\"Add a weighted and directed edge to the graph\"\"\"\n        if from_vertex not in self._vertices:\n            self.set_vertex(from_vertex)\n        if to_vertex not in self._vertices:\n            self.set_vertex(to_vertex)\n        self._vertices[from_vertex].set_neighbor(self._vertices[to_vertex], weight)\n        self._edges[(from_vertex, to_vertex)] = weight\n\n    def get_vertices(self):\n        \"\"\"Return the list of all vertices in the graph\"\"\"\n        return self._vertices.keys()\n\n    def get_edges(self):\n        \"\"\"Return the list of all edges in the graph\"\"\"\n        return self._edges.keys()\n\n    def reset_distances(self, default_distance=sys.maxsize):\n        \"\"\"Reset distances to test Dijkstra's\"\"\"\n        for vertex in self:\n            vertex.distance = default_distance\n\n    def bfs(self, start):\n        \"\"\"Breadth First Search\"\"\"\n        start.distance = 0\n        start.previous = None\n        vert_queue = [start]\n        while vert_queue:\n            current_vert = vert_queue.pop(0)\n            for neigh in current_vert.get_neighbors():\n                if neigh.color == \"white\":\n                    neigh.color = \"gray\"\n                    neigh.distance = current_vert.distance + 1\n                    neigh.previous = current_vert\n                    vert_queue.append(neigh)\n            current_vert.color = \"black\"\n\n    def dfs(self):\n        \"\"\"Depth First search\"\"\"\n        for vertex in self:\n            if vertex.color == \"white\":\n                self.dfs_visit(vertex)\n\n    def dfs_visit(self, start):\n        \"\"\"DFS helper function\"\"\"\n        start.color = \"gray\"\n        self._time = self._time + 1\n        start.discovery_time = self._time\n        for next_vertex in start.get_neighbors():\n            if next_vertex.color == \"white\":\n                next_vertex.set_previous(start)\n                self.dfs_visit(next_vertex)\n        start.color = \"black\"\n        self._time = self._time + 1\n        start.closing_time = self._time\n\n    def traverse(self, src, dst):\n        \"\"\"Traverse a graph\"\"\"\n        path = []\n        current = self.get_vertex(dst)\n        while current:\n            path.append(current)\n            current = current.previous\n        print(\n            \"Path from {} to {} ({}): {}\".format(\n                self.get_vertex(src).key,\n                self.get_vertex(dst).key,\n                self.get_vertex(dst).distance,\n                \" \".join(vertex.key for vertex in reversed(path)),\n            )\n        )\n\n    def dijkstra(self, start):\n        \"\"\"Dijkstra's shortest path algorithm\"\"\"\n        start.distance = 0\n        not_yet_visited = [[start.distance, start]]\n        heapq.heapify(not_yet_visited)\n        while not_yet_visited:\n            current_vertex = heapq.heappop(not_yet_visited)[1]\n            for next_vertex in current_vertex.get_neighbors():\n                new_distance = current_vertex.distance + current_vertex.get_neighbor(\n                    next_vertex\n                )\n                if new_distance < next_vertex.distance:\n                    next_vertex.distance = new_distance\n                    next_vertex.previous = current_vertex\n                    found = False\n                    for vertex in not_yet_visited:\n                        if vertex[1].key == next_vertex.key:\n                            vertex[0] = next_vertex.distance\n                            heapq.heapify(not_yet_visited)\n                            found = True\n                    if not found:\n                        heapq.heappush(\n                            not_yet_visited, [next_vertex.distance, next_vertex]\n                        )\n\n    def bellman_ford(self, start):\n        \"\"\"Bellman-Ford shortest path algorithm\"\"\"\n        start.distance = 0\n        for _ in range(len(self._vertices)):\n            for edge in self._edges:\n                if (\n                    self.get_vertex(edge[0]).distance + self._edges[edge]\n                    < self.get_vertex(edge[1]).distance\n                ):\n                    self.get_vertex(edge[1]).distance = (\n                        self.get_vertex(edge[0]).distance + self._edges[edge]\n                    )\n                    self.get_vertex(edge[1]).previous = self.get_vertex(edge[0])\n        for edge in self._edges:\n            if (\n                self.get_vertex(edge[0]).distance + self._edges[edge]\n                < self.get_vertex(edge[1]).distance\n            ):\n                raise ValueError(\"Graph contains a negative-weight cycle\")\n\n    def prim(self, start):\n        \"\"\"Prim's spanning tree algorithm\"\"\"\n        start.distance = 0\n        not_in_a_tree = [[vertex.distance, vertex] for vertex in self]\n        heapq.heapify(not_in_a_tree)\n        while not_in_a_tree:\n            current_vertex = heapq.heappop(not_in_a_tree)[1]\n            for next_vertex in current_vertex.get_neighbors():\n                new_distance = current_vertex.get_neighbor(next_vertex)\n                if (\n                    any(item[1] == next_vertex for item in not_in_a_tree)\n                    and new_distance < next_vertex.distance\n                ):\n                    next_vertex.previous = current_vertex\n                    next_vertex.distance = new_distance\n                    for item in not_in_a_tree:\n                        if item[1] == next_vertex:\n                            item[0] = new_distance\n                            heapq.heapify(not_in_a_tree)\n                            break\n","src/lib/pythonds3/intro/__init__.py":"#!/usr/bin/env python3\n\"\"\"\npythonds3.intro import statement\n\"\"\"\nfrom pythonds3.intro.oop import Fraction\n\n__all__ = [\"Fraction\"]\n","src/lib/pythonds3/intro/oop.py":"#!/usr/bin/env python3\n\"\"\"\nBradley N. Miller, David L. Ranum\nProblem Solving with Algorithms and Data Structures using Python\nCopyright 2005\nUpdated by Roman Yasinovskyy, 2017\n\"\"\"\n\n\ndef gcd(number1, number2):\n    \"\"\"Helper function to simplify fractions\"\"\"\n    if not isinstance(number1, int) or not isinstance(number2, int):\n        raise TypeError(\"Numerator and denominator must be integers\")\n    while number1 % number2:\n        number1, number2 = number2, number1 % number2\n    return number2\n\n\nclass Fraction:\n    \"\"\"Fraction class\"\"\"\n\n    def __init__(self, new_numer, new_denom):\n        try:\n            common = gcd(new_numer, new_denom)\n        except TypeError:\n            raise\n        self._numer = new_numer // common\n        self._denom = new_denom // common\n\n    @property\n    def numer(self):\n        \"\"\"Get numerator\"\"\"\n        return self._numer\n\n    @numer.setter\n    def set_numer(self, new_numer):\n        \"\"\"Set numerator\"\"\"\n        self._numer = new_numer\n\n    def get_denom(self):\n        \"\"\"Get deniminator\"\"\"\n        return self._denom\n\n    def set_denom(self, new_denom):\n        \"\"\"Set deniminator\"\"\"\n        self._denom = new_denom\n\n    # Another way to create an attribute\n    denom = property(get_denom, set_denom)\n\n    def __str__(self):\n        \"\"\"Get the string value\"\"\"\n        if self._numer > self._denom:\n            return \"{} {}/{}\".format(\n                self._numer // self._denom, self._numer % self._denom, self._denom\n            )\n        return \"{}/{}\".format(self._numer, self._denom)\n\n    def __repr__(self):\n        \"\"\"Fraction representation\"\"\"\n        return \"Fraction({}, {})\".format(self._numer, self._denom)\n\n    def __eq__(self, other):\n        \"\"\"Equality comparison\"\"\"\n        return self._numer * other.denom == other.numer * self._denom\n\n    def __add__(self, other):\n        \"\"\"Add two fractions\"\"\"\n        new_numer = self._numer * other.denom + self._denom * other.numer\n        new_denom = self._denom * other.denom\n        return Fraction(new_numer, new_denom)\n","src/lib/pythonds3/searching/__init__.py":"#!/usr/bin/env python3\n\"\"\"\npythonds3.search import statement\n\"\"\"\nfrom pythonds3.searching.hash_table import HashTable\n\n__all__ = [\"HashTable\"]\n","src/lib/pythonds3/searching/hash_table.py":"#!/usr/bin/env python3\n\"\"\"\nBradley N. Miller, David L. Ranum\nProblem Solving with Algorithms and Data Structures using Python\nCopyright 2005\nUpdated by Roman Yasinovskyy, 2017\n\"\"\"\n\n\nclass HashTable:\n    \"\"\"Hash Table implementation\"\"\"\n\n    def __init__(self, size=16):\n        \"\"\"Create a hash table\"\"\"\n        self._size = size\n        self._slots = [None] * self._size\n        self._data = [None] * self._size\n\n    def __getitem__(self, key):\n        \"\"\"Magic __get__\"\"\"\n        return self.get(key)\n\n    def __setitem__(self, key, data):\n        \"\"\"Magic __set__\"\"\"\n        self.put(key, data)\n\n    def __len__(self):\n        \"\"\"Magic __len__\"\"\"\n        return self._size - self._slots.count(None)\n\n    def __contains__(self, key):\n        \"\"\"Magin in\"\"\"\n        return key in self._slots\n\n    def _hash_function(self, key, size):\n        \"\"\"Simple hash function\"\"\"\n        return key % size\n\n    def _rehash(self, old_hash, size, step=1):\n        \"\"\"Simple rehash function\"\"\"\n        return (old_hash + step) % size\n\n    def is_empty(self):\n        \"\"\"Check if the table is empty\"\"\"\n        return self._size == self._slots.count(None)\n\n    def size(self):\n        \"\"\"Get number of items in the table\"\"\"\n        return self._size - self._slots.count(None)\n\n    def put(self, key, data):\n        \"\"\"Add an item to the table\"\"\"\n        hash_value = self._hash_function(key, len(self._slots))\n\n        if self._slots[hash_value] is None:\n            self._slots[hash_value] = key\n            self._data[hash_value] = data\n        else:\n            if self._slots[hash_value] == key:\n                self._data[hash_value] = data  # replace\n            else:\n                j = 0\n                next_slot = self._rehash(hash_value, len(self._slots), j)\n                while (\n                    self._slots[next_slot] is not None\n                    and self._slots[next_slot] != key\n                    and j < self._size\n                ):\n                    j = j + 1\n                    next_slot = self._rehash(hash_value, len(self._slots), j)\n\n                if self._slots[next_slot] is None:\n                    self._slots[next_slot] = key\n                    self._data[next_slot] = data\n                elif j == self._size:\n                    raise Exception(\"Hash Table is full\")\n                else:\n                    self._data[next_slot] = data  # replace\n\n    def get(self, key):\n        \"\"\"Get an item from the table\"\"\"\n        start_slot = self._hash_function(key, len(self._slots))\n        position = start_slot\n        j = 0\n\n        while self._slots[position] is not None and j < self._size:\n            if self._slots[position] == key:\n                return self._data[position]\n            j = j + 1\n            position = self._rehash(start_slot, len(self._slots), j)\n\n        raise KeyError(\"{} is not in the table\".format(key))\n","src/lib/pythonds3/sorting/__init__.py":"#!/usr/bin/env python3\n\"\"\"\npythonds3.sorting import statement\n\"\"\"\nfrom pythonds3.sorting.sorting_algorithms import bubble_sort\nfrom pythonds3.sorting.sorting_algorithms import select_sort\nfrom pythonds3.sorting.sorting_algorithms import insert_sort\nfrom pythonds3.sorting.sorting_algorithms import shell_sort\nfrom pythonds3.sorting.sorting_algorithms import merge_sort\nfrom pythonds3.sorting.sorting_algorithms import quick_sort\nfrom pythonds3.sorting.sorting_algorithms import heap_sort\n","src/lib/pythonds3/sorting/sorting_algorithms.py":"#!/usr/bin/env python3\n\"\"\"\nBradley N. Miller, David L. Ranum\nProblem Solving with Algorithms and Data Structures using Python\nCopyright 2005\nUpdated by Roman Yasinovskyy, 2017\n\"\"\"\n\nimport heapq\n\n\ndef bubble_sort(lst):\n    \"\"\"Bubble sort\"\"\"\n    for i in range(len(lst) - 1, 0, -1):\n        exchanges = False\n        for j in range(i):\n            if lst[j] > lst[j + 1]:\n                exchanges = True\n                lst[j], lst[j + 1] = lst[j + 1], lst[j]\n        if not exchanges:\n            break\n\n\ndef select_sort(lst):\n    \"\"\"Selection sort\"\"\"\n    for i, item in enumerate(lst):\n        min_idx = len(lst) - 1\n        for j in range(i, len(lst)):\n            if lst[j] < lst[min_idx]:\n                min_idx = j\n        if min_idx != i:\n            lst[min_idx], lst[i] = lst[i], lst[min_idx]\n\n\ndef insert_sort(lst):\n    \"\"\"Insertion sort\"\"\"\n    for i in range(1, len(lst)):\n        cur_val = lst[i]\n        cur_pos = i\n\n        while cur_pos > 0 and lst[cur_pos - 1] > cur_val:\n            lst[cur_pos] = lst[cur_pos - 1]\n            cur_pos = cur_pos - 1\n        lst[cur_pos] = cur_val\n\n\ndef shell_sort(lst):\n    \"\"\"Shell sort\"\"\"\n    sublist_count = len(lst) // 3\n    while sublist_count > 0:\n        for pos_start in range(sublist_count):\n            _gap_insert_sort(lst, pos_start, sublist_count)\n        sublist_count = sublist_count // 2\n\n\ndef _gap_insert_sort(lst, start, gap):\n    \"\"\"Shell sort helper function\"\"\"\n    for i in range(start + gap, len(lst), gap):\n        cur_val = lst[i]\n        cur_pos = i\n        while cur_pos >= gap and lst[cur_pos - gap] > cur_val:\n            lst[cur_pos] = lst[cur_pos - gap]\n            cur_pos = cur_pos - gap\n        lst[cur_pos] = cur_val\n\n\ndef merge_sort(lst):\n    \"\"\"Merge sort\"\"\"\n    if len(lst) > 1:\n        mid = len(lst) // 2\n        left_half = lst[:mid]\n        right_half = lst[mid:]\n\n        merge_sort(left_half)\n        merge_sort(right_half)\n\n        i, j, k = 0, 0, 0\n        while i < len(left_half) and j < len(right_half):\n            if left_half[i] <= right_half[j]:\n                lst[k] = left_half[i]\n                i = i + 1\n            else:\n                lst[k] = right_half[j]\n                j = j + 1\n            k = k + 1\n\n        while i < len(left_half):\n            lst[k] = left_half[i]\n            i = i + 1\n            k = k + 1\n\n        while j < len(right_half):\n            lst[k] = right_half[j]\n            j = j + 1\n            k = k + 1\n\n\ndef quick_sort(lst):\n    \"\"\"Quick sort\"\"\"\n    _quick_sort_help(lst, 0, len(lst) - 1)\n\n\ndef _quick_sort_help(lst, mark_l, mark_r):\n    \"\"\"Quick sort helper\"\"\"\n    if mark_l < mark_r:\n        split = _quick_sort_part(lst, mark_l, mark_r)\n        _quick_sort_help(lst, mark_l, split - 1)\n        _quick_sort_help(lst, split + 1, mark_r)\n\n\ndef _quick_sort_part(lst, mark_l, mark_r):\n    \"\"\"Quick sort partition\"\"\"\n    pivot_val = lst[mark_l]\n    mark_l_cur = mark_l + 1\n    mark_r_cur = mark_r\n    done = False\n\n    while not done:\n        while mark_l_cur <= mark_r_cur and lst[mark_l_cur] <= pivot_val:\n            mark_l_cur = mark_l_cur + 1\n        while mark_l_cur <= mark_r_cur and lst[mark_r_cur] >= pivot_val:\n            mark_r_cur = mark_r_cur - 1\n        if mark_r_cur < mark_l_cur:\n            done = True\n        else:\n            lst[mark_l_cur], lst[mark_r_cur] = lst[mark_r_cur], lst[mark_l_cur]\n    lst[mark_l], lst[mark_r_cur] = lst[mark_r_cur], lst[mark_l]\n\n    return mark_r_cur\n\n\ndef heap_sort(lst):\n    \"\"\"Heap sort\"\"\"\n    res = []\n    heapq.heapify(lst)\n    while lst:\n        res.append(heapq.heappop(lst))\n    for i in res:\n        lst.append(i)\n","src/lib/pythonds3/trees/__init__.py":"#!/usr/bin/env python3\n\"\"\"\npythonds3.trees import statement\n\"\"\"\nfrom pythonds3.trees.avl_tree import AVLTree\nfrom pythonds3.trees.binary_search_tree import BinarySearchTree\nfrom pythonds3.trees.binary_heap import BinaryHeap\nfrom pythonds3.trees.binary_tree import BinaryTree\nfrom pythonds3.trees.priority_queue import PriorityQueue\n\n__all__ = [\"BinaryTree\", \"BinaryHeap\", \"BinarySearchTree\", \"AVLTree\", \"PriorityQueue\"]\n","src/lib/pythonds3/trees/avl_tree.py":"#!/usr/bin/env python3\n\"\"\"\nBradley N. Miller, David L. Ranum\nProblem Solving with Algorithms and Data Structures using Python\nCopyright 2005, 2010\nUpdated by Roman Yasinovskyy, 2017\n\"\"\"\n\nfrom pythonds3.trees.binary_search_tree import BinarySearchTree\nfrom pythonds3.trees.binary_search_tree import BinaryTreeNode\n\n\nclass AVLTreeNode(BinaryTreeNode):\n    \"\"\"AVL Tree Node\"\"\"\n\n    def __init__(self, key, val, balance_factor, left=None, right=None, parent=None):\n        \"\"\"Create an AVL tree node\"\"\"\n        BinaryTreeNode.__init__(self, key, val, left, right, parent)\n        self._balance_factor = balance_factor\n\n    def get_balance_factor(self):\n        \"\"\"Get the node balance factor\"\"\"\n        return self._balance_factor\n\n    def set_balance_factor(self, value):\n        \"\"\"Set the node balance factor\"\"\"\n        self._balance_factor = value\n\n    balance_factor = property(get_balance_factor, set_balance_factor)\n\n\nclass AVLTree(BinarySearchTree):\n    \"\"\"AVL tree implementation\"\"\"\n\n    def __init__(self):\n        \"\"\"Create a new AVL tree\"\"\"\n        BinarySearchTree.__init__(self)\n\n    def put(self, key, value):\n        \"\"\"Add new node\"\"\"\n        if self._root:\n            self._put(key, value, self._root)\n        else:\n            self._root = AVLTreeNode(key, value, 0)\n        self._size = self._size + 1\n\n    def _put(self, key, value, current_node):\n        \"\"\"Add a new node to the tree (helper function)\"\"\"\n        if key < current_node.key:\n            if current_node.get_child_left():\n                self._put(key, value, current_node.child_left)\n            else:\n                current_node.child_left = AVLTreeNode(\n                    key, value, 0, parent=current_node\n                )\n                self.update_balance(current_node.child_left)\n        else:\n            if current_node.get_child_right():\n                self._put(key, value, current_node.child_right)\n            else:\n                current_node.child_right = AVLTreeNode(\n                    key, value, 0, parent=current_node\n                )\n                self.update_balance(current_node.child_right)\n\n    def update_balance(self, node):\n        \"\"\"Update the tree balance\"\"\"\n        if node.balance_factor > 1 or node.balance_factor < -1:\n            self.rebalance(node)\n            return\n        if node.parent:\n            if node.is_child_left():\n                node.parent.balance_factor += 1\n            elif node.is_child_right():\n                node.parent.balance_factor -= 1\n\n            if node.parent.balance_factor != 0:\n                self.update_balance(node.parent)\n\n    def rebalance(self, node):\n        \"\"\"Rebalance the tree\"\"\"\n        if node.balance_factor < 0:\n            if node.child_right.balance_factor > 0:\n                # Do an LR Rotation\n                self.rotate_right(node.child_right)\n                self.rotate_left(node)\n            else:\n                # single left\n                self.rotate_left(node)\n        elif node.balance_factor > 0:\n            if node.child_left.balance_factor < 0:\n                # Do an RL Rotation\n                self.rotate_left(node.child_left)\n                self.rotate_right(node)\n            else:\n                # single right\n                self.rotate_right(node)\n\n    def rotate_left(self, rotation_root):\n        \"\"\"Left rotation\"\"\"\n        new_root = rotation_root.child_right\n        rotation_root.child_right = new_root.child_left\n        if new_root.child_left:\n            new_root.child_left.parent = rotation_root\n        new_root.parent = rotation_root.parent\n        if rotation_root.is_root():\n            self._root = new_root\n        else:\n            if rotation_root.is_child_left():\n                rotation_root.parent.child_left = new_root\n            else:\n                rotation_root.parent.child_right = new_root\n        new_root.child_left = rotation_root\n        rotation_root.parent = new_root\n        rotation_root.balance_factor = (\n            rotation_root.balance_factor + 1 - min(new_root.balance_factor, 0)\n        )\n        new_root.balance_factor = (\n            new_root.balance_factor + 1 + max(rotation_root.balance_factor, 0)\n        )\n\n    def rotate_right(self, rotation_root):\n        \"\"\"Right rotation\"\"\"\n        new_root = rotation_root.child_left\n        rotation_root.child_left = new_root.child_right\n        if new_root.child_right:\n            new_root.child_right.parent = rotation_root\n        new_root.parent = rotation_root.parent\n        if rotation_root.is_root():\n            self._root = new_root\n        else:\n            if rotation_root.is_child_right():\n                rotation_root.parent.child_right = new_root\n            else:\n                rotation_root.parent.child_left = new_root\n        new_root.child_right = rotation_root\n        rotation_root.parent = new_root\n        rotation_root.balance_factor = (\n            rotation_root.balance_factor - 1 - max(new_root.balance_factor, 0)\n        )\n        new_root.balance_factor = (\n            new_root.balance_factor - 1 + min(rotation_root.balance_factor, 0)\n        )\n","src/lib/pythonds3/trees/binary_heap.py":"#!/usr/bin/env python3\n\"\"\"\nBradley N. Miller, David L. Ranum\nProblem Solving with Algorithms and Data Structures using Python\nCopyright 2005\nUpdated by Roman Yasinovskyy, 2017\n\"\"\"\n\n\nclass BinaryHeap:\n    \"\"\"Minimal Binary Heap\"\"\"\n\n    def __init__(self):\n        \"\"\"Create a heap\"\"\"\n        self._heap = []\n\n    def _perc_up(self, cur_idx):\n        \"\"\"Move a node up\"\"\"\n        while (cur_idx - 1) // 2 >= 0:\n            parent_idx = (cur_idx - 1) // 2\n            if self._heap[cur_idx] < self._heap[parent_idx]:\n                self._heap[cur_idx], self._heap[parent_idx] = (\n                    self._heap[parent_idx],\n                    self._heap[cur_idx],\n                )\n            cur_idx = parent_idx\n\n    def _perc_down(self, cur_idx):\n        \"\"\"Move a node down\"\"\"\n        while 2 * cur_idx + 1 < len(self._heap):\n            min_child_idx = self._get_min_child(cur_idx)\n            if self._heap[cur_idx] > self._heap[min_child_idx]:\n                self._heap[cur_idx], self._heap[min_child_idx] = (\n                    self._heap[min_child_idx],\n                    self._heap[cur_idx],\n                )\n            else:\n                return\n            cur_idx = min_child_idx\n\n    def _get_min_child(self, parent_idx):\n        \"\"\"Get a smaller child\"\"\"\n        if 2 * parent_idx + 2 > len(self._heap) - 1:\n            return 2 * parent_idx + 1\n        if self._heap[2 * parent_idx + 1] < self._heap[2 * parent_idx + 2]:\n            return 2 * parent_idx + 1\n        return 2 * parent_idx + 2\n\n    def heapify(self, not_a_heap, show_details=False):\n        \"\"\"Build a heap from any list\"\"\"\n        self._heap = not_a_heap[:]\n        cur_idx = len(self._heap) // 2 - 1\n        while cur_idx >= 0:\n            self._perc_down(cur_idx)\n            cur_idx = cur_idx - 1\n            if show_details:\n                print(self._heap)\n\n    def insert(self, item):\n        \"\"\"Add a new item\"\"\"\n        self._heap.append(item)\n        self._perc_up(len(self._heap) - 1)\n\n    def delete(self):\n        \"\"\"Remove an item\"\"\"\n        self._heap[0], self._heap[-1] = self._heap[-1], self._heap[0]\n        result = self._heap.pop()\n        self._perc_down(0)\n        return result\n\n    def is_empty(self):\n        \"\"\"Check if the heap is empty\"\"\"\n        return not bool(self._heap)\n\n    def __len__(self):\n        \"\"\"Get heap size\"\"\"\n        return len(self._heap)\n\n    def __str__(self):\n        \"\"\"Heap as a string\"\"\"\n        return str(self._heap)\n\n    def __contains__(self, item):\n        \"\"\"__contains__in method override\"\"\"\n        return item in self._heap\n","src/lib/pythonds3/trees/binary_search_tree.py":"#!/usr/bin/env python3\n\"\"\"\nBradley N. Miller, David L. Ranum\nProblem Solving with Algorithms and Data Structures using Python\nCopyright 2005, 2010\nUpdated by Roman Yasinovskyy, 2017\n\"\"\"\n\n\nclass BinaryTreeNode:\n    \"\"\"Binary Tree Node class\"\"\"\n\n    def __init__(self, key, value, left=None, right=None, parent=None):\n        \"\"\"Create new Tree Node\"\"\"\n        self._key = key\n        self._value = value\n        self._child_left = left\n        self._child_right = right\n        self._parent = parent\n\n    def get_child_left(self):\n        \"\"\"Return the node's left child\"\"\"\n        return self._child_left\n\n    def set_child_left(self, node):\n        \"\"\"Assign the node's left child\"\"\"\n        self._child_left = node\n\n    child_left = property(get_child_left, set_child_left)\n\n    def get_child_right(self):\n        \"\"\"Return the node's right child\"\"\"\n        return self._child_right\n\n    def set_child_right(self, node):\n        \"\"\"Assign the node's right child\"\"\"\n        self._child_right = node\n\n    child_right = property(get_child_right, set_child_right)\n\n    def get_parent(self):\n        \"\"\"Return the node's parent\"\"\"\n        return self._parent\n\n    def set_parent(self, node):\n        \"\"\"Assign the node's parent\"\"\"\n        self._parent = node\n\n    parent = property(get_parent, set_parent)\n\n    def is_child_left(self):\n        \"\"\"Check if the node is a left child\"\"\"\n        return self._parent and self._parent.child_left == self\n\n    def is_child_right(self):\n        \"\"\"Check if the node is a right child\"\"\"\n        return self._parent and self._parent.child_right == self\n\n    def is_root(self):\n        \"\"\"Check if the node is a tree root\"\"\"\n        return not self._parent\n\n    def is_leaf(self):\n        \"\"\"Check if the node is a leaf\"\"\"\n        return not (self._child_right or self._child_left)\n\n    def has_a_child(self):\n        \"\"\"Check if the node has any child\"\"\"\n        return self._child_right or self._child_left\n\n    def has_children(self):\n        \"\"\"Check if the node has both children\"\"\"\n        return self._child_right and self._child_left\n\n    def get_key(self):\n        \"\"\"Get node key\"\"\"\n        return self._key\n\n    def set_key(self, key):\n        \"\"\"Set node key\"\"\"\n        self._key = key\n\n    key = property(get_key, set_key)\n\n    def get_value(self):\n        \"\"\"Get node value\"\"\"\n        return self._value\n\n    def set_value(self, value):\n        \"\"\"Set node value\"\"\"\n        self._value = value\n\n    value = property(get_value, set_value)\n\n    def replace_payload(self, key, value, left, right):\n        \"\"\"Change node payload\"\"\"\n        self._key = key\n        self._value = value\n        self._child_left = left\n        self._child_right = right\n        if self.child_left:\n            self._child_left.parent = self\n        if self.child_right:\n            self._child_right.parent = self\n\n    def find_successor(self):\n        \"\"\"Find the node's successor\"\"\"\n        successor = None\n        if self._child_right:\n            successor = self._child_right.find_min()\n        else:\n            if self._parent:\n                if self.is_child_left():\n                    successor = self._parent\n                else:\n                    self._parent.child_right = None\n                    successor = self._parent.find_successor()\n                    self._parent.child_right = self\n        return successor\n\n    def find_min(self):\n        \"\"\"Find the smallest node in the right subtree\"\"\"\n        current = self\n        while current.child_left:\n            current = current.child_left\n        return current\n\n    def splice_out(self):\n        \"\"\"Splice out\"\"\"\n        if self.is_leaf():\n            if self.is_child_left():\n                self._parent.child_left = None\n            else:\n                self._parent.child_right = None\n        elif self.has_a_child():\n            if self.child_left:\n                if self.is_child_left():\n                    self._parent.child_left = self._child_left\n                else:\n                    self._parent.child_right = self._child_left\n                self._child_left.parent = self._parent\n            else:\n                if self.is_child_left():\n                    self._parent.child_left = self._child_right\n                else:\n                    self._parent.child_right = self._child_right\n                self._child_right.parent = self._parent\n\n    def __iter__(self):\n        \"\"\"The standard inorder traversal of a binary tree\"\"\"\n        if self:\n            if self._child_left:\n                for elem in self._child_left:\n                    yield elem\n            yield self._key\n            if self._child_right:\n                for elem in self._child_right:\n                    yield elem\n\n\nclass BinarySearchTree:\n    \"\"\"Binary search tree implementation\"\"\"\n\n    def __init__(self):\n        self._root = None\n        self._size = 0\n\n    def __len__(self):\n        \"\"\"Tree size\"\"\"\n        return self._size\n\n    def size(self):\n        \"\"\"Tree size\"\"\"\n        return self._size\n\n    def __iter__(self):\n        \"\"\"Iterator\"\"\"\n        return self._root.__iter__()\n\n    def __getitem__(self, key):\n        \"\"\"[] getter operator override\"\"\"\n        result = self.get(key)\n        if result:\n            return result\n        raise KeyError(\"Error, key not in tree\")\n\n    def get_root(self):\n        \"\"\"Get tree root\"\"\"\n        return self._root\n\n    def set_root(self, node):\n        \"\"\"Set tree root\"\"\"\n        self._root = node\n\n    root = property(get_root, set_root)\n\n    def get(self, key):\n        \"\"\"Retrieve a value by the key\"\"\"\n        if self._root:\n            result = self._get(key, self._root)\n            if result:\n                return result.value\n            return None\n        else:\n            return None\n\n    def _get(self, key, current_node):\n        \"\"\"Retrieve a value by the key (helper function)\"\"\"\n        if not current_node:\n            return None\n        if current_node.key == key:\n            return current_node\n        elif key < current_node.key:\n            return self._get(key, current_node.child_left)\n        else:\n            return self._get(key, current_node.child_right)\n\n    def __setitem__(self, key, value):\n        \"\"\"[] setter operator override\"\"\"\n        self.put(key, value)\n\n    def put(self, key, value):\n        \"\"\"Add new node\"\"\"\n        if self._root:\n            self._put(key, value, self._root)\n        else:\n            self._root = BinaryTreeNode(key, value)\n        self._size = self._size + 1\n\n    def _put(self, key, value, current_node):\n        \"\"\"Add new node (helper function)\"\"\"\n        if key < current_node.key:\n            if current_node.child_left:\n                self._put(key, value, current_node.child_left)\n            else:\n                current_node.child_left = BinaryTreeNode(\n                    key, value, parent=current_node\n                )\n        else:\n            if current_node.child_right:\n                self._put(key, value, current_node.child_right)\n            else:\n                current_node.child_right = BinaryTreeNode(\n                    key, value, parent=current_node\n                )\n\n    def __contains__(self, key):\n        \"\"\"in operator override\"\"\"\n        return bool(self._get(key, self._root))\n\n    def __delitem__(self, key):\n        \"\"\"del operator override\"\"\"\n        self.delete(key)\n\n    def delete(self, key):\n        \"\"\"Remove a node by its key\"\"\"\n        if self._size > 1:\n            node_to_remove = self._get(key, self._root)\n            if node_to_remove:\n                self._delete(node_to_remove)\n                self._size = self._size - 1\n            else:\n                raise KeyError(\"Error, key not in tree\")\n        elif self._size == 1 and self._root.key == key:\n            self._root = None\n            self._size = self._size - 1\n        else:\n            raise KeyError(\"Error, key not in tree\")\n\n    def _delete(self, current_node):\n        \"\"\"Remove a node by its key (helper function)\"\"\"\n        if current_node.is_leaf():  # removing a leaf\n            if current_node == current_node.parent.child_left:\n                current_node.parent.child_left = None\n            else:\n                current_node.parent.child_right = None\n        elif current_node.has_children():  # removing a node with two children\n            successor = current_node.find_successor()\n            successor.splice_out()\n            current_node.key = successor.key\n            current_node.value = successor.value\n        else:  # removing a node with one child\n            if current_node.get_child_left():\n                if current_node.is_child_left():\n                    current_node.child_left.parent = current_node.parent\n                    current_node.parent.child_left = current_node.child_left\n                elif current_node.is_child_right():\n                    current_node.child_left.parent = current_node.parent\n                    current_node.parent.child_right = current_node.child_left\n                else:\n                    current_node.replace_payload(\n                        current_node.child_left.key,\n                        current_node.child_left.value,\n                        current_node.child_left.child_left,\n                        current_node.child_left.child_right,\n                    )\n            else:\n                if current_node.is_child_left():\n                    current_node.child_right.parent = current_node.parent\n                    current_node.parent.child_left = current_node.child_right\n                elif current_node.is_child_right():\n                    current_node.child_right.parent = current_node.parent\n                    current_node.parent.child_right = current_node.child_right\n                else:\n                    current_node.replace_payload(\n                        current_node.child_right.key,\n                        current_node.child_right.value,\n                        current_node.child_right.child_left,\n                        current_node.child_right.child_right,\n                    )\n\n    def inorder(self):\n        \"\"\"In-order tree traversal\"\"\"\n        self._inorder(self._root)\n\n    def _inorder(self, tree):\n        \"\"\"In-order tree traversal (helper function)\"\"\"\n        if tree:\n            self._inorder(tree.child_left)\n            print(tree.key, end=\" \")\n            self._inorder(tree.child_right)\n\n    def postorder(self):\n        \"\"\"Post-order tree traversal\"\"\"\n        self._postorder(self._root)\n\n    def _postorder(self, tree):\n        \"\"\"Post-order tree traversal (helper function)\"\"\"\n        if tree:\n            self._postorder(tree.child_left)\n            self._postorder(tree.child_right)\n            print(tree.key, end=\" \")\n\n    def preorder(self):\n        \"\"\"Pre-order tree traversal\"\"\"\n        self._preorder(self._root)\n\n    def _preorder(self, tree):\n        \"\"\"Pre-order tree traversal (helper function)\"\"\"\n        if tree:\n            print(tree.key, end=\" \")\n            self._preorder(tree.child_left)\n            self._preorder(tree.child_right)\n\n    def clear(self):\n        \"\"\"Remove all nodes\"\"\"\n        while self._root:\n            self.delete(self._root.key)\n","src/lib/pythonds3/trees/binary_tree.py":"#!/usr/bin/env python3\n\"\"\"\nBradley N. Miller, David L. Ranum\nProblem Solving with Algorithms and Data Structures using Python\nCopyright 2005\nUpdated by Roman Yasinovskyy, 2017\n\"\"\"\n\nimport operator\n\n\nclass BinaryTree:\n    \"\"\"\n    A recursive implementation of Binary Tree\n    Using links and Nodes approach.\n\n    Modified to allow for trees to be constructed from other trees\n    rather than always creating a new tree in the insert_feft or insert_right\n    \"\"\"\n\n    def __init__(self, key):\n        \"\"\"Create new tree\"\"\"\n        self._key = key\n        self._child_left = None\n        self._child_right = None\n\n    def get_root_val(self):\n        \"\"\"Get root key value\"\"\"\n        return self._key\n\n    def set_root_val(self, key):\n        \"\"\"Set root key value\"\"\"\n        self._key = key\n\n    root = property(get_root_val, set_root_val)\n\n    def get_child_left(self):\n        \"\"\"Get left child\"\"\"\n        return self._child_left\n\n    def set_child_left(self, node):\n        \"\"\"Set left child\"\"\"\n        self._child_left = node\n\n    child_left = property(get_child_left, set_child_left)\n\n    def get_child_right(self):\n        \"\"\"Get right child\"\"\"\n        return self._child_right\n\n    def set_child_right(self, node):\n        \"\"\"Set right child\"\"\"\n        self._child_right = node\n\n    child_right = property(get_child_right, set_child_right)\n\n    def is_leaf(self):\n        \"\"\"Check if a node is leaf\"\"\"\n        return (not self._child_left) and (not self._child_right)\n\n    def insert_left(self, new_node):\n        \"\"\"Insert left subtree\"\"\"\n        if isinstance(new_node, BinaryTree):\n            new_subtree = new_node\n        else:\n            new_subtree = BinaryTree(new_node)\n\n        if self._child_left:\n            new_subtree.set_child_left(self._child_left)\n\n        self._child_left = new_subtree\n\n    def insert_right(self, new_node):\n        \"\"\"Insert right subtree\"\"\"\n        if isinstance(new_node, BinaryTree):\n            new_subtree = new_node\n        else:\n            new_subtree = BinaryTree(new_node)\n\n        if self._child_right:\n            new_subtree.set_child_right(self._child_right)\n        self._child_right = new_subtree\n\n    def preorder(self):\n        \"\"\"Pre-order tree traversal\"\"\"\n        print(self._key, end=\" \")\n        if self._child_left:\n            self._child_left.preorder()\n        if self._child_right:\n            self._child_right.preorder()\n\n    def inorder(self):\n        \"\"\"In-order tree traversal\"\"\"\n        if self._child_left:\n            self._child_left.inorder()\n        print(self._key, end=\" \")\n        if self._child_right:\n            self._child_right.inorder()\n\n    def postorder(self):\n        \"\"\"Post-order tree traversal\"\"\"\n        if self._child_left:\n            self._child_left.postorder()\n        if self._child_right:\n            self._child_right.postorder()\n        print(self._key, end=\" \")\n\n    def print_exp(self):\n        \"\"\"Print an expression\"\"\"\n        if self._child_left:\n            print(\"(\", end=\" \")\n            self._child_left.print_exp()\n        print(self._key, end=\" \")\n        if self._child_right:\n            self._child_right.print_exp()\n            print(\")\", end=\" \")\n\n    def postorder_eval(self):\n        \"\"\"Postorder evaluation\"\"\"\n        operations = {\n            \"+\": operator.add,\n            \"-\": operator.sub,\n            \"*\": operator.mul,\n            \"/\": operator.truediv,\n        }\n        result_1 = None\n        result_2 = None\n        if self._child_left:\n            result_1 = self._child_left.postorder_eval()\n        if self._child_right:\n            result_2 = self._child_right.postorder_eval()\n        if result_1 and result_2:\n            return operations[self._key](result_1, result_2)\n        return self._key\n\n    def height(self):\n        \"\"\"Height of a tree\"\"\"\n        if not self._key:\n            return -1\n        if self._child_left:\n            height_left = self._child_left.height()\n        else:\n            height_left = -1\n\n        if self._child_right:\n            height_right = self._child_right.height()\n        else:\n            height_right = -1\n\n        return 1 + max(height_left, height_right)\n\n    def __len__(self):\n        \"\"\"Size of a tree\"\"\"\n        return self.size()\n\n    def size(self):\n        \"\"\"Count nodes in a tree\"\"\"\n        if not self._key:\n            return 0\n        if self._child_left:\n            children_left = self._child_left.size()\n        else:\n            children_left = 0\n\n        if self._child_right:\n            children_right = self._child_right.size()\n        else:\n            children_right = 0\n\n        return 1 + children_left + children_right\n","src/lib/pythonds3/trees/priority_queue.py":"#!/usr/bin/env python3\n\"\"\"\nBradley N. Miller, David L. Ranum\nProblem Solving with Algorithms and Data Structures using Python\nCopyright 2005\nUpdated by Roman Yasinovskyy, 2017\n\"\"\"\n\nfrom pythonds3.trees.binary_heap import BinaryHeap\n\n\nclass PriorityQueue(BinaryHeap):\n    \"\"\"\n    This implementation of binary heap takes (key, value) pairs where key signifies priority\n    We will assume that the keys are all comparable.\n    \"\"\"\n\n    def change_priority(self, new_priority, value):\n        \"\"\"Change the priority\"\"\"\n        key_to_move = 0\n        for i in range(len(self._heap)):\n            if self._heap[i][1] == value:\n                key_to_move = i\n                break\n        if key_to_move > -1:\n            self._heap[key_to_move] = (new_priority, self._heap[key_to_move][1])\n            self._perc_up(key_to_move)\n","src/lib/pythoned/__init__.py":"","src/lib/pythoned/arboles/__init__.py":"\n\n\nfrom .avl import ArbolAVL\nfrom .abb import ArbolBinarioBusqueda\nfrom .monticuloBinario import MonticuloBinario\n\n\n","src/lib/pythoned/arboles/abb.py":"# Bradley N. Miller, David L. Ranum\n# Solución de problemas con algoritmos y estructuras de datos usando Python\n# Copyright 2014\n#\n#abb.py\n\nclass ArbolBinarioBusqueda:\n\n    def __init__(self):\n        self.raiz = None\n        self.tamano = 0\n\n    def agregar(self,clave,valor):\n        if self.raiz:\n            self._agregar(clave,valor,self.raiz)\n        else:\n            self.raiz = NodoArbol(clave,valor)\n        self.tamano = self.tamano + 1\n\n    def _agregar(self,clave,valor,nodoActual):\n        if clave < nodoActual.clave:\n            if nodoActual.tieneHijoIzquierdo():\n                   self._agregar(clave,valor,nodoActual.hijoIzquierdo)\n            else:\n                   nodoActual.hijoIzquierdo = NodoArbol(clave,valor,padre=nodoActual)\n        else:\n            if nodoActual.tieneHijoDerecho():\n                   self._agregar(clave,valor,nodoActual.hijoDerecho)\n            else:\n                   nodoActual.hijoDerecho = NodoArbol(clave,valor,padre=nodoActual)\n \n    def __setitem__(self,c,v):\n        self.agregar(c,v)\n\n    def obtener(self,clave):\n        if self.raiz:\n            res = self._obtener(clave,self.raiz)\n            if res:\n                return res.cargaUtil\n            else:\n                return None\n        else:\n            return None\n\n    def _obtener(self,clave,nodoActual):\n    \tif not nodoActual:\n    \t    return None\n    \telif nodoActual.clave == clave:\n    \t    return nodoActual\n    \telif clave < nodoActual.clave:\n    \t    return self._obtener(clave,nodoActual.hijoIzquierdo)\n    \telse:\n    \t    return self._obtener(clave,nodoActual.hijoDerecho)        \n\n    def __getitem__(self,clave):\n        res = self.obtener(clave) \n        if res:\n            return res\n        else:\n            raise KeyError('Error, la clave no está en el árbol')\n\n    def __contains__(self,clave):\n    \tif self._obtener(clave,self.raiz):\n    \t    return True\n    \telse:\n    \t    return False\n       \n    def longitud(self):\n        return self.tamano\n\n    def __len__(self):\n        return self.tamano\n\n    def __iter__(self):\n        return self.raiz.__iter__()\n\n    def eliminar(self,clave):\n        if self.tamano > 1:\n           nodoAEliminar = self._obtener(clave,self.raiz)\n    \t   if nodoAEliminar:\n    \t       self.remover(nodoAEliminar)\n    \t       self.tamano = self.tamano-1\n    \t   else:\n    \t       raise KeyError('Error, la clave no está en el árbol')\n        elif self.tamano == 1 and self.raiz.clave == clave:\n    \t    self.raiz = None\n    \t    self.tamano = self.tamano - 1\n        else:\n    \t    raise KeyError('Error, la clave no está en el árbol')\n\n    def __delitem__(self,clave):\n        self.eliminar(clave)\n\n    def remover(self,nodoActual):\n        if nodoActual.esHoja(): #hoja\n            if nodoActual == nodoActual.padre.hijoIzquierdo:\n                nodoActual.padre.hijoIzquierdo = None\n            else:\n                nodoActual.padre.hijoDerecho = None\n        elif nodoActual.tieneAmbosHijos(): #interior\n            suc = nodoActual.encontrarSucesor()\n            suc.empalmar()\n            nodoActual.clave = suc.clave\n            nodoActual.cargaUtil = suc.cargaUtil\n\n        else: # este nodo tiene un (1) hijo\n            if nodoActual.tieneHijoIzquierdo():\n                if nodoActual.esHijoIzquierdo():\n                    nodoActual.hijoIzquierdo.padre = nodoActual.padre\n                    nodoActual.padre.hijoIzquierdo = nodoActual.hijoIzquierdo\n                elif nodoActual.esHijoDerecho():\n                    nodoActual.hijoIzquierdo.padre = nodoActual.padre\n                    nodoActual.padre.hijoDerecho = nodoActual.hijoIzquierdo\n                else:\n                    nodoActual.reemplazarDatoDeNodo(nodoActual.hijoIzquierdo.clave, nodoActual.hijoIzquierdo.cargaUtil, nodoActual.hijoIzquierdo.hijoIzquierdo, nodoActual.hijoIzquierdo.hijoDerecho)\n            else:\n                if nodoActual.esHijoIzquierdo():\n                    nodoActual.hijoDerecho.padre = nodoActual.padre\n                    nodoActual.padre.hijoIzquierdo = nodoActual.hijoDerecho\n                elif nodoActual.esHijoDerecho():\n                    nodoActual.hijoDerecho.padre = nodoActual.padre\n                    nodoActual.padre.hijoDerecho = nodoActual.hijoDerecho\n                else:\n                    nodoActual.reemplazarDatoDeNodo(nodoActual.hijoDerecho.clave, nodoActual.hijoDerecho.cargaUtil, nodoActual.hijoDerecho.hijoIzquierdo, nodoActual.hijoDerecho.hijoDerecho)\n\n    def inorden(self):\n        self._inorden(self.raiz)\n\n    def _inorden(self,arbol):\n        if arbol != None:\n            self._inorden(arbol.hijoIzquierdo)\n            print(arbol.clave)\n            self._inorden(arbol.hijoDerecho)\n\n    def postorden(self):\n        self._postorden(self.raiz)\n\n    def _postorden(self, arbol):\n        if arbol:\n            self._postorden(arbol.hijoDerecho)\n            self._postorden(arbol.hijoIzquierdo)\n            print(arbol.clave)            \n\n    def preorden(self):\n        self._preorden(self,self.raiz)\n\n    def _preorden(self,arbol):\n        if arbol:\n            print(arbol.clave)            \n            self._preorden(arbol.hijoIzquierdo)\n            self._preorden(arbol.hijoDerecho)\n\n\nclass NodoArbol:\n   def __init__(self,clave,valor,izquierdo=None,derecho=None,padre=None):\n        self.clave = clave\n        self.cargaUtil = valor\n        self.hijoIzquierdo = izquierdo\n        self.hijoDerecho = derecho\n        self.padre = padre\n        self.factorEquilibrio = 0\n\n    def tieneHijoIzquierdo(self):\n        return self.hijoIzquierdo\n       \n    def tieneHijoDerecho(self):\n        return self.hijoDerecho\n\n    def esHijoIzquierdo(self):\n        return self.padre and self.padre.hijoIzquierdo == self\n    \n    def esHijoDerecho(self):\n        return self.padre and self.padre.hijoDerecho == self\n\n    def esRaiz(self):\n        return not self.padre\n\n    def esHoja(self):\n        return not (self.hijoDerecho or self.hijoIzquierdo)\n\n    def tieneAlgunHijo(self):\n        return self.hijoDerecho or self.hijoIzquierdo\n\n    def tieneAmbosHijos(self):\n        return self.hijoDerecho and self.hijoIzquierdo\n\n    def reemplazarDatoDeNodo(self,clave,valor,hizq,hder):\n        self.clave = clave\n        self.cargaUtil = valor\n        self.hijoIzquierdo = hizq\n        self.hijoDerecho = hder\n        if self.tieneHijoIzquierdo():\n            self.hijoIzquierdo.padre = self\n        if self.tieneHijoDerecho():\n            self.hijoDerecho.padre = self    \n\n    def encontrarSucesor(self):\n        suc = None\n        if self.tieneHijoDerecho():\n            suc = self.hijoDerecho.encontrarMin()\n        else:\n            if self.padre:\n                if self.esHijoIzquierdo():\n                    suc = self.padre\n                else:\n                    self.padre.hijoDerecho = None\n                    suc = self.padre.encontrarSucesor()\n                    self.padre.hijoDerecho = self\n        return suc\n\n    def empalmar(self):\n        if self.esHoja():\n            if self.esHijoIzquierdo():\n                self.padre.hijoIzquierdo = None\n            else:\n                self.padre.hijoDerecho = None\n        elif self.tieneAlgunHijo():\n            if self.tieneHijoIzquierdo():\n                if self.esHijoIzquierdo():\n                    self.padre.hijoIzquierdo = self.hijoIzquierdo\n                else:\n                    self.padre.hijoDerecho = self.hijoIzquierdo\n                self.hijoIzquierdo.padre = self.padre\n            else:\n                if self.esHijoIzquierdo():\n                    self.padre.hijoIzquierdo = self.hijoDerecho\n                else:\n                    self.padre.hijoDerecho = self.hijoDerecho\n                self.hijoDerecho.padre = self.padre\n\n    def encontrarMin(self):\n        actual = self\n        while actual.tieneHijoIzquierdo():\n            actual = actual.hijoIzquierdo\n        return actual\n\n    def __iter__(self):\n        if self:\n    \t    if self.tieneHijoIzquierdo():\n    \t        for elem in self.hijoIzquierdo:\n    \t\t    yield elem\n            yield self.clave\n    \t    if self.tieneHijoDerecho():\n                for elem in self.hijoDerecho:\n    \t\t    yield elem\n","src/lib/pythoned/arboles/arbolBinario.py":"# Bradley N. Miller, David L. Ranum\n# Solución de problemas con algoritmos y estructuras de datos usando Python\n# Copyright 2014\n# \n#arbolBinario.py\n\nclass ArbolBinario:\n    def __init__(self,objetoRaiz):\n        self.clave = objetoRaiz\n        self.hijoIzquierdo = None\n        self.hijoDerecho = None\n    \n    def insertarIzquierdo(self,nuevoNodo):\n        if self.hijoIzquierdo == None:\n            self.hijoIzquierdo = ArbolBinario(nuevoNodo)\n        else:  \n            t = ArbolBinario(nuevoNodo)\n            t.hijoIzquierdo = self.hijoIzquierdo\n            self.hijoIzquierdo = t\n\n    def insertarDerecho(self,nuevoNodo):\n        if self.hijoDerecho == None:\n            self.hijoDerecho = ArbolBinario(nuevoNodo)\n        else:\n            t = ArbolBinario(nuevoNodo)\n            t.hijoDerecho = self.hijoDerecho\n            self.hijoDerecho = t\n\n    def esHoja(self):\n        return ((not self.hijoIzquierdo) and (not self.hijoDerecho))\n\n    def obtenerHijoDerecho(self):\n        return self.hijoDerecho\n    \n    def obtenerHijoIzquierdo(self):\n        return self.hijoIzquierdo\n    \n    def asignarValorRaiz(self,obj):\n        self.clave = obj\n\n    def obtenerValorRaiz(self):\n        return self.clave\n\n    def inorden(self):\n        if self.hijoIzquierdo:\n            self.hijoIzquierdo.inorden()\n        print(self.clave)\n        if self.hijoDerecho:\n            self.hijoDerecho.inorden()\n\n    def postorden(self):\n        if self.hijoIzquierdo:\n            self.hijoIzquierdo.postorden()\n        if self.hijoDerecho:\n            self.hijoDerecho.postorden()\n        print(self.clave)\n\n    def preorden(self):\n        print(self.clave)\n        if self.hijoIzquierdo:\n            self.hijoIzquierdo.preorden()\n        if self.hijoDerecho:\n            self.hijoDerecho.preorden()\n\n    def imprimirExpresion(self):\n        if self.hijoIzquierdo:\n            print('(', end=' ')\n            self.hijoIzquierdo.imprimirExpresion()\n        print(self.clave, end=' ')\n        if self.hijoDerecho:\n            self.hijoDerecho.imprimirExpresion()\n            print(')', end=' ')\n\n    def evalPostorden(self):\n        opers = {'+':operator.add, '-':operator.sub, '*':operator.mul, '/':operator.truediv}\n        res1 = None\n        res2 = None\n        if self.hijoIzquierdo:\n            res1 = self.hijoIzquierdo.evalPostorden()  #// \\label{peleft}\n        if self.hijoDerecho:\n            res2 = self.hijoDerecho.evalPostorden() #// \\label{peright}\n        if res1 and res2:\n            return opers[self.clave](res1,res2) #// \\label{peeval}\n        else:\n            return self.clave\n\ndef inorden(arbol):\n    if arbol != None:\n        inorden(arbol.obtenerHijoIzquierdo())\n        print(arbol.obtenerValorRaiz())\n        inorden(arbol.obtenerHijoDerecho())\n\ndef imprimirExpresion(arbol):\n    if arbol.hijoIzquierdo:\n        print('(', end=' ')\n        imprimirExpresion(arbol.obtenerHijoIzquierdo())\n    print(arbol.obtenerValorRaiz(), end=' ')\n    if arbol.hijoDerecho:\n        imprimirExpresion(arbol.obtenerHijoDerecho())\n        print(')', end=' ') \n\ndef imprimirExpresion(arbol):\n  valorCadena = \"\"\n  if arbol:\n      valorCadena = '(' + imprimirExpresion(arbol.obtenerHijoIzquierdo())\n      valorCadena = valorCadena + str(arbol.obtenerValorRaiz())\n      valorCadena = valorCadena + imprimirExpresion(arbol.obtenerHijoDerecho())+')'\n  return valorCadena\n\ndef evalPostorden(arbol):\n    operadores = {'+':operator.add, '-':operator.sub, '*':operator.mul, '/':operator.truediv}\n    res1 = None\n    res2 = None\n    if arbol:\n        res1 = evalPostorden(arbol.obtenerHijoIzquierdo()) #// \\label{peleft}\n        res2 = evalPostorden(arbol.obtenerHijoDerecho())  #// \\label{peright}\n        if res1 and res2:\n            return operadores[arbol.obtenerValorRaiz()](res1,res2) #// \\label{peeval}\n        else:\n            return arbol.obtenerValorRaiz()\n\ndef altura(arbol):\n    if arbol == None:\n        return -1\n    else:\n        return 1 + max(altura(arbol.hijoIzquierdo),altura(arbol.hijoDerecho))\n\nt = ArbolBinario(7)\nt.insertarIzquierdo(3)\nt.insertarDerecho(9)\ninorden(t)\nimport operator\nx = ArbolBinario('*')\nx.insertarIzquierdo('+')\nl = x.obtenerHijoIzquierdo()\nl.insertarIzquierdo(4)\nl.insertarDerecho(5)\nx.insertarDerecho(7)\nprint(imprimirExpresion(x))\nprint(evalPostorden(x))\nprint(altura(x))\n","src/lib/pythoned/arboles/avl.py":"# Bradley N. Miller, David L. Ranum\n# Solución de problemas con algoritmos y estructuras de datos usando Python\n# Copyright 2014\n#\n#avl.py\n\nfrom .abb import ArbolBinarioBusqueda, NodoArbol\n\nclass ArbolAVL(ArbolBinarioBusqueda):\n\n    def _agregar(self,clave,valor,nodoActual):\n    \tif clave < nodoActual.clave:\n    \t    if nodoActual.tieneHijoIzquierdo():\n    \t\tself._agregar(clave,valor,nodoActual.hijoIzquierdo)\n    \t    else:\n    \t\tnodoActual.hijoIzquierdo = NodoArbol(clave,valor,padre=nodoActual)\n    \t\tself.actualizarEquilibrio(nodoActual.hijoIzquierdo)\n    \telse:\n    \t    if nodoActual.tieneHijoDerecho():\n    \t\tself._agregar(clave,valor,nodoActual.hijoDerecho)\n    \t    else:\n    \t\tnodoActual.hijoDerecho = NodoArbol(clave,valor,padre=nodoActual)\n    \t\tself.actualizarEquilibrio(nodoActual.hijoDerecho)\n\n    def actualizarEquilibrio(self,nodo):\n    \tif nodo.factorEquilibrio > 1 or nodo.factorEquilibrio < -1:\n    \t    self.reequilibrar(nodo)    \n    \t    return\n    \tif nodo.padre != None:\n    \t    if nodo.esHijoIzquierdo():\n    \t\t    nodo.padre.factorEquilibrio += 1\n    \t    elif nodo.esHijoDerecho():\n    \t\t    nodo.padre.factorEquilibrio -= 1\n\n    \t    if nodo.padre.factorEquilibrio != 0:\n    \t\t    self.actualizarEquilibrio(nodo.padre)\n\n    def reequilibrar(self,nodo):\n        if nodo.factorEquilibrio < 0:\n\t    if nodo.hijoDerecho.factorEquilibrio > 0:\n\t        self.rotarDerecha(nodo.hijoDerecho)\n\t        self.rotarIzquierda(nodo)\n\t    else:\n\t        self.rotarIzquierda(nodo)\n        elif nodo.factorEquilibrio > 0:\n\t    if nodo.hijoIzquierdo.factorEquilibrio < 0:\n\t        self.rotarIzquierda(nodo.hijoIzquierdo)\n\t        self.rotarDerecha(nodo)\n\t     else:\n\t        self.rotarDerecha(nodo)\n           \n    def rotarIzquierda(self,rotRaiz):\n    \tnuevaRaiz = rotRaiz.hijoDerecho\n    \trotRaiz.hijoDerecho = nuevaRaiz.hijoIzquierdo\n    \tif nuevaRaiz.hijoIzquierdo != None:\n    \t    nuevaRaiz.hijoIzquierdo.padre = rotRaiz\n    \tnuevaRaiz.padre = rotRaiz.padre\n    \tif rotRaiz.esRaiz():\n    \t    self.raiz = nuevaRaiz\n    \telse:\n    \t    if rotRaiz.esHijoIzquierdo():\n    \t        rotRaiz.padre.hijoIzquierdo = nuevaRaiz\n    \t    else:\n    \t    \trotRaiz.padre.hijoDerecho = nuevaRaiz\n    \tnuevaRaiz.hijoIzquierdo = rotRaiz\n    \trotRaiz.padre = nuevaRaiz\n    \trotRaiz.factorEquilibrio = rotRaiz.factorEquilibrio + 1 - min(nuevaRaiz.factorEquilibrio, 0)\n    \tnuevaRaiz.factorEquilibrio = nuevaRaiz.factorEquilibrio + 1 + max(rotRaiz.factorEquilibrio, 0)\n\n    def rotarDerecha(self,rotRaiz):\n        nuevaRaiz = rotRaiz.hijoIzquierdo\n        rotRaiz.hijoIzquierdo = nuevaRaiz.hijoDerecho\n        if nuevaRaiz.hijoDerecho != None:\n            nuevaRaiz.hijoDerecho.padre = rotRaiz\n        nuevaRaiz.padre = rotRaiz.padre\n        if rotRaiz.esRaiz():\n            self.raiz = nuevaRaiz\n        else:\n            if rotRaiz.esHijoDerecho():\n                rotRaiz.padre.hijoDerecho = nuevaRaiz\n            else:\n                rotRaiz.padre.hijoIzquierdo = nuevaRaiz\n        nuevaRaiz.hijoDerecho = rotRaiz\n        rotRaiz.padre = nuevaRaiz\n        rotRaiz.factorEquilibrio = rotRaiz.factorEquilibrio - 1 - max(nuevaRaiz.factorEquilibrio, 0)\n        nuevaRaiz.factorEquilibrio = nuevaRaiz.factorEquilibrio - 1 + min(rotRaiz.factorEquilibrio, 0)  \n\n","src/lib/pythoned/arboles/monticuloBinario.py":"# Bradley N. Miller, David L. Ranum\n# Solución de problemas con algoritmos y estructuras de datos usando Python\n# Copyright 2014\n# \n#monticuloBinario.py\n\nclass MonticuloBinario:\n    def __init__(self):\n        self.listaMonticulo = [0]\n        self.tamanoActual = 0\n\n    def construirMonticulo(self,unaLista):\n        i = len(unaLista) // 2\n        self.tamanoActual = len(unaLista)\n        self.listaMonticulo = [0] + unaLista[:]\n        print(len(self.listaMonticulo), i)\n        while (i > 0):\n            print(self.listaMonticulo, i)\n            self.infiltAbajo(i)\n            i = i - 1\n        print(self.listaMonticulo,i)\n                        \n    def infiltAbajo(self,i):\n        while (i * 2) <= self.tamanoActual:\n            hm = self.hijoMin(i)\n            if self.listaMonticulo[i] > self.listaMonticulo[hm]:\n                tmp = self.listaMonticulo[i]\n                self.listaMonticulo[i] = self.listaMonticulo[hm]\n                self.listaMonticulo[hm] = tmp\n            i = hm\n                \n    def hijoMin(self,i):\n        if i * 2 + 1 > self.tamanoActual:\n            return i * 2\n        else:\n            if self.listaMonticulo[i * 2] < self.listaMonticulo[i * 2 + 1]:\n                return i * 2\n            else:\n                return i * 2 + 1\n\n    def infiltArriba(self,i):\n        while i // 2 > 0:\n            if self.listaMonticulo[i] < self.listaMonticulo[i//2]:\n               tmp = self.listaMonticulo[i // 2]\n               self.listaMonticulo[i // 2] = self.listaMonticulo[i]\n               self.listaMonticulo[i] = tmp\n            i = i // 2\n \n    def insertar(self,k):\n        self.listaMonticulo.append(k)\n        self.tamanoActual = self.tamanoActual + 1\n        self.infiltArriba(self.tamanoActual)\n\n    def eliminarMin(self):\n        valorSacado = self.listaMonticulo[1]\n        self.listaMonticulo[1] = self.listaMonticulo[self.tamanoActual]\n        self.tamanoActual = self.tamanoActual - 1\n        self.listaMonticulo.pop()\n        self.infiltAbajo(1)\n        return valorSacado\n        \n    def estaVacio(self):\n        if tamanoActual == 0:\n            return True\n        else:\n            return False\n","src/lib/pythoned/basicas/__init__.py":"\n#__all__ = [\"pila\"]\n\n\nfrom .pila import Pila\nfrom .cola import Cola\nfrom .coladoble import ColaDoble\n\n\n","src/lib/pythoned/basicas/cola.py":"# Bradley N. Miller, David L. Ranum\n# Solución de problemas con algoritmos y estructuras de datos usando Python\n# Copyright 2014\n#\n#cola.py\n\nclass Cola:\n    def __init__(self):\n        self.items = []\n\n    def estaVacia(self):\n        return self.items == []\n\n    def agregar(self, item):\n        self.items.insert(0,item)\n\n    def avanzar(self):\n        return self.items.pop()\n\n    def tamano(self):\n        return len(self.items)\n","src/lib/pythoned/basicas/coladoble.py":"# Bradley N. Miller, David L. Ranum\n# Solución de problemas con algoritmos y estructuras de datos usando Python\n# Copyright 2014\n# \n#coladoble.py\n\nclass ColaDoble:\n    def __init__(self):\n        self.items = []\n\n    def estaVacia(self):\n        return self.items == []\n\n    def agregarFrente(self, item):\n        self.items.append(item)\n\n    def agregarFinal(self, item):\n        self.items.insert(0,item)\n\n    def removerFrente(self):\n        return self.items.pop()\n\n    def removerFinal(self):\n        return self.items.pop(0)\n\n    def tamano(self):\n        return len(self.items)\n","src/lib/pythoned/basicas/pila.py":"# Bradley N. Miller, David L. Ranum\n# Solución de problemas con algoritmos y estructuras de datos usando Python\n# Copyright 2014\n# \n#pila.py\n\nclass Pila:\n    def __init__(self):\n        self.items = []\n\n    def estaVacia(self):\n        return self.items == []\n\n    def incluir(self, item):\n        self.items.append(item)\n\n    def extraer(self):\n        return self.items.pop()\n\n    def inspeccionar(self):\n        return self.items[len(self.items)-1]\n\n    def tamano(self):\n        return len(self.items)\n","src/lib/pythoned/grafos/__init__.py":"\n\nfrom .grafoAdy import Grafo\nfrom .grafoAdy import Vertice\nfrom .colaPrioridad import ColaPrioridad\n","src/lib/pythoned/grafos/colaPrioridad.py":"# Bradley N. Miller, David L. Ranum\n# Solución de problemas con algoritmos y estructuras de datos usando Python\n# Copyright 2014\n# \n#colaPrioridad.py\n\nclass ColaPrioridad:\n    def __init__(self):\n        self.arregloMonticulo = [(0,0)]\n        self.tamanoActual = 0\n\n    def construirMonticulo(self,unaLista):\n        self.tamanoActual = len(unaLista)\n        self.arregloMonticulo = [(0,0)]\n        for i in unaLista:\n            self.arregloMonticulo.append(i)\n        i = len(unaLista) // 2            \n        while (i > 0):\n            self.infiltAbajo(i)\n            i = i - 1\n                        \n    def infiltAbajo(self,i):\n        while (i * 2) <= self.tamanoActual:\n            hm = self.hijoMin(i)\n            if self.arregloMonticulo[i][0] > self.arregloMonticulo[hm][0]:\n                tmp = self.arregloMonticulo[i]\n                self.arregloMonticulo[i] = self.arregloMonticulo[hm]\n                self.arregloMonticulo[hm] = tmp\n            i = hm\n                \n    def hijoMin(self,i):\n        if i*2 > self.tamanoActual:\n            return -1\n        else:\n            if i*2 + 1 > self.tamanoActual:\n                return i*2\n            else:\n                if self.arregloMonticulo[i*2][0] < self.arregloMonticulo[i*2+1][0]:\n                    return i*2\n                else:\n                    return i*2+1\n\n    def infiltArriba(self,i):\n        while i // 2 > 0:\n            if self.arregloMonticulo[i][0] < self.arregloMonticulo[i//2][0]:\n               tmp = self.arregloMonticulo[i//2]\n               self.arregloMonticulo[i//2] = self.arregloMonticulo[i]\n               self.arregloMonticulo[i] = tmp\n            i = i//2\n \n    def insertar(self,k):\n        self.arregloMonticulo.append(k)\n        self.tamanoActual = self.tamanoActual + 1\n        self.infiltArriba(self.tamanoActual)\n\n    def eliminarMin(self):\n        valorSacado = self.arregloMonticulo[1][1]\n        self.arregloMonticulo[1] = self.arregloMonticulo[self.tamanoActual]\n        self.tamanoActual = self.tamanoActual - 1\n        self.arregloMonticulo.pop()\n        self.infiltAbajo(1)\n        return valorSacado\n        \n    def estaVacia(self):\n        if self.tamanoActual == 0:\n            return True\n        else:\n            return False\n\n    def decrementarClave(self,valor,nuevo):\n        hecho = False\n        i = 1\n        miClave = 0\n        while not hecho and i <= self.tamanoActual:\n            if self.arregloMonticulo[i][1] == valor:\n                hecho = True\n                miClave = i\n            else:\n                i = i + 1\n        if miClave > 0:\n            self.arregloMonticulo[miClave] = (nuevo,self.arregloMonticulo[miClave][1])\n            self.infiltArriba(miClave)\n            \n    def __contains__(self,vertice):\n        for pareja in self.arregloMonticulo:\n            if pareja[1] == vertice:\n                return True\n        return False     \n\n","src/lib/pythoned/grafos/grafoAdy.py":"# Bradley N. Miller, David L. Ranum\n# Solución de problemas con algoritmos y estructuras de datos usando Python\n# Copyright 2014\n#\n#grafoAdy.py\n\n\nimport sys\n\nclass Grafo:\n    def __init__(self):\n        self.listaVertices = {}\n        self.numVertices = 0\n        \n    def agregarVertice(self,clave):\n        self.numVertices = self.numVertices + 1\n        nuevoVertice = Vertice(clave)\n        self.listaVertices[clave] = nuevoVertice\n        return nuevoVertice\n    \n    def obtenerVertice(self,n):\n        if n in self.listaVertices:\n            return self.listaVertices[n]\n        else:\n            return None\n\n    def __contains__(self,n):\n        return n in self.listaVertices\n    \n    def agregarArista(self,de,a,costo=0):\n            if de not in self.listaVertices:\n                nv = self.agregarVertice(de)\n            if a not in self.listaVertices:\n                nv = self.agregarVertice(a)\n            self.listaVertices[de].agregarVecino(self.listaVertices[a],costo)\n    \n    def obtenerVertices(self):\n        return list(self.listaVertices.keys())\n        \n    def __iter__(self):\n        return iter(self.listaVertices.values())\n                \nclass Vertice:\n    def __init__(self,clave):\n        self.id = clave\n        self.conectadoA = {}\n        self.color = 'blanco'\n        self.dist = sys.maxsize\n        self.predecesor = None\n        self.desc = 0\n        self.fin = 0\n\n    # def __lt__(self,o):\n    #     return self.id < o.id\n    \n    def  agregarVecino(self,vecino,ponderacion=0):\n        self.conectadoA[vecino] = ponderacion\n        \n    def asignarColor(self,color):\n        self.color = color\n        \n    def asignarDistancia(self,d):\n        self.dist = d\n\n    def asignarPredecesor(self,p):\n        self.predecesor = p\n\n    def asignarDescubrimiento(self,tiempoDescubrimiento):\n        self.desc = tiempoDescubrimiento\n        \n    def asignarFinalizacion(self,tiempoFinalizacion):\n        self.fin = tiempoFinalizacion\n        \n    def obtenerFinalizacion(self):\n        return self.fin\n        \n    def obtenerDescubrimiento(self):\n        return self.desc\n        \n    def obtenerPredecesor(self):\n        return self.predecesor\n        \n    def obtenerDistancia(self):\n        return self.dist\n        \n    def obtenerColor(self):\n        return self.color\n    \n    def obtenerConexiones(self):\n        return self.conectadoA.keys()\n        \n    def obtenerPonderacion(self,vecino):\n        return self.conectadoA[vecino]\n                \n    def __str__(self):\n        return str(self.id) + \":color \" + self.color + \":desc \" + str(self.desc) + \":fin \" + str(self.fin) + \":distancia \" + str(self.dist) + \":predecesor \\n\\t[\" + str(self.predecesor)+ \"]\\n\"\n    \n    def obtenerId(self):\n        return self.id\n","src/lib/quopri.py":"raise NotImplementedError(\"quopri is not yet implemented in Skulpt\")\n","src/lib/random.js":"var MersenneTwister=function(a){a==null&&(a=new Date().getTime()),this.N=624,this.M=397,this.MATRIX_A=2567483615,this.UPPER_MASK=2147483648,this.LOWER_MASK=2147483647,this.mt=Array(this.N),this.mti=this.N+1,this.init_genrand(a)};MersenneTwister.prototype.init_genrand=function(a){for(this.mt[0]=a>>>0,this.mti=1;this.mti<this.N;this.mti++){var a=this.mt[this.mti-1]^this.mt[this.mti-1]>>>30;this.mt[this.mti]=(1812433253*((4294901760&a)>>>16)<<16)+1812433253*(65535&a)+this.mti,this.mt[this.mti]>>>=0}},MersenneTwister.prototype.init_by_array=function(a,b){var d,e,f;for(this.init_genrand(19650218),d=1,e=0,f=this.N>b?this.N:b;f;f--){var g=this.mt[d-1]^this.mt[d-1]>>>30;this.mt[d]=(this.mt[d]^(1664525*((4294901760&g)>>>16)<<16)+1664525*(65535&g))+a[e]+e,this.mt[d]>>>=0,d++,e++,d>=this.N&&(this.mt[0]=this.mt[this.N-1],d=1),e>=b&&(e=0)}for(f=this.N-1;f;f--){var g=this.mt[d-1]^this.mt[d-1]>>>30;this.mt[d]=(this.mt[d]^(1566083941*((4294901760&g)>>>16)<<16)+1566083941*(65535&g))-d,this.mt[d]>>>=0,d++,d>=this.N&&(this.mt[0]=this.mt[this.N-1],d=1)}this.mt[0]=2147483648},MersenneTwister.prototype.genrand_int32=function(){var a,b=[0,this.MATRIX_A];if(this.mti>=this.N){var d;for(this.mti==this.N+1&&this.init_genrand(5489),d=0;d<this.N-this.M;d++)a=this.mt[d]&this.UPPER_MASK|this.mt[d+1]&this.LOWER_MASK,this.mt[d]=this.mt[d+this.M]^a>>>1^b[1&a];for(;d<this.N-1;d++)a=this.mt[d]&this.UPPER_MASK|this.mt[d+1]&this.LOWER_MASK,this.mt[d]=this.mt[d+(this.M-this.N)]^a>>>1^b[1&a];a=this.mt[this.N-1]&this.UPPER_MASK|this.mt[0]&this.LOWER_MASK,this.mt[this.N-1]=this.mt[this.M-1]^a>>>1^b[1&a],this.mti=0}return a=this.mt[this.mti++],a^=a>>>11,a^=2636928640&a<<7,a^=4022730752&a<<15,a^=a>>>18,a>>>0},MersenneTwister.prototype.genrand_int31=function(){return this.genrand_int32()>>>1},MersenneTwister.prototype.genrand_real1=function(){return this.genrand_int32()*(1/4294967295)},MersenneTwister.prototype.random=function(){return this.genrand_int32()*(1/4294967296)},MersenneTwister.prototype.genrand_real3=function(){return(this.genrand_int32()+.5)*(1/4294967296)},MersenneTwister.prototype.genrand_res53=function(){var d=this.genrand_int32()>>>5,a=this.genrand_int32()>>>6;return(67108864*d+a)*(1/9007199254740992)};var $builtinmodule=function(){var a=Math.log,b=Math.sqrt,d={},e=new MersenneTwister,f=void 0;d.seed=new Sk.builtin.func(function(a){return Sk.builtin.pyCheckArgsLen(\"seed\",arguments.length,0,1),a=Sk.builtin.asnum$(a),e=0<arguments.length?new MersenneTwister(a):new MersenneTwister,Sk.builtin.none.none$}),d.random=new Sk.builtin.func(function(){return Sk.builtin.pyCheckArgsLen(\"random\",arguments.length,0,0),new Sk.builtin.float_(e.genrand_res53())});var g=function(a){return 0|a},h=function(a,b,d){var f,h,i;if(!Sk.builtin.checkInt(a))throw new Sk.builtin.ValueError(\"non-integer first argument for randrange()\");if(void 0===b)return i=g(e.genrand_res53()*a),new Sk.builtin.int_(i);if(!Sk.builtin.checkInt(b))throw new Sk.builtin.ValueError(\"non-integer stop for randrange()\");if(void 0===d&&(d=1),f=b-a,1==d&&0<f)return i=a+g(e.genrand_res53()*f),new Sk.builtin.int_(i);if(1==d)throw new Sk.builtin.ValueError(\"empty range for randrange() (\"+a+\", \"+b+\", \"+f+\")\");if(!Sk.builtin.checkInt(d))throw new Sk.builtin.ValueError(\"non-integer step for randrange()\");if(0<d)h=g((f+d-1)/d);else if(0>d)h=g((f+d+1)/d);else throw new Sk.builtin.ValueError(\"zero step for randrange()\");if(0>=h)throw new Sk.builtin.ValueError(\"empty range for randrange()\");return i=a+d*g(e.genrand_res53()*h),new Sk.builtin.int_(i)};d.randint=new Sk.builtin.func(function(d,e){return Sk.builtin.pyCheckArgsLen(\"randint\",arguments.length,2,2),d=Sk.builtin.asnum$(d),e=Sk.builtin.asnum$(e),h(d,e+1)}),d.randrange=new Sk.builtin.func(function(a,b,d){return Sk.builtin.pyCheckArgsLen(\"randrange\",arguments.length,1,3),a=Sk.builtin.asnum$(a),b=Sk.builtin.asnum$(b),d=Sk.builtin.asnum$(d),h(a,b,d)}),d.uniform=new Sk.builtin.func(function(d,f){Sk.builtin.pyCheckArgsLen(\"uniform\",arguments.length,2,2),d=Sk.builtin.asnum$(d),f=Sk.builtin.asnum$(f);var g=e.genrand_res53();return c=d+g*(f-d),new Sk.builtin.float_(c)}),d.triangular=new Sk.builtin.func(function(a,d,f){Sk.builtin.pyCheckArgsLen(\"triangular\",arguments.length,2,3),Sk.builtin.pyCheckType(\"low\",\"number\",Sk.builtin.checkNumber(a)),Sk.builtin.pyCheckType(\"high\",\"number\",Sk.builtin.checkNumber(d));var g,h,i;return a=Sk.builtin.asnum$(a),d=Sk.builtin.asnum$(d),a>d&&(i=a,a=d,d=i),void 0===f||f===Sk.builtin.none.none$?f=(d-a)/2:(Sk.builtin.pyCheckType(\"mode\",\"number\",Sk.builtin.checkNumber(f)),f=Sk.builtin.asnum$(f)),g=e.genrand_res53(),h=g<(f-a)/(d-a)?a+b(g*(d-a)*(f-a)):d-b((1-g)*(d-a)*(d-f)),new Sk.builtin.float_(h)});var i=function(d,g){var k,l,m,n,o,h=Math.sin,i=Math.cos,j=Math.PI;return void 0===f?(k=e.genrand_res53(),l=e.genrand_res53(),m=b(-2*a(k)),n=2*j*l,o=m*i(n),f=m*h(n)):(o=f,f=void 0),d+g*o};return d.gauss=new Sk.builtin.func(function(a,b){return Sk.builtin.pyCheckArgsLen(\"gauss\",arguments.length,2,2),Sk.builtin.pyCheckType(\"mu\",\"number\",Sk.builtin.checkNumber(a)),Sk.builtin.pyCheckType(\"sigma\",\"number\",Sk.builtin.checkNumber(b)),a=Sk.builtin.asnum$(a),b=Sk.builtin.asnum$(b),new Sk.builtin.float_(i(a,b))}),d.normalvariate=d.gauss,d.lognormvariate=new Sk.builtin.func(function(a,b){var d=Math.exp;return Sk.builtin.pyCheckArgsLen(\"lognormvariate\",arguments.length,2,2),Sk.builtin.pyCheckType(\"mu\",\"number\",Sk.builtin.checkNumber(a)),Sk.builtin.pyCheckType(\"sigma\",\"number\",Sk.builtin.checkNumber(b)),a=Sk.builtin.asnum$(a),b=Sk.builtin.asnum$(b),new Sk.builtin.float_(d(i(a,b)))}),d.expovariate=new Sk.builtin.func(function(b){Sk.builtin.pyCheckArgsLen(\"expovariate\",arguments.length,1,1),Sk.builtin.pyCheckType(\"lambd\",\"number\",Sk.builtin.checkNumber(b)),b=Sk.builtin.asnum$(b);var d=e.genrand_res53();return new Sk.builtin.float_(-a(d)/b)}),d.choice=new Sk.builtin.func(function(a){if(Sk.builtin.pyCheckArgsLen(\"choice\",arguments.length,1,1),Sk.builtin.pyCheckType(\"seq\",\"sequence\",Sk.builtin.checkSequence(a)),void 0!==a.sq$length){var b=new Sk.builtin.int_(g(e.genrand_res53()*a.sq$length()));return a.mp$subscript(b)}throw new Sk.builtin.TypeError(\"object has no length\")}),d.shuffle=new Sk.builtin.func(function(a){if(Sk.builtin.pyCheckArgsLen(\"shuffle\",arguments.length,1,1),Sk.builtin.pyCheckType(\"x\",\"sequence\",Sk.builtin.checkSequence(a)),a.constructor===Sk.builtin.list){const h=a.v;for(var b=h.length-1;0<b;b-=1){var d=g(e.genrand_res53()*(b+1)),f=h[d];h[d]=h[b],h[b]=f}}else if(void 0===a.sq$length)throw new Sk.builtin.TypeError(\"object has no length\");else if(void 0!==a.mp$ass_subscript)for(var d,b=a.sq$length()-1;0<b;b-=1){d=new Sk.builtin.int_(g(e.genrand_res53()*(b+1))),b=new Sk.builtin.int_(b);var f=a.mp$subscript(d);a.mp$ass_subscript(d,a.mp$subscript(b)),a.mp$ass_subscript(b,f)}else throw new Sk.builtin.TypeError(\"object is immutable\");return Sk.builtin.none.none$}),d.sample=new Sk.builtin.func(function(a,b){var f,g,h,l,m,d=Math.floor;for(Sk.builtin.pyCheckArgsLen(\"sample\",arguments.length,2,2),Sk.builtin.pyCheckType(\"population\",\"iterable\",Sk.builtin.checkIterable(a)),Sk.builtin.pyCheckType(\"k\",\"integer\",Sk.builtin.checkInt(b)),b=Sk.builtin.asnum$(b),m=[],h=Sk.abstr.iter(a),(f=0,l=h.tp$iternext());void 0!==l;f++,l=h.tp$iternext())g=d(e.genrand_res53()*(f+1)),f<b?(g<f&&(m[f]=m[g]),m[g]=l):g<b&&(m[g]=l);if(f<b)throw new Sk.builtin.ValueError(\"sample larger than population\");return new Sk.builtin.list(m)}),d};","src/lib/re.js":"var $builtinmodule=function(name){var validGroups,convert,getFlags,_split,_findall,matchobj,_search,_match,regexobj,mod={__name__:new Sk.builtin.str(\"re\")};return mod.I=2,mod.IGNORECASE=2,mod.M=8,mod.MULTILINE=8,validGroups=[\"(?:\",\"(?=\",\"(?!\"],convert=function(a){var b,c,d;if(c=a.match(/\\(\\?./g),c)for(d=0;d<c.length;d++)if(-1==validGroups.indexOf(c[d]))throw new Sk.builtin.ValueError(\"Disallowed group in pattern: '\"+c[d]+\"'\");return b=a.replace(\"/\\\\/g\",\"\\\\\\\\\"),b=a.replace(/([^\\\\]){,(?![^\\[]*\\])/g,\"$1{0,\"),b},getFlags=function(a){var b=\"g\";return(a&mod.IGNORECASE)==mod.IGNORECASE&&(b+=\"i\"),(a&mod.MULTILINE)==mod.MULTILINE&&(b+=\"m\"),b},_split=function(a,b,c,d){var e,f,g,h,i,j,k,l,m;if(Sk.builtin.pyCheckArgsLen(\"split\",arguments.length,2,4),!Sk.builtin.checkString(a))throw new Sk.builtin.TypeError(\"pattern must be a string\");if(!Sk.builtin.checkString(b))throw new Sk.builtin.TypeError(\"string must be a string\");if(void 0===c&&(c=0),!Sk.builtin.checkNumber(c))throw new Sk.builtin.TypeError(\"maxsplit must be a number\");if(void 0===d&&(d=0),!Sk.builtin.checkNumber(d))throw new Sk.builtin.TypeError(\"flags must be a number\");for(c=Sk.builtin.asnum$(c),e=Sk.ffi.unwrapo(a),f=Sk.ffi.unwrapo(b),e=convert(e),g=null!==e.match(/^\\(.*\\)$/),h=getFlags(d),i=new RegExp(e,h),j=[],k,l=0,m=0;null!=(k=i.exec(f))&&k.index!==i.lastIndex&&(j.push(new Sk.builtin.str(f.substring(l,k.index))),g&&j.push(new Sk.builtin.str(k[0])),l=i.lastIndex,m+=1,!(c&&m>=c)););return j.push(new Sk.builtin.str(f.substring(l))),new Sk.builtin.list(j)},_split.co_varnames=[\"pattern\",\"string\",\"maxsplit\",\"flags\"],_split.$defaults=[new Sk.builtin.int_(0),new Sk.builtin.int_(0)],mod.split=new Sk.builtin.func(_split),_findall=function(a,b,c){var d,e,f,g,h,j;if(Sk.builtin.pyCheckArgsLen(\"findall\",arguments.length,2,3),!Sk.builtin.checkString(a))throw new Sk.builtin.TypeError(\"pattern must be a string\");if(!Sk.builtin.checkString(b))throw new Sk.builtin.TypeError(\"string must be a string\");if(void 0===c&&(c=0),!Sk.builtin.checkNumber(c))throw new Sk.builtin.TypeError(\"flags must be a number\");if(d=Sk.ffi.unwrapo(a),e=Sk.ffi.unwrapo(b),d=convert(d),f=getFlags(c),g=new RegExp(d,f),d.match(/\\$/)){var k=new RegExp(/\\n$/);e.match(k)&&(e=e.slice(0,-1))}for(h=[],j;null!=(j=g.exec(e));){if(2>j.length)h.push(new Sk.builtin.str(j[0]));else if(2==j.length)h.push(new Sk.builtin.str(j[1]));else{for(var l=[],m=1;m<j.length;m++)l.push(new Sk.builtin.str(j[m]));h.push(new Sk.builtin.tuple(l))}j.index===g.lastIndex&&(g.lastIndex+=1)}return new Sk.builtin.list(h)},_findall.co_varnames=[\"pattern\",\"string\",\"flags\"],_findall.$defaults=[new Sk.builtin.int_(0)],mod.findall=new Sk.builtin.func(_findall),matchobj=function(a,b){b.__init__=new Sk.builtin.func(function(a,b,c,d){return a.thematch=b,a.re=c,a.string=d,Sk.builtin.none.none$}),b.groups=new Sk.builtin.func(function(a){var b=a.thematch.v.slice(1);return new Sk.builtin.tuple(b)}),b.group=new Sk.builtin.func(function(a,b){if(b=void 0===b?0:Sk.builtin.asnum$(b),b>=a.thematch.v.length)throw new Sk.builtin.IndexError(\"Index out of range: \"+b);return a.thematch.v[b]})},mod.MatchObject=Sk.misceval.buildClass(mod,matchobj,\"MatchObject\",[]),mod._findre=function(res,string){res=res.replace(/([^\\\\]){,(?![^\\[]*\\])/g,\"$1{0,\");var matches,sitem,retval,re=eval(res),patt=/\\n$/,str=Sk.ffi.remapToJs(string);if(matches=str.match(patt)?str.slice(0,-1).match(re):str.match(re),retval=new Sk.builtin.list,null==matches)return retval;for(var i=0;i<matches.length;++i)sitem=new Sk.builtin.str(matches[i]),retval.v.push(sitem);return retval},_search=function(a,b,c){var d,e;if(Sk.builtin.pyCheckArgsLen(\"search\",arguments.length,2,3),!Sk.builtin.checkString(a))throw new Sk.builtin.TypeError(\"pattern must be a string\");if(!Sk.builtin.checkString(b))throw new Sk.builtin.TypeError(\"string must be a string\");if(void 0===c&&(c=0),!Sk.builtin.checkNumber(c))throw new Sk.builtin.TypeError(\"flags must be a number\");return(e=\"/\"+a.v.replace(/\\//g,\"\\\\/\")+\"/\",lst=mod._findre(e,b),1>lst.v.length)?Sk.builtin.none.none$:(d=Sk.misceval.callsimArray(mod.MatchObject,[lst,a,b]),d)},_search.co_varnames=[\"pattern\",\"string\",\"flags\"],_search.$defaults=[new Sk.builtin.int_(0)],mod.search=new Sk.builtin.func(_search),_match=function(a,b,c){var d,e;if(Sk.builtin.pyCheckArgsLen(\"match\",arguments.length,2,3),!Sk.builtin.checkString(a))throw new Sk.builtin.TypeError(\"pattern must be a string\");if(!Sk.builtin.checkString(b))throw new Sk.builtin.TypeError(\"string must be a string\");if(void 0===c&&(c=0),!Sk.builtin.checkNumber(c))throw new Sk.builtin.TypeError(\"flags must be a number\");return(pat=Sk.ffi.remapToJs(a),e=\"/^\"+pat.replace(/\\//g,\"\\\\/\")+\"/\",lst=mod._findre(e,b),1>Sk.ffi.remapToJs(lst).length)?Sk.builtin.none.none$:(d=Sk.misceval.callsimArray(mod.MatchObject,[lst,a,b]),d)},_match.co_varnames=[\"pattern\",\"string\",\"flags\"],_match.$defaults=[new Sk.builtin.int_(0)],mod.match=new Sk.builtin.func(_match),regexobj=function(a,b){var c,d,e,f,g,h;b.__init__=new Sk.builtin.func(function(a,b,c){return a.re=b,a.flags=void 0===c?0:c,Sk.builtin.none.none$}),h=new Sk.builtin.func(function(a){var b=\"re.compile('\"+Sk.ffi.remapToPy(a.re)+\"')\";return Sk.ffi.remapToPy(b.substring(0,212))}),b.__str__=h,b.__repr__=h,c=function(a,b,c){var d=Sk.ffi.remapToJs(a),e=null==b?0:Sk.ffi.remapToJs(b),f=null==c?d.length:Sk.ffi.remapToJs(c);return\"^\"==e&&(e=d.indexOf(\"\\n\")+1),null===f&&(f=d.length),Sk.ffi.remapToPy(d.substring(e,f))},d=function(a,b,d,e){Sk.builtin.pyCheckArgsLen(\"search\",arguments.length,2,4);var f=c(b,d,e);return _search(a.re,f,a.flags)},d.co_varnames=[\"self\",\"string\",\"pos\",\"endpos\"],d.$defaults=[new Sk.builtin.int_(0),Sk.builtin.none.none$],b.search=new Sk.builtin.func(d),e=function(a,b,d,e){Sk.builtin.pyCheckArgsLen(\"match\",arguments.length,2,4);var f=c(b,d,e);return _match(a.re,f,a.flags)},e.co_varnames=[\"self\",\"string\",\"pos\",\"endpos\"],e.$defaults=[new Sk.builtin.int_(0),Sk.builtin.none.none$],b.match=new Sk.builtin.func(e),f=function(a,b,c){if(Sk.builtin.pyCheckArgsLen(\"split\",arguments.length,2,3),void 0===c&&(c=0),!Sk.builtin.checkInt(c))throw new Sk.builtin.TypeError(\"maxsplit must be an integer\");return _split(a.re,b,c,a.flags)},f.co_varnames=[\"self\",\"string\",\"maxsplit\"],f.$defaults=[new Sk.builtin.int_(0)],b.split=new Sk.builtin.func(f),g=function(a,b,d,e){Sk.builtin.pyCheckArgsLen(\"findall\",arguments.length,2,4);var f=c(b,d,e);return _findall(a.re,f,a.flags)},g.co_varnames=[\"self\",\"string\",\"pos\",\"endpos\"],g.$defaults=[new Sk.builtin.int_(0),Sk.builtin.none.none$],b.findall=new Sk.builtin.func(g)},mod.RegexObject=Sk.misceval.buildClass(mod,regexobj,\"RegexObject\",[]),mod.compile=new Sk.builtin.func(function(a,b){var c;if(Sk.builtin.pyCheckArgsLen(\"compile\",arguments.length,1,2),!Sk.builtin.checkString(a))throw new Sk.builtin.TypeError(\"pattern must be a string\");if(void 0===b&&(b=0),!Sk.builtin.checkNumber(b))throw new Sk.builtin.TypeError(\"flags must be a number\");return c=Sk.misceval.callsimArray(mod.RegexObject,[a,b]),c}),mod.purge=new Sk.builtin.func(function(){}),mod};","src/lib/repr.py":"raise NotImplementedError(\"repr is not yet implemented in Skulpt\")\n","src/lib/requests.py":"# most restricted version\n\"\"\"\nThis solution works in runestone - however it is much more limited.\nAn error does not occur in the same way in the textbook as it does\nfor the command line. Since HTTPError is not currently available in\nRunestone, I had to change the exception.\nDecode and Encode are not available in Runestone, so there might be\nissues with reponses.\nDoes not use the status attribute for urlopen, would be nice to have\nthat back.\nDoes not work on regular web pages (like google or the michigan daily) because of cross-site scripting limits.\n\"\"\"\nfrom urllib.request import urlopen\nimport json\n\nclass Response:\n    def __init__(self, data, url):\n        self.text = data\n        self.url = url\n\n    def json(self):\n        try:\n            return json.loads(self.text)\n        except:\n            return {\"error\": \"Response not interpretable as json. Try printing the .text attribute\"}\n\n    def __str__(self):\n        return \"<A Response object for the following request: {}>\".format(self.url)\n\n\nurl_subs = {\" \": \"+\",\n            \"!\": \"%21\",\n            '\"': \"%22\",\n            \"#\": \"%23\",\n            \"$\": \"%24\",\n            \"'\": \"%27\",\n            \"(\": \"%28\",\n            \")\": \"%29\",\n            \"*\": \"%2A\",\n            \"+\": \"%2B\",\n            \",\": \"%2C\",\n            \"/\": \"%2F\",\n            \":\": \"%3A\",\n            \";\": \"%3B\",\n            \"=\": \"%3D\",\n            \"?\": \"%3F\",\n            \"@\": \"%40\",\n            \"[\": \"%5B\",\n            \"]\": \"%5D\",\n            }\n\ndef _subst(s, substitutions=url_subs):\n    res = \"\"\n    for c in str(s):\n        if c in substitutions:\n            res += substitutions[c]\n        else:\n            res += c\n    return res\n\n\ndef requestURL(baseurl, params={}):\n    try:\n        if len(params) == 0:\n            return baseurl\n        complete_url = baseurl + \"?\"\n        pairs = [\"{}={}\".format(_subst(k), _subst(params[k])) for k in params]\n        complete_url += \"&\".join(pairs)\n        return complete_url\n    except:\n        return None\n\ndef get(baseurl, params={}):\n    full_url = requestURL(baseurl, params)\n    if not full_url:\n        text_data = \"<html><body><h1>invalid request</h1></body></html>\"\n        full_url = \"Couldn’t generate a valid URL\"\n    else:\n        data = urlopen(full_url)\n        text_data = data.read().strip()\n        if len(text_data) == 0:\n            text_data = \"Failed to retrieve that URL\"\n    return Response(text_data, full_url)","src/lib/requests_with_caching.py":"import requests\nimport json\n\nPERMANENT_CACHE_FNAME = \"permanent_cache.txt\"\nTEMP_CACHE_FNAME = \"this_page_cache.txt\"\n\ndef _write_to_file(cache, fname):\n    with open(fname, 'w') as outfile:\n        outfile.write(json.dumps(cache, indent=2))\n\ndef _read_from_file(fname):\n    try:\n        with open(fname, 'r') as infile:\n            res = infile.read()\n            return json.loads(res)\n    except:\n        return {}\n\ndef add_to_cache(cache_file, cache_key, cache_value):\n    temp_cache = _read_from_file(cache_file)\n    temp_cache[cache_key] = cache_value\n    _write_to_file(temp_cache, cache_file)\n\ndef clear_cache(cache_file=TEMP_CACHE_FNAME):\n    _write_to_file({}, cache_file)\n\ndef make_cache_key(baseurl, params_d, private_keys=[\"api_key\", \"apikey\"]):\n    \"\"\"Makes a long string representing the query.\n    Alphabetize the keys from the params dictionary so we get the same order each time.\n    Omit keys with private info.\"\"\"\n    alphabetized_keys = sorted(params_d.keys())\n    res = []\n    for k in alphabetized_keys:\n        if k not in private_keys:\n            res.append(\"{}-{}\".format(k, params_d[k]))\n    return baseurl + \"_\".join(res)\n\ndef get(baseurl, params={}, private_keys_to_ignore=[\"api_key\", \"apikey\"], permanent_cache_file=PERMANENT_CACHE_FNAME, temp_cache_file=TEMP_CACHE_FNAME):\n    full_url = requests.requestURL(baseurl, params)\n    cache_key = make_cache_key(baseurl, params, private_keys_to_ignore)\n    # Load the permanent and page-specific caches from files\n    permanent_cache = _read_from_file(permanent_cache_file)\n    temp_cache = _read_from_file(temp_cache_file)\n    if cache_key in temp_cache:\n        print(\"found in page-specific cache\")\n        # make a Response object containing text from the change, and the full_url that would have been fetched\n        return requests.Response(temp_cache[cache_key], full_url)\n    elif cache_key in permanent_cache:\n        print(\"found in permanent_cache\")\n        # make a Response object containing text from the change, and the full_url that would have been fetched\n        return requests.Response(permanent_cache[cache_key], full_url)\n    else:\n        print(\"new; adding to cache\")\n        # actually request it\n        resp = requests.get(baseurl, params)\n        # save it\n        add_to_cache(temp_cache_file, cache_key, resp.text)\n        return resp","src/lib/rexec.py":"raise NotImplementedError(\"rexec is not yet implemented in Skulpt\")\n","src/lib/rfc822.py":"raise NotImplementedError(\"rfc822 is not yet implemented in Skulpt\")\n","src/lib/rlcompleter.py":"raise NotImplementedError(\"rlcompleter is not yet implemented in Skulpt\")\n","src/lib/robotparser.py":"raise NotImplementedError(\"robotparser is not yet implemented in Skulpt\")\n","src/lib/runpy.py":"raise NotImplementedError(\"runpy is not yet implemented in Skulpt\")\n","src/lib/sched.py":"raise NotImplementedError(\"sched is not yet implemented in Skulpt\")\n","src/lib/sets.py":"raise NotImplementedError(\"sets is not yet implemented in Skulpt\")\n","src/lib/sgmllib.py":"raise NotImplementedError(\"sgmllib is not yet implemented in Skulpt\")\n","src/lib/sha.py":"raise NotImplementedError(\"sha is not yet implemented in Skulpt\")\n","src/lib/shelve.py":"raise NotImplementedError(\"shelve is not yet implemented in Skulpt\")\n","src/lib/shlex.py":"raise NotImplementedError(\"shlex is not yet implemented in Skulpt\")\n","src/lib/shutil.py":"raise NotImplementedError(\"shutil is not yet implemented in Skulpt\")\n","src/lib/signal.js":"var $builtinmodule=function(){var a={SIG_DFL:new Sk.builtin.int_(0),SIG_IGN:new Sk.builtin.int_(1),CTRL_C_EVENT:new Sk.builtin.int_(0),CTRL_BREAK_EVENT:new Sk.builtin.int_(0),NSIG:new Sk.builtin.int_(23),SIGHUP:new Sk.builtin.int_(1),SIGNINT:new Sk.builtin.int_(2),SIGILL:new Sk.builtin.int_(4),SIGFPE:new Sk.builtin.int_(8),SIGKILL:new Sk.builtin.int_(9),SIGSEGV:new Sk.builtin.int_(11),SIGTERM:new Sk.builtin.int_(15),SIGBREAK:new Sk.builtin.int_(21),SIGABRT:new Sk.builtin.int_(22),pause:new Sk.builtin.func(function(){Sk.builtin.pyCheckArgsLen(\"pause\",arguments.length,0,0);var a=new Sk.misceval.Suspension;return a.resume=function(){return Sk.builtin.none.none$},a.data={type:\"Sk.promise\",promise:new Promise(function(a){if(null!=Sk.signals&&Sk.signals.addEventListener){function handleSignal(){Sk.signals.removeEventListener(handleSignal),a()}Sk.signals.addEventListener(handleSignal)}else console.warn(\"signal.pause() not supported\"),Sk.misceval.print_(\"signal.pause() not supported\"),a()})},a}),signal:new Sk.builtin.func(function(){throw new Sk.builtin.NotImplementedError(\"signal.signal is not supported.\")})};return a};","src/lib/site.py":"raise NotImplementedError(\"site is not yet implemented in Skulpt\")\n","src/lib/smtpd.py":"raise NotImplementedError(\"smtpd is not yet implemented in Skulpt\")\n","src/lib/smtplib.py":"raise NotImplementedError(\"smtplib is not yet implemented in Skulpt\")\n","src/lib/sndhdr.py":"raise NotImplementedError(\"sndhdr is not yet implemented in Skulpt\")\n","src/lib/socket.py":"raise NotImplementedError(\"socket is not yet implemented in Skulpt\")\n","src/lib/sqlite3/__init__.py":"raise NotImplementedError(\"sqlite3 is not yet implemented in Skulpt\")\n","src/lib/sre.py":"raise NotImplementedError(\"sre is not yet implemented in Skulpt\")\n","src/lib/sre_compile.py":"raise NotImplementedError(\"sre_compile is not yet implemented in Skulpt\")\n","src/lib/sre_constants.py":"raise NotImplementedError(\"sre_constants is not yet implemented in Skulpt\")\n","src/lib/sre_parse.py":"raise NotImplementedError(\"sre_parse is not yet implemented in Skulpt\")\n","src/lib/ssl.py":"raise NotImplementedError(\"ssl is not yet implemented in Skulpt\")\n","src/lib/stat.py":"raise NotImplementedError(\"stat is not yet implemented in Skulpt\")\n","src/lib/statistics.py":"\"\"\"\nBasic statistics module.\n\nThis module provides functions for calculating statistics of data, including\naverages, variance, and standard deviation.\n\nCalculating averages\n--------------------\n\n==================  =============================================\nFunction            Description\n==================  =============================================\nmean                Arithmetic mean (average) of data.\nharmonic_mean       Harmonic mean of data.\nmedian              Median (middle value) of data.\nmedian_low          Low median of data.\nmedian_high         High median of data.\nmedian_grouped      Median, or 50th percentile, of grouped data.\nmode                Mode (most common value) of data.\n==================  =============================================\n\nCalculate the arithmetic mean (\"the average\") of data:\n\n>>> mean([-1.0, 2.5, 3.25, 5.75])\n2.625\n\n\nCalculate the standard median of discrete data:\n\n>>> median([2, 3, 4, 5])\n3.5\n\n\nCalculate the median, or 50th percentile, of data grouped into class intervals\ncentred on the data values provided. E.g. if your data points are rounded to\nthe nearest whole number:\n\n>>> median_grouped([2, 2, 3, 3, 3, 4])  #doctest: +ELLIPSIS\n2.8333333333...\n\nThis should be interpreted in this way: you have two data points in the class\ninterval 1.5-2.5, three data points in the class interval 2.5-3.5, and one in\nthe class interval 3.5-4.5. The median of these data points is 2.8333...\n\n\nCalculating variability or spread\n---------------------------------\n\n==================  =============================================\nFunction            Description\n==================  =============================================\npvariance           Population variance of data.\nvariance            Sample variance of data.\npstdev              Population standard deviation of data.\nstdev               Sample standard deviation of data.\n==================  =============================================\n\nCalculate the standard deviation of sample data:\n\n>>> stdev([2.5, 3.25, 5.5, 11.25, 11.75])  #doctest: +ELLIPSIS\n4.38961843444...\n\nIf you have previously calculated the mean, you can pass it as the optional\nsecond argument to the four \"spread\" functions to avoid recalculating it:\n\n>>> data = [1, 2, 2, 4, 4, 4, 5, 6]\n>>> mu = mean(data)\n>>> pvariance(data, mu)\n2.5\n\n\nExceptions\n----------\n\nA single exception is defined: StatisticsError is a subclass of ValueError.\n\n\"\"\"\n\n__all__ = [ 'StatisticsError',\n            'pstdev', 'pvariance', 'stdev', 'variance',\n            'median',  'median_low', 'median_high', 'median_grouped',\n            'mean', 'mode', 'harmonic_mean',\n          ]\n\nfrom collections import Counter\nfrom math import sqrt\n\ndef mean(data):\n    return sum(data) / len(data)\n\ndef harmonic_mean(data):\n    raise NotImplementedError\n\n\ndef median(data):\n    _data = sorted(data)\n    l = len(_data)\n    if l % 2 == 0:\n        _median = (_data[l//2] + _data[l//2-1]) / 2\n    else:\n        _median = _data[l//2]\n    return _median\n\ndef median_low(data):\n    _data = sorted(data)\n    l = len(_data)\n    if l % 2 == 0:\n        _median = _data[l//2-1]\n    else:\n        _median = _data[l//2]\n    return _median\n\n\ndef median_high(data):\n    _data = sorted(data)\n    l = len(_data)\n    if l % 2 == 0:\n        _median = _data[l//2]\n    else:\n        _median = _data[l//2]\n    return _median\n\n\ndef median_grouped(data):\n    return median(data)\n\ndef mode(data):\n    counter = Counter(data)\n    max_count = max(counter.values())\n    _mode = [k for k,v in counter.items() if v == max_count]\n    if len(_mode) > 1:\n        raise ValueError(\"No Unique Mode, found {} equally common values\".format(len(_mode)))\n    else:\n        return _mode[0]\n\n\ndef variance(data):\n    average=mean(data)\n    _variance=0\n    for d in data:\n        _variance += ((average-d)**2)\n        final_variance=_variance/len(data)\n    return final_variance\n\ndef pvariance(data):\n    average=mean(data)\n    _variance=0\n    for d in data:\n        _variance += ((average-d)**2)\n        final_variance=_variance/len(data)\n    return final_variance\n\ndef variance(data):\n    average=mean(data)\n    _variance=0\n    for d in data:\n        _variance += ((average-d)**2)\n        final_variance=_variance/(len(data) -1)\n    return final_variance\n\ndef pstdev(data):\n    return sqrt(pvariance(data))\n\ndef stdev(data):\n    return sqrt(variance(data))\n\n","src/lib/statvfs.py":"raise NotImplementedError(\"statvfs is not yet implemented in Skulpt\")\n","src/lib/string.js":"var $builtinmodule=function(){var a={};return a.ascii_lowercase=new Sk.builtin.str(\"abcdefghijklmnopqrstuvwxyz\"),a.ascii_uppercase=new Sk.builtin.str(\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"),a.ascii_letters=new Sk.builtin.str(a.ascii_lowercase.v+a.ascii_uppercase.v),a.lowercase=new Sk.builtin.str(\"abcdefghijklmnopqrstuvwxyz\"),a.uppercase=new Sk.builtin.str(\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"),a.letters=new Sk.builtin.str(a.lowercase.v+a.uppercase.v),a.digits=new Sk.builtin.str(\"0123456789\"),a.hexdigits=new Sk.builtin.str(\"0123456789abcdefABCDEF\"),a.octdigits=new Sk.builtin.str(\"01234567\"),a.punctuation=new Sk.builtin.str(\"!\\\"#$%&'()*+,-./:;<=>?@[\\\\]^_`{|}~\"),a.whitespace=new Sk.builtin.str(\"\\t\\n\\x0B\\f\\r \"),a.printable=new Sk.builtin.str(a.digits.v+a.letters.v+a.punctuation.v+\" \\t\\n\\r\\x0B\\f\"),a.split=new Sk.builtin.func(function(a,b,c){return Sk.misceval.callsimArray(Sk.builtin.str.prototype.split,[a,b,c])}),a.capitalize=new Sk.builtin.func(function(a){return Sk.misceval.callsimArray(Sk.builtin.str.prototype.capitalize,[a])}),a.join=new Sk.builtin.func(function(a,b){return void 0===b&&(b=new Sk.builtin.str(\" \")),Sk.misceval.callsimArray(Sk.builtin.str.prototype.join,[b,a])}),a.capwords=new Sk.builtin.func(function(b,c){if(Sk.builtin.pyCheckArgsLen(\"capwords\",arguments.length,1,2),!Sk.builtin.checkString(b))throw new Sk.builtin.TypeError(\"s must be a string\");if(void 0===c&&(c=new Sk.builtin.str(\" \")),!Sk.builtin.checkString(c))throw new Sk.builtin.TypeError(\"sep must be a string\");for(var d=Sk.misceval.callsimArray(a.split,[b,c]).v,e=[],f=0;f<d.length;f++){var g=d[f],h=Sk.misceval.callsimArray(a.capitalize,[g]);e.push(h)}return Sk.misceval.callsimArray(a.join,[new Sk.builtin.list(e),c])}),a};","src/lib/string.py":"raise NotImplementedError(\"string is not yet implemented in Skulpt\")\n","src/lib/stringold.py":"raise NotImplementedError(\"stringold is not yet implemented in Skulpt\")\n","src/lib/stringprep.py":"raise NotImplementedError(\"stringprep is not yet implemented in Skulpt\")\n","src/lib/struct.py":"raise NotImplementedError(\"struct is not yet implemented in Skulpt\")\n","src/lib/subprocess.py":"raise NotImplementedError(\"subprocess is not yet implemented in Skulpt\")\n","src/lib/sunau.py":"raise NotImplementedError(\"sunau is not yet implemented in Skulpt\")\n","src/lib/sunaudio.py":"raise NotImplementedError(\"sunaudio is not yet implemented in Skulpt\")\n","src/lib/symbol.py":"raise NotImplementedError(\"symbol is not yet implemented in Skulpt\")\n","src/lib/symtable.py":"raise NotImplementedError(\"symtable is not yet implemented in Skulpt\")\n","src/lib/tabnanny.py":"raise NotImplementedError(\"tabnanny is not yet implemented in Skulpt\")\n","src/lib/tarfile.py":"raise NotImplementedError(\"tarfile is not yet implemented in Skulpt\")\n","src/lib/telnetlib.py":"raise NotImplementedError(\"telnetlib is not yet implemented in Skulpt\")\n","src/lib/tempfile.py":"raise NotImplementedError(\"tempfile is not yet implemented in Skulpt\")\n","src/lib/test/__init__.py":"__author__ = 'bmiller'\n\ndef testEqual(actual, expected, places=5):\n    if isinstance(expected,int):\n        if actual == expected:\n            print('Pass')\n            return True\n    elif isinstance(expected,float):\n        if abs(actual-expected) < 10**(-places):\n            print('Pass')\n            return True\n    else:\n        if actual == expected:\n            print('Pass')\n            return True\n    print('Test Failed: expected ' + str(expected) + ' but got ' + str(actual))\n    return False\n\ndef testNotEqual(actual, expected):\n    pass\n\n","src/lib/test/decimaltestdata/__init__.py":"raise NotImplementedError(\"decimaltestdata is not yet implemented in Skulpt\")\n","src/lib/test/test_support.py":"\"\"\"Supporting definitions for the Python regression tests.\"\"\"\n\nif __name__ != 'test.test_support':\n    raise ImportError('test_support must be imported from the test package')\n\nimport unittest\n\n\n# def run_unittest(*classes):\n#     \"\"\"Run tests from unittest.TestCase-derived classes.\"\"\"\n#     valid_types = (unittest.TestSuite, unittest.TestCase)\n#     suite = unittest.TestSuite()\n#     for cls in classes:\n#         if isinstance(cls, str):\n#             if cls in sys.modules:\n#                 suite.addTest(unittest.findTestCases(sys.modules[cls]))\n#             else:\n#                 raise ValueError(\"str arguments must be keys in sys.modules\")\n#         elif isinstance(cls, valid_types):\n#             suite.addTest(cls)\n#         else:\n#             suite.addTest(unittest.makeSuite(cls))\n#     _run_suite(suite)\n\ndef run_unittest(*classes):\n    \"\"\"Run tests from unittest.TestCase-derived classes.\"\"\"\n    for cls in classes:\n        print cls\n        if issubclass(cls, unittest.TestCase):\n            cls().main()\n        else:\n            print \"Don't know what to do with \", cls\n","src/lib/textwrap.py":"\"\"\"Text wrapping and filling.\n\"\"\"\n\n# Copyright (C) 1999-2001 Gregory P. Ward.\n# Copyright (C) 2002, 2003 Python Software Foundation.\n# Written by Greg Ward <gward@python.net>\n\nimport re, string\n\n__all__ = ['TextWrapper', 'wrap', 'fill', 'dedent', 'indent', 'shorten']\n\n# Hardcode the recognized whitespace characters to the US-ASCII\n# whitespace characters.  The main reason for doing this is that\n# some Unicode spaces (like \\u00a0) are non-breaking whitespaces.\n_whitespace = '\\t\\n\\x0b\\x0c\\r '\n\nclass TextWrapper:\n    \"\"\"\n    Object for wrapping/filling text.  The public interface consists of\n    the wrap() and fill() methods; the other methods are just there for\n    subclasses to override in order to tweak the default behaviour.\n    If you want to completely replace the main wrapping algorithm,\n    you'll probably have to override _wrap_chunks().\n    Several instance attributes control various aspects of wrapping:\n      width (default: 70)\n        the maximum width of wrapped lines (unless break_long_words\n        is false)\n      initial_indent (default: \"\")\n        string that will be prepended to the first line of wrapped\n        output.  Counts towards the line's width.\n      subsequent_indent (default: \"\")\n        string that will be prepended to all lines save the first\n        of wrapped output; also counts towards each line's width.\n      expand_tabs (default: true)\n        Expand tabs in input text to spaces before further processing.\n        Each tab will become 0 .. 'tabsize' spaces, depending on its position\n        in its line.  If false, each tab is treated as a single character.\n      tabsize (default: 8)\n        Expand tabs in input text to 0 .. 'tabsize' spaces, unless\n        'expand_tabs' is false.\n      replace_whitespace (default: true)\n        Replace all whitespace characters in the input text by spaces\n        after tab expansion.  Note that if expand_tabs is false and\n        replace_whitespace is true, every tab will be converted to a\n        single space!\n      fix_sentence_endings (default: false)\n        Ensure that sentence-ending punctuation is always followed\n        by two spaces.  Off by default because the algorithm is\n        (unavoidably) imperfect.\n      break_long_words (default: true)\n        Break words longer than 'width'.  If false, those words will not\n        be broken, and some lines might be longer than 'width'.\n      break_on_hyphens (default: true)\n        Allow breaking hyphenated words. If true, wrapping will occur\n        preferably on whitespaces and right after hyphens part of\n        compound words.\n      drop_whitespace (default: true)\n        Drop leading and trailing whitespace from lines.\n      max_lines (default: None)\n        Truncate wrapped lines.\n      placeholder (default: ' [...]')\n        Append to the last line of truncated text.\n    \"\"\"\n\n    unicode_whitespace_trans = {}\n    # uspace = ord(' ')\n    uspace = ' '\n    for x in _whitespace:\n        # unicode_whitespace_trans[ord(x)] = uspace\n        unicode_whitespace_trans[x] = uspace\n\n    # This funky little regex is just the trick for splitting\n    # text up into word-wrappable chunks.  E.g.\n    #   \"Hello there -- you goof-ball, use the -b option!\"\n    # splits into\n    #   Hello/ /there/ /--/ /you/ /goof-/ball,/ /use/ /the/ /-b/ /option!\n    # (after stripping out empty strings).\n    wordsep_re = re.compile(\n        r'(\\s+|'                                  # any whitespace\n        r'[^\\s\\w]*\\w+[^0-9\\W]-(?=\\w+[^0-9\\W]))')  # hyphenated words\n    em_dash = re.compile(r'(\\s+|'                                  # any whitespace\n                         r'[^\\s\\w]*\\w+[^0-9\\W]-(?=\\w+[^0-9\\W])|'   # hyphenated words\n                         r'(?!^)-{2,}(?=\\w))')                     # em-dash\n\n                         \n    # This less funky little regex just split on recognized spaces. E.g.\n    #   \"Hello there -- you goof-ball, use the -b option!\"\n    # splits into\n    #   Hello/ /there/ /--/ /you/ /goof-ball,/ /use/ /the/ /-b/ /option!/\n    wordsep_simple_re = re.compile(r'(\\s+)')\n\n\n    # XXX this is not locale- or charset-aware -- string.lowercase\n    # is US-ASCII only (and therefore English-only)\n    sentence_end_re = re.compile(r'[a-z]'             # lowercase letter\n                                 r'[\\.\\!\\?]'          # sentence-ending punct.\n                                 r'[\\\"\\']?'           # optional end-of-quote\n                                 r'\\Z')               # end of chunk\n    sentence_end_re = r'[a-z][\\.\\!\\?][\\\"\\']?'\n\n    def __init__(self,\n                 width=70,\n                 initial_indent=\"\",\n                 subsequent_indent=\"\",\n                 expand_tabs=True,\n                 replace_whitespace=True,\n                 fix_sentence_endings=False,\n                 break_long_words=True,\n                 drop_whitespace=True,\n                 break_on_hyphens=True,\n                 tabsize=8,\n                 max_lines=None,\n                 placeholder=' [...]'):\n        self.width = width\n        self.initial_indent = initial_indent\n        self.subsequent_indent = subsequent_indent\n        self.expand_tabs = expand_tabs\n        self.replace_whitespace = replace_whitespace\n        self.fix_sentence_endings = fix_sentence_endings\n        self.break_long_words = break_long_words\n        self.drop_whitespace = drop_whitespace\n        self.break_on_hyphens = break_on_hyphens\n        self.tabsize = tabsize\n        self.max_lines = max_lines\n        self.placeholder = placeholder\n\n\n    # -- Private methods -----------------------------------------------\n    # (possibly useful for subclasses to override)\n\n    def _munge_whitespace(self, text):\n        \"\"\"_munge_whitespace(text : string) -> string\n        Munge whitespace in text: expand tabs and convert all other\n        whitespace characters to spaces.  Eg. \" foo\\\\tbar\\\\n\\\\nbaz\"\n        becomes \" foo    bar  baz\".\n        \"\"\"\n        if self.expand_tabs:\n            text = text.expandtabs(self.tabsize)\n        if self.replace_whitespace:\n            for key, val in self.unicode_whitespace_trans.items():\n                text = text.replace(key, val)\n        return text\n\n\n    def _split(self, text):\n        \"\"\"_split(text : string) -> [string]\n        Split the text to wrap into indivisible chunks.  Chunks are\n        not quite the same as words; see _wrap_chunks() for full\n        details.  As an example, the text\n          Look, goof-ball -- use the -b option!\n        breaks into the following chunks:\n          'Look,', ' ', 'goof-', 'ball', ' ', '--', ' ',\n          'use', ' ', 'the', ' ', '-b', ' ', 'option!'\n        if break_on_hyphens is True, or in:\n          'Look,', ' ', 'goof-ball', ' ', '--', ' ',\n          'use', ' ', 'the', ' ', '-b', ' ', option!'\n        otherwise.\n        \"\"\"\n        if self.break_on_hyphens is True:\n            chunks = self.wordsep_re.split(text)\n            if \"--\" in text:\n                chunks = [item \n                            for sublist in [self.em_dash.split(chunk) for chunk in chunks] \n                                for item in sublist]\n        else:\n            chunks = self.wordsep_simple_re.split(text)\n        chunks = [c for c in chunks if c]\n        return chunks\n\n    def _fix_sentence_endings(self, chunks):\n        \"\"\"_fix_sentence_endings(chunks : [string])\n        Correct for sentence endings buried in 'chunks'.  Eg. when the\n        original text contains \"... foo.\\\\nBar ...\", munge_whitespace()\n        and split() will convert that to [..., \"foo.\", \" \", \"Bar\", ...]\n        which has one too few spaces; this method simply changes the one\n        space to two.\n        \"\"\"\n        i = 0\n        # patsearch = self.sentence_end_re.search\n        while i < len(chunks)-1:\n            if chunks[i+1] == \" \" and re.search(self.sentence_end_re, chunks[i]) and chunks[i][-1] in \".!?\\\"\\'\":\n                chunks[i+1] = \"  \"\n                i += 2\n            else:\n                i += 1\n\n    def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):\n        \"\"\"_handle_long_word(chunks : [string],\n                             cur_line : [string],\n                             cur_len : int, width : int)\n        Handle a chunk of text (most likely a word, not whitespace) that\n        is too long to fit in any line.\n        \"\"\"\n        # Figure out when indent is larger than the specified width, and make\n        # sure at least one character is stripped off on every pass\n        if width < 1:\n            space_left = 1\n        else:\n            space_left = width - cur_len\n\n        # If we're allowed to break long words, then do so: put as much\n        # of the next chunk onto the current line as will fit.\n        if self.break_long_words:\n            cur_line.append(reversed_chunks[-1][:space_left])\n            reversed_chunks[-1] = reversed_chunks[-1][space_left:]\n\n        # Otherwise, we have to preserve the long word intact.  Only add\n        # it to the current line if there's nothing already there --\n        # that minimizes how much we violate the width constraint.\n        elif not cur_line:\n            cur_line.append(reversed_chunks.pop())\n\n        # If we're not allowed to break long words, and there's already\n        # text on the current line, do nothing.  Next time through the\n        # main loop of _wrap_chunks(), we'll wind up here again, but\n        # cur_len will be zero, so the next line will be entirely\n        # devoted to the long word that we can't handle right now.\n\n    def _wrap_chunks(self, chunks):\n        \"\"\"_wrap_chunks(chunks : [string]) -> [string]\n        Wrap a sequence of text chunks and return a list of lines of\n        length 'self.width' or less.  (If 'break_long_words' is false,\n        some lines may be longer than this.)  Chunks correspond roughly\n        to words and the whitespace between them: each chunk is\n        indivisible (modulo 'break_long_words'), but a line break can\n        come between any two chunks.  Chunks should not have internal\n        whitespace; ie. a chunk is either all whitespace or a \"word\".\n        Whitespace chunks will be removed from the beginning and end of\n        lines, but apart from that whitespace is preserved.\n        \"\"\"\n        lines = []\n        if self.width <= 0:\n            raise ValueError(\"invalid width %r (must be > 0)\" % self.width)\n        if self.max_lines is not None:\n            if self.max_lines > 1:\n                indent = self.subsequent_indent\n            else:\n                indent = self.initial_indent\n            if len(indent) + len(self.placeholder.lstrip()) > self.width:\n                raise ValueError(\"placeholder too large for max width\")\n\n        # Arrange in reverse order so items can be efficiently popped\n        # from a stack of chucks.\n        chunks.reverse()\n\n        while chunks:\n\n            # Start the list of chunks that will make up the current line.\n            # cur_len is just the length of all the chunks in cur_line.\n            cur_line = []\n            cur_len = 0\n\n            # Figure out which static string will prefix this line.\n            if lines:\n                indent = self.subsequent_indent\n            else:\n                indent = self.initial_indent\n\n            # Maximum width for this line.\n            width = self.width - len(indent)\n\n            # First chunk on line is whitespace -- drop it, unless this\n            # is the very beginning of the text (ie. no lines started yet).\n            if self.drop_whitespace and chunks[-1].strip() == '' and lines:\n                del chunks[-1]\n\n            while chunks:\n                l = len(chunks[-1])\n\n                # Can at least squeeze this chunk onto the current line.\n                if cur_len + l <= width:\n                    cur_line.append(chunks.pop())\n                    cur_len += l\n\n                # Nope, this line is full.\n                else:\n                    break\n\n            # The current line is full, and the next chunk is too big to\n            # fit on *any* line (not just this one).\n            if chunks and len(chunks[-1]) > width:\n                self._handle_long_word(chunks, cur_line, cur_len, width)\n                cur_len = sum(map(len, cur_line))\n\n            # If the last chunk on this line is all whitespace, drop it.\n            if self.drop_whitespace and cur_line and cur_line[-1].strip() == '':\n                cur_len -= len(cur_line[-1])\n                del cur_line[-1]\n\n            if cur_line:\n                if (self.max_lines is None or\n                    len(lines) + 1 < self.max_lines or\n                    (not chunks or\n                     self.drop_whitespace and\n                     len(chunks) == 1 and\n                     not chunks[0].strip()) and cur_len <= width):\n                    # Convert current line back to a string and store it in\n                    # list of all lines (return value).\n                    lines.append(indent + ''.join(cur_line))\n                else:\n                    while cur_line:\n                        if (cur_line[-1].strip() and\n                            cur_len + len(self.placeholder) <= width):\n                            cur_line.append(self.placeholder)\n                            lines.append(indent + ''.join(cur_line))\n                            break\n                        cur_len -= len(cur_line[-1])\n                        del cur_line[-1]\n                    else:\n                        if lines:\n                            prev_line = lines[-1].rstrip()\n                            if (len(prev_line) + len(self.placeholder) <=\n                                    self.width):\n                                lines[-1] = prev_line + self.placeholder\n                                break\n                        lines.append(indent + self.placeholder.lstrip())\n                    break\n\n        return lines\n\n    def _split_chunks(self, text):\n        text = self._munge_whitespace(text)\n        return self._split(text)\n\n    # -- Public interface ----------------------------------------------\n\n    def wrap(self, text):\n        \"\"\"wrap(text : string) -> [string]\n        Reformat the single paragraph in 'text' so it fits in lines of\n        no more than 'self.width' columns, and return a list of wrapped\n        lines.  Tabs in 'text' are expanded with string.expandtabs(),\n        and all other whitespace characters (including newline) are\n        converted to space.\n        \"\"\"\n        chunks = self._split_chunks(text)\n        if self.fix_sentence_endings:\n            self._fix_sentence_endings(chunks)\n        return self._wrap_chunks(chunks)\n\n    def fill(self, text):\n        \"\"\"fill(text : string) -> string\n        Reformat the single paragraph in 'text' to fit in lines of no\n        more than 'self.width' columns, and return a new string\n        containing the entire wrapped paragraph.\n        \"\"\"\n        return \"\\n\".join(self.wrap(text))\n\n\n# -- Convenience interface ---------------------------------------------\n\ndef wrap(text, width=70, **kwargs):\n    \"\"\"Wrap a single paragraph of text, returning a list of wrapped lines.\n    Reformat the single paragraph in 'text' so it fits in lines of no\n    more than 'width' columns, and return a list of wrapped lines.  By\n    default, tabs in 'text' are expanded with string.expandtabs(), and\n    all other whitespace characters (including newline) are converted to\n    space.  See TextWrapper class for available keyword args to customize\n    wrapping behaviour.\n    \"\"\"\n    w = TextWrapper(width=width, **kwargs)\n    return w.wrap(text)\n\ndef fill(text, width=70, **kwargs):\n    \"\"\"Fill a single paragraph of text, returning a new string.\n    Reformat the single paragraph in 'text' to fit in lines of no more\n    than 'width' columns, and return a new string containing the entire\n    wrapped paragraph.  As with wrap(), tabs are expanded and other\n    whitespace characters converted to space.  See TextWrapper class for\n    available keyword args to customize wrapping behaviour.\n    \"\"\"\n    w = TextWrapper(width=width, **kwargs)\n    return w.fill(text)\n\ndef shorten(text, width, **kwargs):\n    \"\"\"Collapse and truncate the given text to fit in the given width.\n    The text first has its whitespace collapsed.  If it then fits in\n    the *width*, it is returned as is.  Otherwise, as many words\n    as possible are joined and then the placeholder is appended::\n        >>> textwrap.shorten(\"Hello  world!\", width=12)\n        'Hello world!'\n        >>> textwrap.shorten(\"Hello  world!\", width=11)\n        'Hello [...]'\n    \"\"\"\n    w = TextWrapper(width=width, max_lines=1, **kwargs)\n    return w.fill(' '.join(text.strip().split()))\n\n\n# -- Loosely related functionality -------------------------------------\n\n# _whitespace_only_re = re.compile('^[ \\t]+$', re.MULTILINE)\n# _leading_whitespace_re = re.compile('(^[ \\t]*)(?:[^ \\t\\n])', re.MULTILINE)\n\ndef dedent(text):\n    \"\"\"Remove any common leading whitespace from every line in `text`.\n    This can be used to make triple-quoted strings line up with the left\n    edge of the display, while still presenting them in the source code\n    in indented form.\n    Note that tabs and spaces are both treated as whitespace, but they\n    are not equal: the lines \"  hello\" and \"\\\\thello\" are\n    considered to have no common leading whitespace.\n    Entirely blank lines are normalized to a newline character.\n    \"\"\"\n    # Look for the longest leading string of spaces and tabs common to\n    # all lines.\n    margin = None\n\n    indents = re.findall(r'(^[ \\t]*)(?:[^ \\t\\n])',text, re.MULTILINE)\n    for indent in indents:\n        if margin is None:\n            margin = indent\n\n        # Current line more deeply indented than previous winner:\n        # no change (previous winner is still on top).\n        elif indent.startswith(margin):\n            pass\n\n        # Current line consistent with and no deeper than previous winner:\n        # it's the new winner.\n        elif margin.startswith(indent):\n            margin = indent\n\n        # Find the largest common whitespace between current line and previous\n        # winner.\n        else:\n            for i, (x, y) in enumerate(zip(margin, indent)):\n                if x != y:\n                    margin = margin[:i]\n                    break\n    # sanity check (testing/debugging only)\n    if 0 and margin:\n        for line in text.split(\"\\n\"):\n            assert not line or line.startswith(margin), \\\n                   \"line = %r, margin = %r\" % (line, margin)\n\n    if margin:\n        lines = [line[len(margin):] \n                    if line.strip()\n                        else line.strip() \n                            for line in text.split(\"\\n\")]\n        text = \"\\n\".join(lines)\n    return text\n\n\ndef indent(text, prefix, predicate=None):\n    \"\"\"Adds 'prefix' to the beginning of selected lines in 'text'.\n    If 'predicate' is provided, 'prefix' will only be added to the lines\n    where 'predicate(line)' is True. If 'predicate' is not provided,\n    it will default to adding 'prefix' to all non-empty lines that do not\n    consist solely of whitespace characters.\n    \"\"\"\n    if predicate is None:\n        def predicate(line):\n            return line.strip()\n\n    def prefixed_lines():\n        for line in text.splitlines(True):\n            yield (prefix + line if predicate(line) else line)\n    return ''.join(prefixed_lines())\n\n\nif __name__ == \"__main__\":\n    #print dedent(\"\\tfoo\\n\\tbar\")\n    #print dedent(\"  \\thello there\\n  \\t  how are you?\")\n    print(dedent(\"Hello there.\\n  This is indented.\"))","src/lib/this.py":"\nprint(\"\"\"\nThe Zen of Python, by Tim Peters\n\nBeautiful is better than ugly.\nExplicit is better than implicit.\nSimple is better than complex.\nComplex is better than complicated.\nFlat is better than nested.\nSparse is better than dense.\nReadability counts.\nSpecial cases aren't special enough to break the rules.\nAlthough practicality beats purity.\nErrors should never pass silently.\nUnless explicitly silenced.\nIn the face of ambiguity, refuse the temptation to guess.\nThere should be one-- and preferably only one --obvious way to do it.\nAlthough that way may not be obvious at first unless you're Dutch.\nNow is better than never.\nAlthough never is often better than *right* now.\nIf the implementation is hard to explain, it's a bad idea.\nIf the implementation is easy to explain, it may be a good idea.\nNamespaces are one honking great idea -- let's do more of those!\n\"\"\"\n)\n","src/lib/threading.py":"raise NotImplementedError(\"threading is not yet implemented in Skulpt\")\n","src/lib/time.js":"var $builtinmodule=function(){function check_struct_time(a){if(!(a instanceof b))throw new Sk.builtin.TypeError(\"Required argument 'struct_time' must be of type: 'struct_time'\");var c,d=a.v.length,e=a.v;for(c=0;c<d;++c)if(!Sk.builtin.checkInt(e[c]))throw new Sk.builtin.TypeError(\"struct_time may only contain integers\");return!0}function padLeft(a,b,d){var c=a.toString();return Array(b-c.length+1).join(d||\" \")+c}function isLeapYear(a){return 0==(3&a)&&(0!=a%100||0==a%400)}function getDayOfYear(a,b){b=b||!1;var c=b?a.getUTCMonth():a.getMonth(),d=b?a.getUTCDate():a.getDate(),e=[0,31,59,90,120,151,181,212,243,273,304,334][c]+d;return 1<c&&isLeapYear(b?a.getUTCFullYear():a.getFullYear())&&e++,e}function stdTimezoneOffset(){var a=Math.max,b=new Date(2002,0,1),c=new Date(2002,6,1);return a(b.getTimezoneOffset(),c.getTimezoneOffset())}function dst(a){return a.getTimezoneOffset()<stdTimezoneOffset()}function timeZoneName(a){var b,c=/\\((.*)\\)/.exec(a.toString());if(null!=this.navigator&&(b=this.navigator.userLanguage||this.navigator.language),c&&1<c.length)return c[1];if(void 0===b)return null;try{var d=a.toLocaleString(b,{timeZoneName:\"short\"});return c=d.split(\" \"),c[c.length-1]}catch(a){return null}}function date_to_struct_time(a,c){return c=c||!1,new b([Sk.builtin.assk$(c?a.getUTCFullYear():a.getFullYear()),Sk.builtin.assk$((c?a.getUTCMonth():a.getMonth())+1),Sk.builtin.assk$(c?a.getUTCDate():a.getDate()),Sk.builtin.assk$(c?a.getUTCHours():a.getHours()),Sk.builtin.assk$(c?a.getUTCMinutes():a.getMinutes()),Sk.builtin.assk$(c?a.getUTCSeconds():a.getSeconds()),Sk.builtin.assk$(((c?a.getUTCDay():a.getDay())+6)%7),Sk.builtin.assk$(getDayOfYear(a,c)),Sk.builtin.assk$(c?0:dst(a)?1:0)])}function localtime_f(a){Sk.builtin.pyCheckArgsLen(\"localtime\",arguments.length,0,1);var b=new Date;if(a){Sk.builtin.pyCheckType(\"secs\",\"number\",Sk.builtin.checkNumber(a));var c=Sk.builtin.asnum$(a);b.setTime(1e3*c)}return date_to_struct_time(b)}function asctime_f(a){if(!a||Sk.builtin.checkNone(a)?a=localtime_f():!(a instanceof b)&&(a=new b(a)),a instanceof Sk.builtin.tuple&&9==a.v.length){var e=[d[Sk.builtin.asnum$(a.v[6])],c[Sk.builtin.asnum$(a.v[1])-1],padLeft(Sk.builtin.asnum$(a.v[2]).toString(),2,\"0\"),padLeft(Sk.builtin.asnum$(a.v[3]).toString(),2,\"0\")+\":\"+padLeft(Sk.builtin.asnum$(a.v[4]).toString(),2,\"0\")+\":\"+padLeft(Sk.builtin.asnum$(a.v[5]).toString(),2,\"0\"),padLeft(Sk.builtin.asnum$(a.v[0]).toString(),4,\"0\")];return new Sk.builtin.str(e.join(\" \"))}}function mktime_f(a){if(a instanceof Sk.builtin.tuple&&9==a.v.length){var b=new Date(Sk.builtin.asnum$(a.v[0]),Sk.builtin.asnum$(a.v[1])-1,Sk.builtin.asnum$(a.v[2]),Sk.builtin.asnum$(a.v[3]),Sk.builtin.asnum$(a.v[4]),Sk.builtin.asnum$(a.v[5]));return Sk.builtin.assk$(b.getTime()/1e3,void 0)}throw new Sk.builtin.TypeError(\"mktime() requires a struct_time or 9-tuple\")}var a={__file__:\"/src/lib/time/__init__.js\",__package__:new Sk.builtin.str(\"\")},b=Sk.builtin.make_structseq(\"time\",\"struct_time\",{tm_year:\"year, for example, 1993\",tm_mon:\"month of year, range [1, 12]\",tm_mday:\"day of month, range [1, 31]\",tm_hour:\"hours, range [0, 23]\",tm_min:\"minutes, range [0, 59]\",tm_sec:\"seconds, range [0, 61]\",tm_wday:\"day of week, range [0, 6], Monday is 0\",tm_yday:\"day of year, range [1, 366]\",tm_isdst:\"1 if summer time is in effect, 0 if not, and -1 if unknown\"});a.struct_time=b,a.time=new Sk.builtin.func(function(){Sk.builtin.pyCheckArgsLen(\"time\",arguments.length,0,0);var a=Date.now();return this.performance&&this.performance.now&&(a+=performance.now()%1),Sk.builtin.assk$(a/1e3,void 0)}),a.sleep=new Sk.builtin.func(function(a){return Sk.builtin.pyCheckArgsLen(\"sleep\",arguments.length,1,1),Sk.builtin.pyCheckType(\"delay\",\"float\",Sk.builtin.checkNumber(a)),new Sk.misceval.promiseToSuspension(new Promise(function(b){Sk.setTimeout(function(){b(Sk.builtin.none.none$)},1e3*Sk.ffi.remapToJs(a))}))}),a.localtime=new Sk.builtin.func(localtime_f),a.gmtime=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen(\"localtime\",arguments.length,0,1);var b=new Date;if(a){Sk.builtin.pyCheckType(\"secs\",\"number\",Sk.builtin.checkNumber(a));var c=Sk.builtin.asnum$(a);b.setTime(1e3*c)}return date_to_struct_time(b,!0)});var c=[\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"],d=[\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\",\"Sun\"];return a.asctime=new Sk.builtin.func(asctime_f),a.ctime=new Sk.builtin.func(function(a){return asctime_f(localtime_f(a))}),a.mktime=new Sk.builtin.func(mktime_f),a.timezone=new Sk.builtin.int_(60*stdTimezoneOffset()),a.altzone=new Sk.builtin.int_(60*function altTimezoneOffset(){var a=Math.min,b=new Date(2002,0,1),c=new Date(2002,6,1);return a(b.getTimezoneOffset(),c.getTimezoneOffset())}()),a.daylight=new Sk.builtin.int_(dst(new Date)?1:0),a.tzname=new Sk.builtin.tuple(function timeZoneNames(){var a=new Date(2002,0,1),b=new Date(2002,6,1);return dst(a)?[new Sk.builtin.str(timeZoneName(b)),new Sk.builtin.str(timeZoneName(a))]:[new Sk.builtin.str(timeZoneName(a)),new Sk.builtin.str(timeZoneName(b))]}()),a.accept2dyear=Sk.builtin.assk$(1),a.clock=new Sk.builtin.func(function(){var a=0;return a=this.performance&&this.performance.now?performance.now()/1e3:new Date().getTime()/1e3,new Sk.builtin.float_(a)}),a.strftime=new Sk.builtin.func(function strftime_f(a,c){var d;if(Sk.builtin.pyCheckArgsLen(\"strftime\",arguments.length,1,2),!Sk.builtin.checkString(a))throw new Sk.builtin.TypeError(\"format must be a string\");return c?!(c instanceof b)&&(c=new b(c)):c=localtime_f(),check_struct_time(c),d=Sk.ffi.remapToJs(a),Sk.ffi.remapToPy(strftime(d,new Date(1e3*mktime_f(c).v)))}),a.tzset=new Sk.builtin.func(function tzset_f(){throw new Sk.builtin.NotImplementedError(\"time.tzset() is not yet implemented\")}),a.strptime=new Sk.builtin.func(function strptime_f(a,b){Sk.builtin.pyCheckArgsLen(\"strptime\",arguments.length,1,2),Sk.builtin.pyCheckType(\"string\",\"string\",Sk.builtin.checkString(a)),void 0===b?b=new Sk.builtin.str(\"%a %b %d %H:%M:%S %Y\"):Sk.builtin.pyCheckType(\"format\",\"string\",Sk.builtin.checkString(b));let c=date_to_struct_time(strptime(Sk.ffi.remapToJs(a),Sk.ffi.remapToJs(b),!0));return c.v[8]=new Sk.builtin.int_(-1),c}),a};","src/lib/timeit.py":"raise NotImplementedError(\"timeit is not yet implemented in Skulpt\")\n","src/lib/toaiff.py":"raise NotImplementedError(\"toaiff is not yet implemented in Skulpt\")\n","src/lib/token.js":"var $builtinmodule=function(){var a={__file__:\"/src/lib/token.py\"};const b=[];for(token in Sk.token.tok_name){const c=Sk.token.tok_name[token].slice(2),d=parseInt(token,10);b.push(Sk.ffi.remapToPy(d)),b.push(Sk.ffi.remapToPy(c)),a[c]=Sk.ffi.remapToPy(d)}return a.tok_name=new Sk.builtin.dict(b),a.ISTERMINAL=new Sk.builtin.func(function(a){return Sk.builtin.pyCheckArgsLen(\"ISTERMINAL\",arguments.length,1,1),Sk.token.ISTERMINAL(Sk.ffi.remapToJs(a))}),a.ISNONTERMINAL=new Sk.builtin.func(function(a){return Sk.builtin.pyCheckArgsLen(\"ISNONTERMINAL\",arguments.length,1,1),Sk.token.ISNONTERMINAL(Sk.ffi.remapToJs(a))}),a.ISEOF=new Sk.builtin.func(function(a){return Sk.builtin.pyCheckArgsLen(\"ISEOF\",arguments.length,1,1),Sk.token.ISEOF(Sk.ffi.remapToJs(a))}),a};","src/lib/tokenize.js":"var $builtinmodule=function(){var a={tokenize:new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen(\"tokenize\",1,1),Sk.builtin.checkFunction(a);const b=[];return Sk._tokenize(\"<stdin>\",function jsReadline(){const b=Sk.misceval.callsimArray(a);return Sk.ffi.remapToJs(b)},\"UTF-8\",function receiveToken(a){b.push(new Sk.builtin.tuple([Sk.ffi.remapToPy(a.type),Sk.ffi.remapToPy(a.string),new Sk.builtin.tuple([Sk.ffi.remapToPy(a.start[0]),Sk.ffi.remapToPy(a.start[1])]),new Sk.builtin.tuple([Sk.ffi.remapToPy(a.end[0]),Sk.ffi.remapToPy(a.end[1])]),Sk.ffi.remapToPy(a.line)]))}),new Sk.builtin.list(b)})};return a};","src/lib/trace.py":"raise NotImplementedError(\"trace is not yet implemented in Skulpt\")\n","src/lib/traceback.py":"raise NotImplementedError(\"traceback is not yet implemented in Skulpt\")\n","src/lib/tty.py":"raise NotImplementedError(\"tty is not yet implemented in Skulpt\")\n","src/lib/turtle.js":"var $builtinmodule=function(){\"use strict\";var e=function getConfiguredTarget(){var e,t;for(e=Sk.TurtleGraphics&&Sk.TurtleGraphics.target||\"turtle\",t=\"string\"==typeof e?document.getElementById(e):e;t.firstChild;)t.removeChild(t.firstChild);return t}();return e.turtleInstance?e.turtleInstance.reset():e.turtleInstance=function generateTurtleModule(e){var t=Math.round,r=Math.max,n=Math.sqrt,a=Math.min,s=Math.abs,o=Math.PI,d=Math.atan2,_=Math.sin,c=Math.cos;function getAsset(e){var t=g.assets,r=\"function\"==typeof t?t(e):t[e];return\"string\"==typeof r?new Promise(function(t,n){var a=new Image;a.onload=function(){g.assets[e]=this,t(a)},a.onerror=function(){n(new Error(\"Missing asset: \"+r))},a.src=r}):new InstantPromise(void 0,r)}function InstantPromise(e,t){this.lastResult=t,this.lastError=e}function FrameManager(){this.reset()}function getFrameManager(){return A||(A=new FrameManager),A}function MouseHandler(){var t=this;for(var r in this._target=getTarget(),this._managers={},this._handlers={mousedown:function(r){t.onEvent(\"mousedown\",r)},mouseup:function(r){t.onEvent(\"mouseup\",r)},mousemove:function(r){t.onEvent(\"mousemove\",r)}},this._handlers)this._target.addEventListener(r,this._handlers[r])}function EventManager(e,t){this._type=e,this._target=t,this._handlers=void 0,getMouseHandler().addManager(e,this)}function Turtle(e){if(getFrameManager().addTurtle(this),this._screen=getScreen(),this._managers={},this._shape=e.v,!v.hasOwnProperty(this._shape))throw new Sk.builtin.ValueError(\"Shape:'\"+this._shape+\"' not in default shape, please check shape again!\");this.reset()}function Screen(){var e,t;this._frames=1,this._delay=void 0,this._bgcolor=\"none\",this._mode=\"standard\",this._managers={},this._keyLogger={},e=(g.worldWidth||g.width||getWidth())/2,t=(g.worldHeight||g.height||getHeight())/2,this.setUpWorld(-e,-t,e,t)}function ensureAnonymous(){return f||(f=Sk.misceval.callsimArray(y.Turtle)),f.instance}function getTarget(){return e}function getScreen(){return p||(p=new Screen),p}function getMouseHandler(){return h||(h=new MouseHandler),h}function getWidth(){return 0|(p&&p._width||g.width||getTarget().clientWidth||T.width)}function getHeight(){return 0|(p&&p._height||g.height||getTarget().clientHeight||T.height)}function createLayer(e,t){var r,n=document.createElement(\"canvas\"),a=getWidth(),s=getHeight(),l=getTarget().firstChild?-s+\"px\":\"0\";return n.width=a,n.height=s,n.style.position=\"relative\",n.style.display=\"block\",n.style.setProperty(\"margin-top\",l),n.style.setProperty(\"z-index\",e),t&&(n.style.display=\"none\"),getTarget().appendChild(n),r=n.getContext(\"2d\"),r.lineCap=\"round\",r.lineJoin=\"round\",applyWorld(getScreen(),r),r}function cancelAnimationFrame(){u&&((window.cancelAnimationFrame||window.mozCancelAnimationFrame)(u),u=void 0),m&&(window.clearTimeout(m),m=void 0)}function applyWorld(e,t){var r=e.llx,n=e.lly,a=e.urx,s=e.ury,l=e.xScale,i=e.yScale;t&&(clearLayer(t),t.restore(),t.save(),t.scale(1/l,1/i),t.translate(-r,-s))}function pushUndo(e){var t,r,n;if(g.allowUndo&&e._bufferSize){for(e._undoBuffer||(e._undoBuffer=[]);e._undoBuffer.length>e._bufferSize;)e._undoBuffer.shift();for(r={},t=[\"x\",\"y\",\"angle\",\"radians\",\"color\",\"fill\",\"down\",\"filling\",\"shown\",\"shape\",\"size\"],n=0;n<t.length;n++)r[t[n]]=e[\"_\"+t[n]];return e._undoBuffer.push(r),e.addUpdate(function(){r.fillBuffer=this.fillBuffer?this.fillBuffer.slice():void 0,e._paper&&e._paper.canvas&&(r.image=e._paper.canvas.toDataURL())},!1)}}function popUndo(e){var t;if(e._bufferSize&&e._undoBuffer&&(t=e._undoBuffer.pop(),!!t)){for(var r in t)\"image\"!=r&&\"fillBuffer\"!==r&&(e[\"_\"+r]=t[r]);return e.addUpdate(function(){var e;t.image&&(L.src=t.image,e=L),clearLayer(this.context(),!1,L),delete t.image},!0,t)}}function removeLayer(e){e&&e.canvas&&e.canvas.parentNode&&e.canvas.parentNode.removeChild(e.canvas)}function clearLayer(e,t,r){e&&(e.save(),e.setTransform(1,0,0,1,0,0),t?(e.fillStyle=t,e.fillRect(0,0,e.canvas.width,e.canvas.height)):e.clearRect(0,0,e.canvas.width,e.canvas.height),r&&e.drawImage(r,0,0),e.restore())}function drawTurtle(e,t){var r,n,a,s=v[e.shape],l=getScreen(),u=getWidth(),m=getHeight(),p=l.xScale,g=l.yScale;if(t){if(r=c(e.radians)/p,n=_(e.radians)/g,a=d(n,r)-o/2,t.save(),t.translate(e.x,e.y),t.scale(p,g),s.nodeName){var f=s.naturalWidth,h=s.naturalHeight;t.drawImage(s,0,0,f,h,-f/2,-h/2,f,h)}else{t.rotate(a),t.beginPath(),t.lineWidth=1,t.strokeStyle=e.color,t.fillStyle=e.fill,t.moveTo(-s[0][0],s[0][1]);for(var $=1;$<s.length;$++)t.lineTo(-s[$][0],s[$][1]);t.closePath(),t.fill(),t.stroke()}t.restore()}}function drawDot(e,t){var r=this.context(),n=getScreen(),l=n.xScale,i=n.yScale;r&&(r.beginPath(),r.moveTo(this.x,this.y),e*=a(s(l),s(i)),r.arc(this.x,this.y,e/2,0,Turtle.RADIANS),r.closePath(),r.fillStyle=t||this.color,r.fill())}function measureText(e,t){return t&&(S.font=t),S.measureText(e).width}function drawText(e,t,r){var n=this.context();n&&(n.save(),r&&(n.font=r),t&&t.match(/^(left|right|center)$/)&&(n.textAlign=t),n.scale(1,-1),n.fillStyle=this.fill,n.fillText(e,this.x,-this.y),n.restore())}function drawLine(e,t){var r=this.context();r&&(t&&(r.beginPath(),r.moveTo(this.x,this.y)),r.lineWidth=this.size*getScreen().lineScale,r.strokeStyle=this.color,r.lineTo(e.x,e.y),r.stroke())}function drawFill(){var e,t=this.context(),r=this.fillBuffer;if(t&&r&&r.length){for(t.save(),t.beginPath(),t.moveTo(r[0].x,r[0].y),e=1;e<r.length;e++)t.lineTo(r[e].x,r[e].y);for(t.closePath(),t.fillStyle=this.fill,t.fill(),e=1;e<r.length;e++)r[e].stroke&&(t.beginPath(),t.moveTo(r[e-1].x,r[e-1].y),t.lineWidth=r[e].size*getScreen().lineScale,t.strokeStyle=r[e].color,t.lineTo(r[e].x,r[e].y),t.stroke());t.restore()}}function partialTranslate(e,t,r,n,a){return function(){return e.addUpdate(function(e){this.down&&drawLine.call(this,e,n)},a,{x:t,y:r},n)}}function translate(e,a,l,o,d,_,c){var u,m=e._computed_speed,p=getScreen(),g=s(p.xScale),f=s(p.yScale),h=a,$=l,w=n(o*o*g+d*d*f),b=m?t(r(1,w/m)):1,v=getFrameManager().willRenderNext()?Promise.resolve():new InstantPromise;for(e.addUpdate(function(){this.filling&&this.fillBuffer.push({x:this.x,y:this.y,stroke:this.down,color:this.color,size:this.size})},!1),u=0;u<b;u++)h=a+o/b*(u+1),$=l+d/b*(u+1),v=v.then(partialTranslate(e,h,$,_,m||!c)),_=!1;return v.then(function(){return[a+o,l+d]})}function partialRotate(e,t,r,n){return function(){return e.addUpdate(void 0,n,{angle:t,radians:r})}}function rotate(e,n,a,l){var o,d=e._computed_speed,_=360*(a/e._fullCircle),c=d?t(r(1,s(_)/d)):1,u={},m=getFrameManager().willRenderNext()?Promise.resolve():new InstantPromise;for(o=0;o<c;o++)calculateHeading(e,n+a/c*(o+1),u),m=m.then(partialRotate(e,u.angle,u.radians,d||!l));return m.then(function(){return calculateHeading(e,n+a)})}function getCoordinates(e,t){return void 0===t&&(t=e&&(e.y||e._y||e[1])||0,e=e&&(e.x||e._x||e[0])||0),{x:e,y:t}}function hexToRGB(e){var t,r,n;return(t=/^rgba?\\((\\d+),(\\d+),(\\d+)(?:,([.\\d]+))?\\)$/.exec(e))?(n=[parseInt(t[1]),parseInt(t[2]),parseInt(t[3])],t[4]&&n.push(parseFloat(t[4]))):/^#?[a-f\\d]{3}|[a-f\\d]{6}$/i.exec(e)?(4===e.length&&(e=e.replace(/^#?([a-f\\d])([a-f\\d])([a-f\\d])$/i,function(e,t,r,n){return t+t+r+r+n+n})),r=/^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(e),n=[parseInt(r[1],16),parseInt(r[2],16),parseInt(r[3],16)]):n=e,n}function createColor(e,t,n,s,l){var o;if(void 0!==n&&(t=[t,n,s,l]),t.constructor===Array&&t.length){if(255===e){for(o=0;3>o;o++)if(\"number\"==typeof t[o])t[o]=r(0,a(255,parseInt(t[o])));else throw new Sk.builtin.ValueError(\"bad color sequence\");}else for(o=0;3>o;o++)if(\"number\"!=typeof t[o])throw new Sk.builtin.ValueError(\"bad color sequence\");else if(1>=t[o])t[o]=r(0,a(255,parseInt(255*t[o])));else throw new Sk.builtin.ValueError(\"bad color sequence\");\"number\"==typeof t[o]?(t[3]=r(0,a(1,t[o])),t=\"rgba(\"+t.join(\",\")+\")\"):t=\"rgb(\"+t.slice(0,3).join(\",\")+\")\"}else if(\"string\"==typeof t&&!t.match(/\\s*url\\s*\\(/i))t=t.replace(/\\s+/g,\"\");else return\"black\";return t}function calculateHeading(e,t,r){var n=e._angle||0,a=e._radians||0;return r||(r={}),\"number\"==typeof t&&(e._isRadians?n=a=t%Turtle.RADIANS:e._fullCircle?(n=t%e._fullCircle,a=n/e._fullCircle*Turtle.RADIANS):n=a=0,0>n&&(n+=e._fullCircle,a+=Turtle.RADIANS)),r.angle=n,r.radians=a,r}function pythonToJavascriptFunction(e,t){return function(){var r=Array.prototype.slice.call(arguments),n=r.map(function(e){return Sk.ffi.remapToPy(e)});return\"undefined\"!=typeof t&&n.unshift(t),Sk.misceval.applyAsync(void 0,e,void 0,void 0,void 0,n).catch(Sk.uncaughtException)}}function addModuleMethod(e,t,r,n){var a,s=r.replace(/^\\$/,\"\"),l=s.replace(/_\\$[a-z]+\\$$/i,\"\"),o=e.prototype[r].length,d=e.prototype[r].minArgs,_=e.prototype[r].co_varnames||[],c=e.prototype[r].returnType,u=e.prototype[r].isSk;void 0===d&&(d=o),a=function(){var e,t,a,s,_,m=Array.prototype.slice.call(arguments,0),p=n?n():m.shift().instance;if(m.length<d||m.length>o)throw _=d===o?\"exactly \"+o:\"between \"+d+\" and \"+o,new Sk.builtin.TypeError(l+\"() takes \"+_+\" positional argument(s) (\"+m.length+\" given)\");for(e=m.length;0<=--e;)void 0!==m[e]&&(m[e]=m[e]instanceof Sk.builtin.func?pythonToJavascriptFunction(m[e]):m[e]instanceof Sk.builtin.method?pythonToJavascriptFunction(m[e].im_func,m[e].im_self):m[e]&&m[e].$d instanceof Sk.builtin.dict&&m[e].instance?m[e].instance:Sk.ffi.remapToJs(m[e]));var g=m.slice(0);for(m=[],e=g.length;0<=e;--e)null!==g[e]&&(m[e]=g[e]);try{t=p[r].apply(p,m)}catch(t){throw window&&window.console&&(window.console.log(\"wrapped method failed\"),window.console.log(t.stack)),t}return t instanceof InstantPromise&&(t=t.lastResult),t instanceof Promise?(t=t.catch(function(t){throw window&&window.console&&(window.console.log(\"promise failed\"),window.console.log(t.stack)),t}),a=new Sk.misceval.Suspension,a.resume=function(){return void 0===s?Sk.builtin.none.none$:Sk.ffi.remapToPy(s)},a.data={type:\"Sk.promise\",promise:t.then(function(e){return s=e,e})},a):void 0===t?Sk.builtin.none.none$:u?t:\"function\"==typeof c?c(t):Sk.ffi.remapToPy(t)},a.co_name=new Sk.builtin.str(l),a.co_varnames=_.slice(),a.$defaults=[];for(var m=d;m<_.length;m++)a.$defaults.push(Sk.builtin.none.none$);n||a.co_varnames.unshift(\"self\"),t[s]=new Sk.builtin.func(a)}function initTurtle(e,t){Sk.builtin.pyCheckArgs(\"__init__\",arguments,2,3,!1,!1),e.instance=new Turtle(t),e.instance.skInstance=e}function focusTurtle(e){return void 0!==e&&(w=!!e,w?getTarget().focus():getTarget().blur()),w}function resetTurtle(){for(cancelAnimationFrame(),getScreen().reset(),getFrameManager().reset();e.firstChild;)e.removeChild(e.firstChild);h&&h.reset(),$=0,p=void 0,f=void 0,h=void 0,k=0}function stopTurtle(){cancelAnimationFrame(),h&&h.reset(),$=0,p=void 0,f=void 0,h=void 0,k=0}var u,m,p,g,f,h,y={__name__:new Sk.builtin.str(\"turtle\")},$=0,w=!0,b=1e3/30,v={},k=0,x={},T={target:\"turtle\",width:400,height:400,worldWidth:0,worldHeight:0,animate:!0,bufferSize:0,allowUndo:!0,assets:{}};e.hasAttribute(\"tabindex\")||e.setAttribute(\"tabindex\",0),x.FLOAT=function(e){return new Sk.builtin.float_(e)},x.COLOR=function(e){if(\"string\"==typeof e)return new Sk.builtin.str(e);for(var t=0;3>t;t++)e[t]=Sk.builtin.assk$(e[t]);return 4===e.length&&(e[3]=new Sk.builtin.float_(e[3])),new Sk.builtin.tuple(e)},x.TURTLE_LIST=function(e){for(var t=[],r=0;r<e.length;r++)t.push(e[r].skInstance);return new Sk.builtin.tuple(t)},v.arrow=[[-10,0],[10,0],[0,10]],v.square=[[10,-10],[10,10],[-10,10],[-10,-10]],v.triangle=[[10,-5.77],[0,11.55],[-10,-5.77]],v.classic=[[0,0],[-5,-9],[0,-7],[5,-9]],v.turtle=[[0,16],[-2,14],[-1,10],[-4,7],[-7,9],[-9,8],[-6,5],[-7,1],[-5,-3],[-8,-6],[-6,-8],[-4,-5],[0,-7],[4,-5],[6,-8],[8,-6],[5,-3],[7,1],[6,5],[9,8],[7,9],[4,7],[1,10],[2,14]],v.circle=[[10,0],[9.51,3.09],[8.09,5.88],[5.88,8.09],[3.09,9.51],[0,10],[-3.09,9.51],[-5.88,8.09],[-8.09,5.88],[-9.51,3.09],[-10,0],[-9.51,-3.09],[-8.09,-5.88],[-5.88,-8.09],[-3.09,-9.51],[-0,-10],[3.09,-9.51],[5.88,-8.09],[8.09,-5.88],[9.51,-3.09]],g=function(){for(var e in Sk.TurtleGraphics||(Sk.TurtleGraphics={}),T)Sk.TurtleGraphics.hasOwnProperty(e)||(Sk.TurtleGraphics[e]=T[e]);return Sk.TurtleGraphics}(),InstantPromise.prototype.then=function(e){if(this.lastError)return this;try{this.lastResult=e(this.lastResult)}catch(t){this.lastResult=void 0,this.lastError=t}return this.lastResult instanceof Promise?this.lastResult:this},InstantPromise.prototype.catch=function(e){if(this.lastError)try{this.lastResult=e(this.lastError),this.lastError=void 0}catch(t){this.lastResult=void 0,this.lastError=t}return this.lastResult instanceof Promise?this.lastResult:this};var A;(function(e){function animationFrame(e){return g.animate?!e&&t?t:function(t){return m=window.setTimeout(t,e||b),m}:function(e){e()}}var t;(function(e){e&&(t=function(t){return u=e(t)})})(window.requestAnimationFrame||window.mozRequestAnimationFrame),e.willRenderNext=function(){return!!(this._buffer&&this._frameCount+1===this.frameBuffer())},e.turtles=function(){return this._turtles},e.addTurtle=function(e){this._turtles.push(e)},e.reset=function(){if(this._turtles)for(var e=this._turtles.length;0<=--e;)this._turtles[e].reset();this._turtles=[],this._frames=[],this._frameCount=0,this._buffer=1,this._rate=0,this._animationFrame=animationFrame()},e.addFrame=function(e,t){var r=!1;return t&&(this._frameCount+=1),this.frames().push(e),r=!g.animate||this._buffer&&this._frameCount===this.frameBuffer(),r?this.update():new InstantPromise},e.frames=function(){return this._frames},e.frameBuffer=function(e){return\"number\"==typeof e&&(this._buffer=0|e,e&&e<=this._frameCount)?this.update():this._buffer},e.refreshInterval=function(e){return\"number\"==typeof e&&(this._rate=0|e,this._animationFrame=animationFrame(e)),this._rate},e.update=function(){return this._frames&&this._frames.length?this.requestAnimationFrame():new InstantPromise},e.requestAnimationFrame=function(){var e,t,r=this._frames,n=this._animationFrame,a=this._turtles,s=getScreen().spriteLayer();return this._frames=[],this._frameCount=0,new Promise(function(l){n(function paint(){for(t=0;t<r.length;t++)r[t]&&r[t]();for(clearLayer(s),t=0;t<a.length;t++)e=a[t],e.getState().shown&&drawTurtle(e.getState(),s);l()})})}})(FrameManager.prototype),function(e){e.onEvent=function(t,r){function computeCoordinates(){if(!_){var t=getScreen(),l=t.spriteLayer().canvas.getBoundingClientRect();e=0|r.clientX-l.left,n=0|r.clientY-l.top,a=e*t.xScale+t.llx,s=n*t.yScale+t.ury,_=!0}}var e,n,a,s,l,o=this._managers[t],d=this._managers.mousemove,_=!1;if((\"mousedown\"===t||\"mouseup\"===t)&&d&&d.length)for(computeCoordinates(),l=d.length;0<=--l;)d[l].test(e,n,a,s)&&d[l].canMove(\"mousedown\"===t);if(o&&o.length)for(computeCoordinates(),l=o.length;0<=--l;)\"mousemove\"===t&&o[l].canMove()&&o[l].test(e,n,a,s)?o[l].trigger([a,s]):\"mousedown\"===t&&o[l].test(e,n,a,s)&&o[l].trigger([a,s])},e.reset=function(){this._managers={}},e.addManager=function(e,t){this._managers[e]||(this._managers[e]=[]),this._managers[e].push(t)}}(MouseHandler.prototype),function(e){e.reset=function(){this._handlers=void 0},e.canMove=function(e){return!!(this._target&&this._target.hitTest)&&(void 0!==e&&(this._target.hitTest.hit=e),this._target.hitTest.hit)},e.test=function(e,t,r,n){return this._target&&this._target.hitTest?this._target.hitTest(e,t,r,n):!!this._target},e.trigger=function(e){var t,r=this._handlers;if(r&&r.length)for(t=0;t<r.length;t++)r[t].apply({},e)},e.addHandler=function(e,t){var r=this._handlers;if(!t&&r&&r.length)for(;r.shift(););return\"function\"==typeof e?void(!r&&(r=this._handlers=[]),r.push(e)):void(r&&!r.length&&this.reset())}}(EventManager.prototype),Turtle.RADIANS=2*o,function(e){function circleRotate(e,t,r){return function(){return e.addUpdate(void 0,!1,{angle:t,radians:r})}}function circleSegment(e,t,r,n,a,s){return function(){return e.translate(t,r,n,a,s,!0)}}e.hitTest=function(e,t){var r=getScreen().hitTestLayer();clearLayer(r),drawTurtle(this.getState(),r);var n=r.getImageData(e,t,1,1).data;return n[3]||n[0]||n[1]||n[2]},e.addUpdate=function(e,t,r){var n=this,a=this.getState(),s=Array.prototype.slice.call(arguments,r?2:3);return getFrameManager().addFrame(function(){if(e&&e.apply(a,s),r)for(var t in r)a[t]=r[t]},t)},e.getState=function(){var e=this;return this._state||(this._state={x:this._x,y:this._y,angle:this._angle,radians:this._radians,shape:this._shape,color:this._color,fill:this._fill,filling:this._filling,size:this._size,speed:this._computed_speed,down:this._down,shown:this._shown,colorMode:this._colorMode,context:function(){return e.getPaper()}}),this._state},e.translate=function(e,t,r,n,a,s){var l=this;return translate(this,e,t,r,n,a,s).then(function(e){l._x=e[0],l._y=e[1]})},e.rotate=function(e,t,r){var n=this;return rotate(this,e,t,r).then(function(e){n._angle=e.angle,n._radians=e.radians})},e.queueMoveBy=function(e,t,r,n){var a=c(r)*n,s=_(r)*n;return this.translate(e,t,a,s,!0)},e.queueTurnTo=function(e,t){return t%=this._fullCircle,0>t&&(t+=this._fullCircle),this.rotate(e,t-e)},e.getManager=function(e){return this._managers[e]||(this._managers[e]=new EventManager(e,this)),this._managers[e]},e.getPaper=function(){return this._paper||(this._paper=createLayer(2))},e.reset=function(){for(var e in this._x=0,this._y=0,this._radians=0,this._angle=0,this._shown=!0,this._down=!0,this._color=\"black\",this._fill=\"black\",this._size=1,this._filling=!1,this._undoBuffer=[],this._speed=3,this._computed_speed=5,this._colorMode=1,this._state=void 0,this._managers)this._managers[e].reset();this._isRadians=!1,this._fullCircle=360,this._bufferSize=\"number\"==typeof g.bufferSize?g.bufferSize:0,removeLayer(this._paper),this._paper=void 0},e.$degrees=function(e){return e=\"number\"==typeof e?s(e):360,this._isRadians=!1,this._angle=e&&this._fullCircle?this._angle/this._fullCircle*e:this._radians=0,this._fullCircle=e,this.addUpdate(void 0,!1,{angle:this._angle,radians:this._radians})},e.$degrees.minArgs=0,e.$degrees.co_varnames=[\"fullcircle\"],e.$degrees.returnType=x.FLOAT,e.$radians=function(){return this._isRadians||(this._isRadians=!0,this._angle=this._radians,this._fullCircle=Turtle.RADIANS),this._angle},e.$radians.returnType=x.FLOAT,e.$position=e.$pos=function(){return[this.$xcor(),this.$ycor()]},e.$position.returnType=function(e){return new Sk.builtin.tuple([new Sk.builtin.float_(e[0]),new Sk.builtin.float_(e[1])])},e.$towards=function(e,t){var r=getCoordinates(e,t),n=o+d(this._y-r.y,this._x-r.x),a=n*(this._fullCircle/Turtle.RADIANS);return a},e.$towards.co_varnames=[\"x\",\"y\"],e.$towards.minArgs=1,e.$towards.returnType=x.FLOAT,e.$distance=function(e,t){var r=getCoordinates(e,t),a=r.x-this._x,s=r.y-this._y;return n(a*a+s*s)},e.$distance.co_varnames=[\"x\",\"y\"],e.$distance.minArgs=1,e.$distance.returnType=x.FLOAT,e.$heading=function(){return 1e-13>s(this._angle)?0:this._angle},e.$heading.returnType=x.FLOAT,e.$xcor=function(){return 1e-13>s(this._x)?0:this._x},e.$xcor.returnType=x.FLOAT,e.$ycor=function(){return 1e-13>s(this._y)?0:this._y},e.$ycor.returnType=x.FLOAT,e.$forward=e.$fd=function(e){return pushUndo(this),this.queueMoveBy(this._x,this._y,this._radians,e)},e.$forward.co_varnames=e.$fd.co_varnames=[\"distance\"],e.$undo=function(){popUndo(this)},e.$undobufferentries=function(){return this._undoBuffer.length},e.$setundobuffer=function(e){this._bufferSize=\"number\"==typeof e?a(s(e),1e3):0},e.$setundobuffer.co_varnames=[\"size\"],e.$backward=e.$back=e.$bk=function(e){return pushUndo(this),this.queueMoveBy(this._x,this._y,this._radians,-e)},e.$backward.co_varnames=e.$back.co_varnames=e.$bk.co_varnames=[\"distance\"],e.$goto_$rw$=e.$setpos=e.$setposition=function(e,t){var r=getCoordinates(e,t);return pushUndo(this),this.translate(this._x,this._y,r.x-this._x,r.y-this._y,!0)},e.$goto_$rw$.co_varnames=e.$setpos.co_varnames=e.$setposition.co_varnames=[\"x\",\"y\"],e.$goto_$rw$.minArgs=e.$setpos.minArgs=e.$setposition.minArgs=1,e.$setx=function(e){return this.translate(this._x,this._y,e-this._x,0,!0)},e.$setx.co_varnames=[\"x\"],e.$sety=function(e){return this.translate(this._x,this._y,0,e-this._y,!0)},e.$sety.co_varnames=[\"y\"],e.$home=function(){var e=this,t=this._angle;return pushUndo(this),e.translate(this._x,this._y,-this._x,-this._y,!0).then(function(){return e.queueTurnTo(t,0)}).then(function(){})},e.$right=e.$rt=function(e){return pushUndo(this),this.rotate(this._angle,-e)},e.$right.co_varnames=e.$rt.co_varnames=[\"angle\"],e.$left=e.$lt=function(e){return pushUndo(this),this.rotate(this._angle,e)},e.$left.co_varnames=e.$lt.co_varnames=[\"angle\"],e.$setheading=e.$seth=function(e){return pushUndo(this),this.queueTurnTo(this._angle,e)},e.$setheading.co_varnames=e.$seth.co_varnames=[\"angle\"],e.$circle=function(e,t,r){var n,d,u,m,p,g,f,h,$,b=this,v=this._x,k=this._y,T=this._angle,A={},L=1/getScreen().lineScale,S=!0;for(pushUndo(this),void 0===t&&(t=b._fullCircle),void 0===r&&(d=s(t)/b._fullCircle,r=1+(0|a(11+s(e*L)/6,59)*d)),u=t/r,m=.5*u,p=2*e*_(u*o/b._fullCircle),0>e?(p=-p,u=-u,m=-m,n=T-t):n=T+t,$=getFrameManager().willRenderNext()?Promise.resolve():new InstantPromise,T+=m,g=0;g<r;g++)calculateHeading(b,T+u*g,A),f=c(A.radians)*p,h=_(A.radians)*p,$=$.then(circleRotate(b,A.angle,A.radians)).then(circleSegment(b,v,k,f,h,S)),v+=f,k+=h,S=!1;return $=$.then(function(){return calculateHeading(b,n,A),b._angle=A.angle,b._radians=A.radians,b.addUpdate(void 0,!0,A)}),$},e.$circle.co_varnames=[\"radius\",\"extent\",\"steps\"],e.$circle.minArgs=1,e.$penup=e.$up=e.$pu=function(){return this._down=!1,this.addUpdate(void 0,!1,{down:!1})},e.$pendown=e.$down=e.$pd=function(){return this._down=!0,this.addUpdate(void 0,!1,{down:!0})},e.$isdown=function(){return this._down},e.$speed=function(e){return void 0===e?this._speed:(this._speed=r(0,a(1e3,e)),this._computed_speed=r(0,2*e-1),this.addUpdate(void 0,!1,{speed:this._computed_speed}))},e.$speed.minArgs=0,e.$speed.co_varnames=[\"speed\"],e.$pencolor=function(e,t,r,n){return void 0===e?hexToRGB(this._color):(this._color=createColor(this._colorMode,e,t,r,n),this.addUpdate(void 0,this._shown,{color:this._color}))},e.$pencolor.co_varnames=[\"r\",\"g\",\"b\",\"a\"],e.$pencolor.minArgs=0,e.$pencolor.returnType=x.COLOR,e.$fillcolor=function(e,t,r,n){return void 0===e?hexToRGB(this._fill):(this._fill=createColor(this._colorMode,e,t,r,n),this.addUpdate(void 0,this._shown,{fill:this._fill}))},e.$fillcolor.co_varnames=[\"r\",\"g\",\"b\",\"a\"],e.$fillcolor.minArgs=0,e.$fillcolor.returnType=x.COLOR,e.$color=function(e,t,r,n){return void 0===e?[this.$pencolor(),this.$fillcolor()]:(void 0===t||void 0!==r?(this._color=createColor(this._colorMode,e,t,r,n),this._fill=this._color):(this._color=createColor(this._colorMode,e),this._fill=createColor(this._colorMode,t)),this.addUpdate(void 0,this._shown,{color:this._color,fill:this._fill}))},e.$color.minArgs=0,e.$color.co_varnames=[\"color\",\"fill\",\"b\",\"a\"],e.$color.returnType=function(e){return new Sk.builtin.tuple([x.COLOR(e[0]),x.COLOR(e[1])])},e.$fill=function(e){this;return void 0===e?this._filling:(e=!!e,e===this._filling)?void 0:(this._filling=e,e?(pushUndo(this),this.addUpdate(void 0,!1,{filling:!0,fillBuffer:[{x:this._x,y:this._y}]})):(pushUndo(this),this.addUpdate(function(){this.fillBuffer.push(this),drawFill.call(this)},!0,{filling:!1,fillBuffer:void 0})))},e.$fill.co_varnames=[\"flag\"],e.$fill.minArgs=0,e.$begin_fill=function(){return this.$fill(!0)},e.$end_fill=function(){return this.$fill(!1)},e.$stamp=function(){return pushUndo(this),this.addUpdate(function(){drawTurtle(this,this.context())},!0)},e.$dot=function(e,t,n,l,i){return pushUndo(this),e=Sk.builtin.asnum$(e),e=\"number\"==typeof e?r(1,0|s(e)):r(this._size+4,2*this._size),t=void 0===t?this._color:createColor(this._colorMode,t,n,l,i),this.addUpdate(drawDot,!0,void 0,e,t)},e.$dot.co_varnames=[\"size\",\"color\",\"g\",\"b\",\"a\"],e.$write=function(e,t,r,n){var a,s,l,i,o,d=this;return pushUndo(this),e+=\"\",n&&n.constructor===Array&&(s=\"string\"==typeof n[0]?n[0]:\"Arial\",l=(n[1]||\"12pt\")+\"\",i=\"string\"==typeof n[2]?n[2]:\"normal\",/^\\d+$/.test(l)&&(l+=\"pt\"),n=[i,l,s].join(\" \")),r||(r=\"left\"),a=this.addUpdate(drawText,!0,void 0,e,r,n),t&&(\"left\"===r||\"center\"===r)&&(o=measureText(e,n),\"center\"===r&&(o/=2),a=a.then(function(){var e=d.getState();return d.translate(e.x,e.y,o,0,!0)})),a},e.$write.co_varnames=[\"message\",\"move\",\"align\",\"font\"],e.$write.minArgs=1,e.$pensize=e.$width=function(e){return void 0===e?this._size:(this._size=e,this.addUpdate(void 0,this._shown,{size:e}))},e.$pensize.minArgs=e.$width.minArgs=0,e.$pensize.co_varnames=e.$width.co_varnames=[\"width\"],e.$showturtle=e.$st=function(){return this._shown=!0,this.addUpdate(void 0,!0,{shown:!0})},e.$hideturtle=e.$ht=function(){return this._shown=!1,this.addUpdate(void 0,!0,{shown:!1})},e.$isvisible=function(){return this._shown},e.$shape=function(e){return e&&v[e]?(this._shape=e,this.addUpdate(void 0,this._shown,{shape:e})):this._shape},e.$shape.minArgs=0,e.$shape.co_varnames=[\"name\"],e.$colormode=function(e){return void 0===e?this._colorMode:(this._colorMode=255===e?255:1,this.addUpdate(void 0,this._shown,{colorMode:this._colorMode}))},e.$colormode.minArgs=0,e.$colormode.co_varnames=[\"cmode\"],e.$colormode.returnType=function(e){return 255===e?new Sk.builtin.int_(255):new Sk.builtin.float_(1)},e.$window_width=function(){return this._screen.$window_width()},e.$window_height=function(){return this._screen.$window_height()},e.$tracer=function(e,t){return this._screen.$tracer(e,t)},e.$tracer.minArgs=0,e.$tracer.co_varnames=[\"n\",\"delay\"],e.$update=function(){return this._screen.$update()},e.$delay=function(e){return this._screen.$delay(e)},e.$delay.minArgs=0,e.$delay.co_varnames=[\"delay\"],e.$reset=function(){return this.reset(),this.$clear()},e.$mainloop=e.$done=function(){return this._screen.$mainloop()},e.$clear=function(){return this.addUpdate(function(){clearLayer(this.context())},!0)},e.$dot.minArgs=0,e.$onclick=function(e,t,r){this.getManager(\"mousedown\").addHandler(e,r)},e.$onclick.minArgs=1,e.$onclick.co_varnames=[\"method\",\"btn\",\"add\"],e.$onrelease=function(e,t,r){this.getManager(\"mouseup\").addHandler(e,r)},e.$onrelease.minArgs=1,e.$onrelease.co_varnames=[\"method\",\"btn\",\"add\"],e.$ondrag=function(e,t,r){this.getManager(\"mousemove\").addHandler(e,r)},e.$ondrag.minArgs=1,e.$ondrag.co_varnames=[\"method\",\"btn\",\"add\"],e.$getscreen=function(){return Sk.misceval.callsimArray(y.Screen)},e.$getscreen.isSk=!0,e.$clone=function(){var e=Sk.misceval.callsimOrSuspendArray(y.Turtle);return e.instance._x=this._x,e.instance._y=this._y,e.instance._angle=this._angle,e.instance._radians=this._radians,e.instance._shape=this._shape,e.instance._color=this._color,e.instance._fill=this._fill,e.instance._filling=this._filling,e.instance._size=this._size,e.instance._computed_speed=this._computed_speed,e.instance._down=this._down,e.instance._shown=this._shown,e.instance._colorMode=this._colorMode,e.instance._isRadians=this._isRadians,e.instance._fullCircle=this._fullCircle,e.instance._bufferSize=this._bufferSize,e.instance._undoBuffer=this._undoBuffer,e._clonedFrom=this,e},e.$clone.returnType=function(e){return e},e.$getturtle=e.$getpen=function(){return this.skInstance},e.$getturtle.isSk=!0}(Turtle.prototype),function(e){e.spriteLayer=function(){return this._sprites||(this._sprites=createLayer(3))},e.bgLayer=function(){return this._background||(this._background=createLayer(1))},e.hitTestLayer=function(){return this._hitTest||(this._hitTest=createLayer(0,!0))},e.getManager=function(e){return this._managers[e]||(this._managers[e]=new EventManager(e,this)),this._managers[e]},e.reset=function(){for(var e in this._keyListeners=void 0,this._keyLogger)window.clearInterval(this._keyLogger[e]),window.clearTimeout(this._keyLogger[e]),delete this._keyLogger[e];for(e in this._keyDownListener&&(getTarget().removeEventListener(\"keydown\",this._keyDownListener),this._keyDownListener=void 0),this._keyUpListener&&(getTarget().removeEventListener(\"keyup\",this._keyUpListener),this._keyUpListener=void 0),this._timer&&(window.clearTimeout(this._timer),this._timer=void 0),this._managers)this._managers[e].reset();this._mode=\"standard\",removeLayer(this._sprites),this._sprites=void 0,removeLayer(this._background),this._background=void 0},e.setUpWorld=function(e,t,r,n){var l=this;l.llx=e,l.lly=t,l.urx=r,l.ury=n,l.xScale=(r-e)/getWidth(),l.yScale=-1*(n-t)/getHeight(),l.lineScale=a(s(l.xScale),s(l.yScale))},e.$setup=function(e,t,r,n){return isNaN(parseFloat(e))&&(e=getWidth()),isNaN(parseFloat(t))&&(t=getHeight()),1>=e&&(e=getWidth()*e),1>=t&&(t=getHeight()*t),this._width=e,this._height=t,this._xOffset=void 0===r||isNaN(parseInt(r))?0:parseInt(r),this._yOffset=void 0===n||isNaN(parseInt(n))?0:parseInt(n),\"world\"===this._mode?this._setworldcoordinates(this.llx,this.lly,this.urx,this.ury):this._setworldcoordinates(-e/2,-t/2,e/2,t/2)},e.$setup.minArgs=0,e.$setup.co_varnames=[\"width\",\"height\",\"startx\",\"starty\"],e.$register_shape=e.$addshape=function(e,t){return t?void(v[e]=t):getAsset(e).then(function(t){v[e]=t})},e.$register_shape.minArgs=1,e.$getshapes=function(){return Object.keys(v)},e.$tracer=function(e,t){return void 0!==e||void 0!==t?(\"number\"==typeof t&&(this._delay=t,getFrameManager().refreshInterval(t)),\"number\"==typeof e?(this._frames=e,getFrameManager().frameBuffer(e)):void 0):this._frames},e.$tracer.co_varnames=[\"frames\",\"delay\"],e.$tracer.minArgs=0,e.$delay=function(e){return void 0===e?void 0===this._delay?b:this._delay:this.$tracer(void 0,e)},e.$delay.co_varnames=[\"delay\"],e._setworldcoordinates=function(e,t,r,n){var a=this,s=getFrameManager().turtles();return this.setUpWorld(e,t,r,n),this._sprites&&applyWorld(this,this._sprites),this._background&&applyWorld(this,this._background),this.$clear()},e.$setworldcoordinates=function(e,t,r,n){return this._mode=\"world\",this._setworldcoordinates(e,t,r,n)},e.$setworldcoordinates.co_varnames=[\"llx\",\"lly\",\"urx\",\"ury\"],e.minArgs=4,e.$clear=e.$clearscreen=function(){return this.reset(),this.$reset()},e.$update=function(){return getFrameManager().update()},e.$reset=e.$resetscreen=function(){var e=this,t=getFrameManager().turtles();return getFrameManager().addFrame(function(){applyWorld(e,e._sprites),applyWorld(e,e._background);for(var r=0;r<t.length;r++)t[r].reset(),applyWorld(e,t[r]._paper)},!0)},e.$window_width=function(){return getWidth()},e.$window_height=function(){return getHeight()},e.$delay.minArgs=0,e.$turtles=function(){return getFrameManager().turtles()},e.$turtles.returnType=x.TURTLE_LIST,e.$bgpic=function(e){var t;return e?(t=this,getAsset(e).then(function(e){clearLayer(t.bgLayer(),void 0,e)})):this._bgpic},e.$bgpic.minArgs=0,e.$bgpic.co_varnames=[\"name\"],e.$bgcolor=function(e,t,r,n){return void 0===e?hexToRGB(this._bgcolor):(this._bgcolor=createColor(this._colorMode,e,t,r,n),void clearLayer(this.bgLayer(),this._bgcolor))},e.$bgcolor.minArgs=0,e.$bgcolor.co_varnames=[\"color\",\"g\",\"b\",\"a\"],e.$bgcolor.returnType=x.COLOR,e.$mainloop=e.$done=function(){},e.$bye=function(){return Sk.TurtleGraphics.reset()},e.$exitonclick=function(){return this._exitOnClick=!0,this.getManager(\"mousedown\").addHandler(function(){resetTurtle()},!1)},e.$onclick=function(e,t,r){this._exitOnClick||this.getManager(\"mousedown\").addHandler(e,r)},e.$onclick.minArgs=1,e.$onclick.co_varnames=[\"method\",\"btn\",\"add\"];var t={8:/^back(space)?$/i,9:/^tab$/i,13:/^(enter|return)$/i,16:/^shift$/i,17:/^(ctrl|control)$/i,18:/^alt$/i,27:/^esc(ape)?$/i,32:/^space$/i,33:/^page[\\s\\-]?up$/i,34:/^page[\\s\\-]?down$/i,35:/^end$/i,36:/^home$/i,37:/^left([\\s\\-]?arrow)?$/i,38:/^up([\\s\\-]?arrow)?$/i,39:/^right([\\s\\-]?arrow)?$/i,40:/^down([\\s\\-]?arrow)?$/i,45:/^insert$/i,46:/^del(ete)?$/i};e._createKeyRepeater=function(e,t){var r=this;r._keyLogger[t]=window.setTimeout(function(){r._keyListeners[e](),r._keyLogger[t]=window.setInterval(function(){r._keyListeners[e]()},50)},333)},e._createKeyDownListener=function(){var r=this;this._keyDownListener||(this._keyDownListener=function(n){var e=String.fromCharCode;if(focusTurtle()){var a,s,l=n.charCode||n.keyCode,i=e(l).toLowerCase();if(!r._keyLogger[l])for(a in r._keyListeners)if(s=1<a.length&&t[l]&&t[l].test(a),a===i||s){r._keyListeners[a](),r._createKeyRepeater(a,l),n.preventDefault();break}}},getTarget().addEventListener(\"keydown\",this._keyDownListener))},e._createKeyUpListener=function(){var t=this;this._keyUpListener||(this._keyUpListener=function(r){var e=t._keyLogger[r.charCode||r.keyCode];void 0!==e&&(r.preventDefault(),window.clearInterval(e),window.clearTimeout(e),delete t._keyLogger[r.charCode||r.keyCode])},getTarget().addEventListener(\"keyup\",this._keyUpListener))},e.$listen=function(){this._createKeyUpListener(),this._createKeyDownListener()},e.$onkey=function(e,t){if(\"function\"==typeof t){var r=e;e=t,t=r}t=(t+\"\").toLowerCase(),e&&\"function\"==typeof e?(!this._keyListeners&&(this._keyListeners={}),this._keyListeners[t]=e):delete this._keyListeners[t]},e.$onkey.minArgs=2,e.$onkey.co_varnames=[\"method\",\"keyValue\"],e.$onscreenclick=function(e,t,r){this.getManager(\"mousedown\").addHandler(e,r)},e.$onscreenclick.minArgs=1,e.$onscreenclick.co_varnames=[\"method\",\"btn\",\"add\"],e.$ontimer=function(e,t){this._timer&&(window.clearTimeout(this._timer),this._timer=void 0),e&&\"number\"==typeof t&&(this._timer=window.setTimeout(e,r(0,0|t)))},e.$ontimer.minArgs=0,e.$ontimer.co_varnames=[\"method\",\"interval\"]}(Screen.prototype);var L=new Image,S=document.createElement(\"canvas\").getContext(\"2d\");for(var C in initTurtle.co_varnames=[\"self\",\"shape\"],initTurtle.co_name=new Sk.builtin.str(\"Turtle\"),initTurtle.co_argcount=2,initTurtle.$defaults=[Sk.builtin.none.none$,new Sk.builtin.str(\"classic\")],Turtle.prototype)/^\\$[a-z_]+/.test(C)&&addModuleMethod(Turtle,y,C,ensureAnonymous);return addModuleMethod(Screen,y,\"$mainloop\",getScreen),addModuleMethod(Screen,y,\"$done\",getScreen),addModuleMethod(Screen,y,\"$bye\",getScreen),addModuleMethod(Screen,y,\"$tracer\",getScreen),addModuleMethod(Screen,y,\"$update\",getScreen),addModuleMethod(Screen,y,\"$delay\",getScreen),addModuleMethod(Screen,y,\"$window_width\",getScreen),addModuleMethod(Screen,y,\"$window_height\",getScreen),y.Turtle=Sk.misceval.buildClass(y,function TurtleWrapper(e,t){for(var r in t.__init__=new Sk.builtin.func(initTurtle),Turtle.prototype)/^\\$[a-z_]+/.test(r)&&addModuleMethod(Turtle,t,r)},\"Turtle\",[]),y.Screen=Sk.misceval.buildClass(y,function ScreenWrapper(e,t){for(var r in t.__init__=new Sk.builtin.func(function(e){e.instance=getScreen()}),Screen.prototype)/^\\$[a-z_]+/.test(r)&&addModuleMethod(Screen,t,r)},\"Screen\",[]),{skModule:y,reset:resetTurtle,stop:stopTurtle,focus:focusTurtle,Turtle:Turtle,Screen:Screen}}(e),Sk.TurtleGraphics.module=e.turtleInstance.skModule,Sk.TurtleGraphics.reset=e.turtleInstance.reset,Sk.TurtleGraphics.stop=e.turtleInstance.stop,Sk.TurtleGraphics.focus=e.turtleInstance.focus,Sk.TurtleGraphics.raw={Turtle:e.turtleInstance.Turtle,Screen:e.turtleInstance.Screen},e.turtleInstance.skModule};","src/lib/types.py":"\"\"\"\nThis file was modified from CPython.\nCopyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,\n2011, 2012, 2013, 2014, 2015 Python Software Foundation; All Rights Reserved\n\"\"\"\n\"\"\"Define names for all type symbols known in the standard interpreter.\nTypes that are part of optional modules (e.g. array) are not listed.\n\"\"\"\nimport sys\n\n# Iterators in Python aren't a matter of type but of protocol.  A large\n# and changing number of builtin types implement *some* flavor of\n# iterator.  Don't check the type!  Use hasattr to check for both\n# \"__iter__\" and \"next\" attributes instead.\n\nNoneType = type(None)\nTypeType = type\nObjectType = object\nIntType = int\nLongType = long\nFloatType = float\nBooleanType = bool\ntry:\n    ComplexType = complex\nexcept NameError:\n    pass\nStringType = str\n\n# StringTypes is already outdated.  Instead of writing \"type(x) in\n# types.StringTypes\", you should use \"isinstance(x, basestring)\".  But\n# we keep around for compatibility with Python 2.2.\ntry:\n    UnicodeType = unicode\n    StringTypes = (StringType, UnicodeType)\nexcept NameError:\n    StringTypes = (StringType,)\n\nBufferType = buffer\n\nTupleType = tuple\nListType = list\nDictType = DictionaryType = dict\n\ndef _f(): pass\nFunctionType = type(_f)\nLambdaType = type(lambda: None)         # Same as FunctionType\n#CodeType = type(_f.func_code)\n\ndef _g():\n    yield 1\nGeneratorType = type(_g())\n\nclass _C:\n    def _m(self): pass\nClassType = type(_C)\nUnboundMethodType = type(_C._m)         # Same as MethodType\n_x = _C()\nInstanceType = type(_x)\nMethodType = type(_x._m)\nBuiltinFunctionType = type(len)\nBuiltinMethodType = type([].append)     # Same as BuiltinFunctionType\n\nModuleType = type(sys)\nFileType = file\ntry:\n    XRangeType = xrange\nexcept NameError:\n    pass\n\n# try:\n#     raise TypeError\n# except TypeError:\n#     tb = sys.exc_info()[2]\n#     TracebackType = type(tb)\n#     FrameType = type(tb.tb_frame)\n#     del tb\n\nSliceType = slice\n# EllipsisType = type(Ellipsis)\n\n# DictProxyType = type(TypeType.__dict__)\nNotImplementedType = type(NotImplemented)\n\n# For Jython, the following two types are identical\n# GetSetDescriptorType = type(FunctionType.func_code)\n# MemberDescriptorType = type(FunctionType.func_globals)\n\ndel sys, _f, _g, _C, _x                           # Not for export\n__all__ = list(n for n in globals() if n[:1] != '_')\n","src/lib/unittest/__init__.py":"__author__ = 'bmiller'\n'''\nThis is the start of something that behaves like\nthe unittest module from cpython.\n\n'''\n\n\nclass _AssertRaisesContext:\n    \"\"\"A context manager used to implement TestCase.assertRaises* methods.\"\"\"\n    def __init__(self, expected, test_case):\n        self.test_case = test_case\n        self.expected = expected\n        self.exception = None\n\n    def _is_subtype(self, expected, basetype):\n        if isinstance(expected, tuple):\n            return all(self._is_subtype(e, basetype) for e in expected)\n        return isinstance(expected, type) and issubclass(expected, basetype)\n\n    def handle(self, args, kwargs):\n        \"\"\"\n        If args is empty, assertRaises is being used as a\n        context manager, so return self.\n        If args is not empty, call a callable passing positional and keyword\n        arguments.\n        \"\"\"\n        try:\n            if not self._is_subtype(self.expected, BaseException):\n                raise TypeError('assertRaises() arg 1 must be an exception type or tuple of exception types')\n            if not args:\n                return self\n\n            callable_obj = args[0]\n            args = args[1:]\n            with self:\n                callable_obj(*args, **kwargs) \n\n        finally:\n            # bpo-23890: manually break a reference cycle\n            self = None\n\n    def __enter__(self):\n        return self\n\n    def __exit__(self, exc_type, exc_value, tb):\n        res = True\n        feedback = \"\"\n        self.exception = exc_value\n        try:\n            act_exc = exc_type.__name__\n        except AttributeError:\n            act_exc = str(exc_type)\n        try:\n            exp_exc = self.expected.__name__\n        except AttributeError:\n            exp_exc = str(self.expected)\n\n        if exc_type is None:\n            res = False\n            feedback = \"{} not raised\".format(exp_exc)\n        elif not issubclass(exc_type, self.expected):\n            res = False\n            feedback = \"Expected {} but got {}\".format(exp_exc, act_exc)\n\n        self.test_case.appendResult(res, act_exc, exp_exc, feedback)\n        return True\n\n\nclass TestCase:\n    def __init__(self):\n        self.numPassed = 0\n        self.numFailed = 0\n        self.assertPassed = 0\n        self.assertFailed = 0\n        self.verbosity = 1\n        self.tlist = []\n        testNames = {}\n        for name in dir(self):\n            if name[:4] == 'test' and name not in testNames:\n                self.tlist.append(getattr(self,name))\n                testNames[name]=True\n\n    def setUp(self):\n        pass\n\n    def tearDown(self):\n        pass\n    \n    def cleanName(self,funcName):\n    # work around skulpts lack of an __name__\n        funcName = str(funcName)\n        funcName = funcName[13:]\n        funcName = funcName[:funcName.find('<')-3]\n        return funcName\n\n    def main(self):\n\n        for func in self.tlist:\n            if self.verbosity > 1:\n                print('Running %s' % self.cleanName(func))\n            try:\n                self.setUp()\n                self.assertPassed = 0\n                self.assertFailed = 0\n                func()\n                self.tearDown()\n                if self.assertFailed == 0:\n                    self.numPassed += 1\n                else:\n                    self.numFailed += 1\n                    print('Tests failed in %s ' % self.cleanName(func))\n            except Exception as e:\n                self.assertFailed += 1\n                self.numFailed += 1\n                print('Test threw exception in %s (%s)' % (self.cleanName(func), e))\n        self.showSummary()\n\n    def assertEqual(self, actual, expected, feedback=\"\"):\n        res = actual==expected\n        if not res and feedback == \"\":\n            feedback = \"Expected %s to equal %s\" % (str(actual),str(expected))\n        self.appendResult(res, actual ,expected, feedback)\n\n    def assertNotEqual(self, actual, expected, feedback=\"\"):\n        res = actual != expected\n        if not res and feedback == \"\":\n            feedback = \"Expected %s to not equal %s\" % (str(actual),str(expected))\n        self.appendResult(res, actual, expected, feedback)\n\n    def assertTrue(self,x, feedback=\"\"):\n        res = bool(x) is True\n        if not res and feedback == \"\":\n            feedback = \"Expected %s to be True\" % (str(x))\n        self.appendResult(res, x, True, feedback)\n\n    def assertFalse(self,x, feedback=\"\"):\n        res = not bool(x)\n        if not res and feedback == \"\":\n            feedback = \"Expected %s to be False\" % (str(x))\n        self.appendResult(res, x, False, feedback)\n\n    def assertIs(self,a,b, feedback=\"\"):\n        res = a is b\n        if not res and feedback == \"\":\n            feedback = \"Expected %s to be the same object as %s\" % (str(a),str(b))\n        self.appendResult(res, a, b, feedback)\n\n    def assertIsNot(self,a,b, feedback=\"\"):\n        res = a is not b\n        if not res and feedback == \"\":\n            feedback = \"Expected %s to not be the same object as %s\" % (str(a),str(b))\n        self.appendResult(res, a, b, feedback)\n\n    def assertIsNone(self,x, feedback=\"\"):\n        res = x is None\n        if not res and feedback == \"\":\n            feedback = \"Expected %s to be None\" % (str(x))\n        self.appendResult(res, x, None, feedback)\n\n    def assertIsNotNone(self,x, feedback=\"\"):\n        res = x is not None\n        if not res and feedback == \"\":\n            feedback = \"Expected %s to not be None\" % (str(x))\n        self.appendResult(res, x, None, feedback)\n\n    def assertIn(self, a, b, feedback=\"\"):\n        res = a in b\n        if not res and feedback == \"\":\n            feedback = \"Expected %s to be in %s\" % (str(a),str(b))\n        self.appendResult(res, a, b, feedback)\n\n    def assertNotIn(self, a, b, feedback=\"\"):\n        res = a not in b\n        if not res and feedback == \"\":\n            feedback = \"Expected %s to not be in %s\" % (str(a),str(b))\n        self.appendResult(res, a, b, feedback)\n\n    def assertIsInstance(self,a,b, feedback=\"\"):\n        res = isinstance(a,b)\n        if not res and feedback == \"\":\n            feedback = \"Expected %s to be an instance of %s\" % (str(a), str(b))\n        self.appendResult(res, a, b, feedback)\n\n    def assertNotIsInstance(self,a,b, feedback=\"\"):\n        res = not isinstance(a,b)\n        if not res and feedback == \"\":\n            feedback = \"Expected %s to not be an instance of %s\" % (str(a),str(b))\n        self.appendResult(res, a, b, feedback)\n\n    def assertAlmostEqual(self, a, b, places=7, feedback=\"\", delta=None):\n\n        if delta is not None:\n            res = abs(a-b) <= delta\n        else:\n            if places is None:\n                places = 7\n            res = round(a-b, places) == 0\n        \n        if not res and feedback == \"\":\n            feedback = \"Expected %s to equal %s\" % (str(a),str(b))\n        self.appendResult(res, a, b, feedback)\n\n    def assertNotAlmostEqual(self, a, b, places=7, feedback=\"\", delta=None):\n\n        if delta is not None:\n            res = not (a == b) and abs(a - b) > delta\n        else:\n            if places is None:\n                places = 7\n\n            res = round(a-b, places) != 0\n\n        if not res and feedback == \"\":\n            feedback = \"Expected %s to not equal %s\" % (str(a),str(b))\n        self.appendResult(res, a, b, feedback)\n\n    def assertGreater(self,a,b, feedback=\"\"):\n        res = a > b\n        if not res and feedback == \"\":\n            feedback = \"Expected %s to be greater than %s\" % (str(a),str(b))\n        self.appendResult(res, a, b, feedback)\n\n    def assertGreaterEqual(self,a,b, feedback=\"\"):\n        res = a >= b\n        if not res and feedback == \"\":\n            feedback = \"Expected %s to be >= %s\" % (str(a),str(b))\n        self.appendResult(res, a, b, feedback)\n\n    def assertLess(self, a, b, feedback=\"\"):\n        res = a < b\n        if not res and feedback == \"\":\n            feedback = \"Expected %s to be less than %s\" % (str(a),str(b))\n        self.appendResult(res, a, b, feedback)\n\n    def assertLessEqual(self,a,b, feedback=\"\"):\n        res = a <= b\n        if not res and feedback == \"\":\n            feedback = \"Expected %s to be <= %s\" % (str(a),str(b))\n        self.appendResult(res, a, b, feedback)\n\n    def appendResult(self,res,actual,expected,feedback):\n        if res:\n            msg = 'Pass'\n            self.assertPassed += 1\n        else:\n            msg = 'Fail: ' +  feedback\n            print(msg)\n            self.assertFailed += 1\n\n    def assertRaises(self, expected_exception, *args, **kwargs):\n        context = _AssertRaisesContext(expected_exception, self)\n        try:\n            return context.handle(args, kwargs)\n        finally:\n            # bpo-23890: manually break a reference cycle\n            context = None\n\n    def fail(self, msg=None):\n        if msg is None:\n            msg = 'Fail'\n        else:\n            msg = 'Fail: ' + msg\n        print(msg)\n        self.assertFailed += 1\n\n    def showSummary(self):\n        pct = self.numPassed / (self.numPassed+self.numFailed) * 100\n        print(\"Ran %d tests, passed: %d failed: %d\\n\" % (self.numPassed+self.numFailed,\n                                               self.numPassed, self.numFailed))\n\n\n\ndef main(verbosity=1):\n    glob = globals() # globals() still needs work\n    for name in glob:\n        if type(glob[name]) == type and issubclass(glob[name], TestCase):\n            try:\n                tc = glob[name]()\n                tc.verbosity = verbosity\n                tc.main()\n            except:\n                print(\"Uncaught Error in: \", name)\n","src/lib/unittest/gui.py":"import document\nfrom unittest import TestCase\nfrom urllib.request import urlopen\nfrom time import sleep\n\n\nclass TestCaseGui(TestCase):\n    def __init__(self):\n        TestCase.__init__(self)\n        self.closestDiv = document.currentDiv()\n        self.divid = document.currentGradingContainer()\n        self.mydiv = document.getElementById(self.divid)\n        # If there is no div then create a dummy to avoid errors when running\n        # grading \"off screen\"\n        if self.mydiv is None:\n            self.mydiv = document.createElement(\"div\")\n            self.mydiv.setAttribute(\"id\", self.divid + \"_offscreen_unit_results\")\n            self.mydiv.setCSS(\"display\", \"none\")\n            body = document.getElementsByTagName(\"body\")[0]\n            body.appendChild(self.mydiv)\n            self.unit_results_divid = self.divid + \"_offscreen_unit_results\"\n\n        res = document.getElementById(self.divid + \"_unit_results\")\n        if res:\n            self.resdiv = res\n            res.innerHTML = \"\"\n            self.unit_results_divid = self.divid + \"_unit_results\"\n        else:\n            self.resdiv = document.createElement(\"div\")\n            self.resdiv.setAttribute(\"id\", self.divid + \"_unit_results\")\n            self.resdiv.setAttribute(\"class\", \"unittest-results\")\n            self.mydiv.appendChild(self.resdiv)\n            self.unit_results_divid = self.divid + \"_unit_results\"\n\n    def main(self):\n        t = document.createElement(\"table\")\n        self.resTable = t\n        x = self.resdiv.closest(\".timedComponent\")\n        if x:\n            self.is_timed = True\n        else:\n            self.is_timed = False\n        self.resdiv.appendChild(self.resTable)\n        if self.is_timed:\n            self.resdiv.setCSS(\"display\", \"none\")\n\n        headers = [\"Result\", \"Actual Value\", \"Expected Value\", \"Notes\"]\n        row = document.createElement(\"tr\")\n        for item in headers:\n            head = document.createElement(\"th\")\n            head.setAttribute(\"class\", \"ac-feedback\")\n            head.innerHTML = item\n            head.setCSS(\"text-align\", \"center\")\n            row.appendChild(head)\n        self.resTable.appendChild(row)\n\n        for func in self.tlist:\n            try:\n                self.setUp()\n                func()\n                self.tearDown()\n            except Exception as e:\n                self.appendResult(\"Error\", None, None, str(e).split(\"on line\")[0])\n                self.numFailed += 1\n        self.showSummary()\n\n    def getOutput(self):\n        sleep(0.2)\n        # self.divid will be the gradingWrapper when in grading mode\n        if self.closestDiv != self.divid:\n            output = document.querySelector(\n                \"#{} #{}_stdout\".format(self.divid, self.closestDiv)\n            )\n        else:\n            output = document.getElementById(self.divid + \"_stdout\")\n        return output.innerText\n\n    def getEditorText(self):\n        return document.getCurrentEditorValue()\n\n    def appendResult(self, res, actual, expected, param):\n        trimActual = False\n        if len(str(actual)) > 15:\n            trimActual = True\n            actualType = type(actual)\n        trimExpected = False\n        if len(str(expected)) > 15:\n            trimExpected = True\n            expectedType = type(expected)\n        row = document.createElement(\"tr\")\n        err = False\n        if res == \"Error\":\n            err = True\n            msg = \"Error: %s\" % param\n            errorData = document.createElement(\"td\")\n            errorData.setAttribute(\"class\", \"ac-feedback\")\n            errorData.innerHTML = \"ERROR\"\n            errorData.setCSS(\"background-color\", \"#de8e96\")\n            errorData.setCSS(\"text-align\", \"center\")\n            row.appendChild(errorData)\n        elif res:\n            passed = document.createElement(\"td\")\n            passed.setAttribute(\"class\", \"ac-feedback\")\n            passed.innerHTML = \"Pass\"\n            passed.setCSS(\"background-color\", \"#83d382\")\n            passed.setCSS(\"text-align\", \"center\")\n            row.appendChild(passed)\n            self.numPassed += 1\n        else:\n            fail = document.createElement(\"td\")\n            fail.setAttribute(\"class\", \"ac-feedback\")\n            fail.innerHTML = \"Fail\"\n            fail.setCSS(\"background-color\", \"#de8e96\")\n            fail.setCSS(\"text-align\", \"center\")\n            row.appendChild(fail)\n            self.numFailed += 1\n\n        act = document.createElement(\"td\")\n        act.setAttribute(\"class\", \"ac-feedback\")\n        if trimActual:\n            actHTML = str(actual)[:5] + \"...\" + str(actual)[-5:]\n            if actualType == str:\n                actHTML = repr(actHTML)\n            act.innerHTML = actHTML\n        else:\n            act.innerHTML = repr(actual)\n        act.setCSS(\"text-align\", \"center\")\n        row.appendChild(act)\n\n        expect = document.createElement(\"td\")\n        expect.setAttribute(\"class\", \"ac-feedback\")\n\n        if trimExpected:\n            expectedHTML = str(expected)[:5] + \"...\" + str(expected)[-5:]\n            if expectedType == str:\n                expectedHTML = repr(expectedHTML)\n            expect.innerHTML = expectedHTML\n        else:\n            expect.innerHTML = repr(expected)\n        expect.setCSS(\"text-align\", \"center\")\n        row.appendChild(expect)\n        inp = document.createElement(\"td\")\n        inp.setAttribute(\"class\", \"ac-feedback\")\n\n        if err:\n            inp.innerHTML = msg\n        else:\n            inp.innerHTML = param\n        inp.setCSS(\"text-align\", \"center\")\n        row.appendChild(inp)\n\n        if trimActual or trimExpected:\n            expandbutton = document.createElement(\"button\")\n            expandbutton.innerHTML = \"Expand Differences\"\n            expandmsg = \"Actual: \" + str(actual) + \"\\nExpected: \" + str(expected)\n            expandbutton.setAttribute(\"value\", expandmsg)\n            expandbutton.setAttribute(\"onclick\", \"alert(this.value)\")\n            expandbutton.setAttribute(\"class\", \"btn btn-info\")\n            row.appendChild(expandbutton)\n\n        self.resTable.appendChild(row)\n\n    def showSummary(self):\n        pct = float(self.numPassed) / (self.numPassed + self.numFailed) * 100\n        pctcorrect = (\n            \"percent:\"\n            + str(pct)\n            + \":passed:\"\n            + str(self.numPassed)\n            + \":failed:\"\n            + str(self.numFailed)\n        )\n        pTag = document.createElement(\"p\")\n        if not self.is_timed:\n            pTag.innerHTML = \"You passed: \" + str(pct) + \"% of the tests\"\n            self.resdiv.appendChild(pTag)\n        try:\n            jseval(\"window.edList['{}'].pct_correct = {}\".format(self.closestDiv, pct))\n            jseval(\n                \"window.edList['{}'].unit_results = '{}'\".format(\n                    self.closestDiv, pctcorrect\n                )\n            )\n            jseval(\n                \"window.edList['{}'].unit_results_divid = '{}'\".format(\n                    self.closestDiv, self.mydiv.getAttribute(\"id\")\n                )\n            )\n\n        except:\n            print(\n                \"failed to find object to record unittest results! {}\".format(\n                    pctcorrect\n                )\n            )\n","src/lib/urllib/__init__.js":"var $builtinmodule=function(){return{}};","src/lib/urllib/request/__init__.js":"var $builtinmodule=function(){var a={};a.Response=Sk.misceval.buildClass(a,function(a,b){b.__init__=new Sk.builtin.func(function(a,b){a.data$=b.responseText,a.lineList=a.data$.split(\"\\n\"),a.lineList=a.lineList.slice(0,-1);for(var c=0;c<a.lineList.length;c++)a.lineList[c]+=\"\\n\";a.currentLine=0,a.pos$=0}),b.__str__=new Sk.builtin.func(function(){return Sk.ffi.remapToPy(\"<Response>\")}),b.__iter__=new Sk.builtin.func(function(a){var b=a.lineList;return Sk.builtin.makeGenerator(function(){return this.$index>=this.$lines.length?void 0:new Sk.builtin.str(this.$lines[this.$index++])},{$obj:a,$index:0,$lines:b})}),b.read=new Sk.builtin.func(function(a,b){if(a.closed)throw new Sk.builtin.ValueError(\"I/O operation on closed file\");var c=a.data$.length;void 0===b&&(b=c);var d=new Sk.builtin.str(a.data$.substr(a.pos$,b));return a.pos$+=b,a.pos$>=c&&(a.pos$=c),d}),b.readline=new Sk.builtin.func(function(a){var b=\"\";return a.currentLine<a.lineList.length&&(b=a.lineList[a.currentLine],a.currentLine++),new Sk.builtin.str(b)}),b.readlines=new Sk.builtin.func(function(a){for(var b=[],c=a.currentLine;c<a.lineList.length;c++)b.push(new Sk.builtin.str(a.lineList[c]));return new Sk.builtin.list(b)})},\"Response\",[]);var b=function(a){var b;if(!Sk.jsonpSites)return!1;for(b=0;b<Sk.jsonpSites.length;b++)if(a.startsWith(Sk.jsonpSites[b]))return!0;return!1};return a.urlopen=new Sk.builtin.func(function(c,d){var e=b(c.v)?new Promise(function(b,d){var e=document.createElement(\"script\");e.src=c.v+\"&callback=Sk.jsonpcallback\",e.onerror=function(){d(\"An error occured getting the data\")},Sk.jsonpcallback=function(c){var d={responseText:JSON.stringify(c)};b(Sk.misceval.callsimArray(a.Response,[d]))};try{document.body.appendChild(e)}catch(a){console.log(\"caught error in urlopen\"+a)}}):new Promise(function(b){var e=new XMLHttpRequest;e.addEventListener(\"loadend\",function(){b(Sk.misceval.callsimArray(a.Response,[e]))}),d?(e.open(\"POST\",c.v),e.setRequestHeader(\"Content-type\",\"application/x-www-form-urlencoded\"),e.send(d.v)):(e.open(\"GET\",c.v),e.send(null))});var f=new Sk.misceval.Suspension;return f.resume=function(){return resolution},f.data={type:\"Sk.promise\",promise:e.then(function(a){return resolution=a,a},function(a){return resolution=\"\",a})},f}),a};","src/lib/urllib2.py":"raise NotImplementedError(\"urllib2 is not yet implemented in Skulpt\")\n","src/lib/urlparse.py":"raise NotImplementedError(\"urlparse is not yet implemented in Skulpt\")\n","src/lib/user.py":"raise NotImplementedError(\"user is not yet implemented in Skulpt\")\n","src/lib/uu.py":"raise NotImplementedError(\"uu is not yet implemented in Skulpt\")\n","src/lib/uuid.py":"raise NotImplementedError(\"uuid is not yet implemented in Skulpt\")\n","src/lib/warnings.py":"raise NotImplementedError(\"warnings is not yet implemented in Skulpt\")\n","src/lib/wave.py":"raise NotImplementedError(\"wave is not yet implemented in Skulpt\")\n","src/lib/weakref.py":"raise NotImplementedError(\"weakref is not yet implemented in Skulpt\")\n","src/lib/webbrowser.js":"var $builtinmodule=function(){function open_tab(a){return(Sk.builtin.pyCheckType(\"url\",\"string\",Sk.builtin.checkString(a)),!b)?Sk.builtin.bool.false$:(a=a.$jsstr(),window.open(a,\"_blank\"),Sk.builtin.bool.true$)}var a={},b=\"undefined\"!=typeof window&&\"undefined\"!=typeof window.navigator;return a.__name__=new Sk.builtin.str(\"webbrowser\"),a.open=new Sk.builtin.func(function open(a){return Sk.builtin.pyCheckArgsLen(\"open\",arguments.length+1,1,3),open_tab(a)}),a.open_new=new Sk.builtin.func(function open_new(a){return Sk.builtin.pyCheckArgsLen(\"open_new\",arguments.length,1,1),open_tab(a)}),a.open_new_tab=new Sk.builtin.func(function open_new_tab(a){return Sk.builtin.pyCheckArgsLen(\"open_new_tab\",arguments.length,1,1),open_tab(a)}),a.DefaultBrowser=Sk.misceval.buildClass(a,function dflbrowser(a,b){b.__init__=new Sk.builtin.func(function __init__(){return Sk.builtin.none.none$}),b.open=new Sk.builtin.func(function open(a,b){return Sk.builtin.pyCheckArgsLen(\"open\",arguments.length,2,4),open_tab(b)}),b.open_new=new Sk.builtin.func(function open_new(a,b){return Sk.builtin.pyCheckArgsLen(\"open_new\",arguments.length,2,2),open_tab(b)}),b.open_new_tab=new Sk.builtin.func(function open_new_tab(a,b){return Sk.builtin.pyCheckArgsLen(\"open_new_tab\",arguments.length,2,2),open_tab(b)})},\"DefaultBrowser\",[]),a.get=new Sk.builtin.func(function get(){return Sk.builtin.pyCheckArgsLen(\"get\",arguments.length,0,1),Sk.misceval.callsimArray(a.DefaultBrowser,[])}),a};","src/lib/webbrowser.py":"raise NotImplementedError(\"webbrowser is not yet implemented in Skulpt\")\n","src/lib/webgl/__init__.js":"var $builtinmodule=function(){var a={__name__:new Sk.builtin.str(\"webgl\")},c=function(a){return\"<table style=\\\"background-color: #8CE; width: 100%; height: 100%;\\\"><tr><td align=\\\"center\\\"><div style=\\\"display: table-cell; vertical-align: middle;\\\"><div style=\\\"\\\">\"+a+\"</div></div></td></tr></table>\"},d=\"This page requires a browser that supports WebGL.<br/><a href=\\\"http://get.webgl.org\\\">Click here to upgrade your browser.</a>\",e=function(a){for(var b=[\"webgl\",\"experimental-webgl\",\"webkit-3d\",\"moz-webgl\"],c=null,d=0;d<b.length;++d){try{c=a.getContext(b[d])}catch(a){}if(c)break}if(c){function returnFalse(){return!1}a.onselectstart=returnFalse,a.onmousedown=returnFalse}return c},f=function(a,f){var g=document.getElementById(a);if(f||(f=g.getElementsByTagName(\"canvas\")[0]),!f)return void(g.innerHTML=c(d));var h=e(f);if(!h){var i=navigator.userAgent.match(/(\\w+\\/.*? )/g),j={};try{for(var k=0;k<i.length;++k){for(var l=i[k].match(/(\\w+)/g),m=[],n=1;n<l.length;++n)m.push(parseInt(l[n]));j[l[0]]=m}}catch(a){}g.innerHTML=j.Chrome&&(7<j.Chrome[0]||7==j.Chrome[0]&&0<j.Chrome[1]||7==j.Chrome[0]&&0==j.Chrome[1]&&521<=j.Chrome[2])?c(\"It doesn't appear your computer can support WebGL.<br/><a href=\\\"http://get.webgl.org\\\">Click here for more information.</a>\"):c(d)}return h};return a.Context=Sk.misceval.buildClass(a,function(a,b){b.__init__=new Sk.builtin.func(function(a,b){var c=document.getElementById(b.v),d=f(b.v,c);if(!d)throw new Error(\"Your browser does not appear to support WebGL.\");for(var e in a.gl=d,d.__proto__)if(\"number\"==typeof d.__proto__[e])Sk.abstr.objectSetItem(a.$d,new Sk.builtin.str(e),d.__proto__[e]);else if(\"function\"==typeof d.__proto__[e])switch(e){case\"bufferData\":break;case\"clearColor\":break;case\"drawArrays\":break;case\"getAttribLocation\":break;case\"getUniformLocation\":break;case\"shaderSource\":break;case\"uniformMatrix4fv\":break;case\"vertexAttribPointer\":break;case\"viewport\":break;default:(function(b){Sk.abstr.objectSetItem(a.$d,new Sk.builtin.str(e),new Sk.builtin.func(function(){var a=d.__proto__[b];return a.apply(d,arguments)}))})(e);}d.clearColor(100/255,149/255,237/255,1),d.clear(d.COLOR_BUFFER_BIT)}),b.tp$getattr=Sk.builtin.object.prototype.GenericGetAttr,b.bufferData=new Sk.builtin.func(function(a,b,c,d){a.gl.bufferData(b,c.v,d)}),b.clearColor=new Sk.builtin.func(function(a,b,c,d,e){a.gl.clearColor(Sk.builtin.asnum$(b),Sk.builtin.asnum$(c),Sk.builtin.asnum$(d),Sk.builtin.asnum$(e))}),b.getAttribLocation=new Sk.builtin.func(function(a,b,c){return a.gl.getAttribLocation(b,c.v)}),b.getUniformLocation=new Sk.builtin.func(function(a,b,c){return a.gl.getUniformLocation(b,c.v)}),b.shaderSource=new Sk.builtin.func(function(a,b,c){a.gl.shaderSource(b,c.v)}),b.drawArrays=new Sk.builtin.func(function(a,b,c,d){a.gl.drawArrays(Sk.builtin.asnum$(b),Sk.builtin.asnum$(c),Sk.builtin.asnum$(d))}),b.vertexAttribPointer=new Sk.builtin.func(function(a,b,c,d,e,f,g){a.gl.vertexAttribPointer(b,Sk.builtin.asnum$(c),Sk.builtin.asnum$(d),e,Sk.builtin.asnum$(f),Sk.builtin.asnum$(g))}),b.viewport=new Sk.builtin.func(function(a,b,c,d,e){a.gl.viewport(Sk.builtin.asnum$(b),Sk.builtin.asnum$(c),Sk.builtin.asnum$(d),Sk.builtin.asnum$(e))}),b.uniformMatrix4fv=new Sk.builtin.func(function(a,b,c,d){a.gl.uniformMatrix4fv(Sk.builtin.asnum$(b),c,d.v)}),b.setDrawFunc=new Sk.builtin.func(function(a,b){var c=new Date().getTime(),d=setInterval(function(){Sk.misceval.callsimArray(b,[a,new Date().getTime()-c])},1e3/60)})},\"Context\",[]),a.Float32Array=Sk.misceval.buildClass(a,function(a,b){b.__init__=new Sk.builtin.func(function(a,b){a.v=\"number\"==typeof b?new Float32Array(b):new Float32Array(Sk.ffi.remapToJs(b))}),b.__repr__=new Sk.builtin.func(function(a){for(var b=[],c=0;c<a.v.length;++c)b.push(a.v[c]);return new Sk.builtin.str(\"[\"+b.join(\", \")+\"]\")})},\"Float32Array\",[]),a.Matrix4x4=Sk.misceval.buildClass(a,function(a,b){b.__init__=new Sk.builtin.func(function(a,b){a.v=new Float32Array(Sk.ffi.remapToJs(b))}),b.identity=new Sk.builtin.func(function(a){var b=a.v;b[0]=1,b[1]=0,b[2]=0,b[3]=0,b[4]=0,b[5]=1,b[6]=0,b[7]=0,b[8]=0,b[9]=0,b[10]=1,b[11]=0,b[12]=0,b[13]=0,b[14]=0,b[15]=1}),b.perspective=new Sk.builtin.func(function(b,c,d,e,g){var h=Math.tan,i=Math.PI,j=h(.5*i-.5*(Sk.builtin.asnum$(c)*i/180)),l=Sk.builtin.asnum$(d),a=Sk.builtin.asnum$(e),n=Sk.builtin.asnum$(g),f=1/(a-n),k=b.v;k[0]=j/l,k[1]=0,k[2]=0,k[3]=0,k[4]=0,k[5]=j,k[6]=0,k[7]=0,k[8]=0,k[9]=0,k[10]=(a+n)*f,k[11]=-1,k[12]=0,k[13]=0,k[14]=2*(a*n*f),k[15]=0}),b.translate=new Sk.builtin.func(function(a,b){var c=a.v,d=Sk.ffi.remapToJs(b);c[0]=1,c[1]=0,c[2]=0,c[3]=0,c[4]=0,c[5]=1,c[6]=0,c[7]=0,c[8]=0,c[9]=0,c[10]=1,c[11]=0,c[12]=d[0],c[13]=d[1],c[14]=d[2],c[15]=1}),b.__repr__=new Sk.builtin.func(function(a){for(var b=[],c=0;c<a.v.length;++c)b.push(a.v[c]);return new Sk.builtin.str(\"[\"+b.join(\", \")+\"]\")})},\"Matrix4x4\",[]),a};","src/lib/webgl/math.js":"var $builtinmodule=function(){var a={};return a.Mat44=Sk.misceval.buildClass(a,function(b,c){var d=Math.sqrt;c.__init__=new Sk.builtin.func(function(a){Sk.misceval.callsimArray(c.loadIdentity,[a]),a.stack=[]}),c.push=new Sk.builtin.func(function(a){a.stack.push(a.elements.slice(0))}),c.pop=new Sk.builtin.func(function(a){a.elements=a.stack.pop()}),c.loadIdentity=new Sk.builtin.func(function(a){a.elements=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]}),c.transform3=new Sk.builtin.func(function(b,c){var d=b.elements;return Sk.misceval.callsimArray(a.Vec3,[d[0]*c.x+d[4]*c.y+d[8]*c.z,d[1]*c.x+d[5]*c.y+d[9]*c.z,d[2]*c.x+d[6]*c.y+d[10]*c.z])}),c.scale=new Sk.builtin.func(function(a,b,c,d){return a.elements[0]*=b,a.elements[1]*=b,a.elements[2]*=b,a.elements[3]*=b,a.elements[4]*=c,a.elements[5]*=c,a.elements[6]*=c,a.elements[7]*=c,a.elements[8]*=d,a.elements[9]*=d,a.elements[10]*=d,a.elements[11]*=d,a}),c.translate=new Sk.builtin.func(function(a,b,c,d){return a.elements[12]+=a.elements[0]*b+a.elements[4]*c+a.elements[8]*d,a.elements[13]+=a.elements[1]*b+a.elements[5]*c+a.elements[9]*d,a.elements[14]+=a.elements[2]*b+a.elements[6]*c+a.elements[10]*d,a.elements[15]+=a.elements[3]*b+a.elements[7]*c+a.elements[11]*d,a}),c.rotate=new Sk.builtin.func(function(b,c,e,f,g){var h=Math.cos,i=Math.sin,j=Math.PI,k=d(e*e+f*f+g*g),l=i(c*j/180),m=h(c*j/180);if(0<k){var n,o,p,q,r,s,t,u,v,w,A;e/=k,f/=k,g/=k,n=e*e,o=f*f,p=g*g,q=e*f,r=f*g,s=g*e,t=e*l,u=f*l,v=g*l,w=1-m,A=Sk.misceval.callsimArray(a.Mat44),A.elements[0]=w*n+m,A.elements[1]=w*q-v,A.elements[2]=w*s+u,A.elements[3]=0,A.elements[4]=w*q+v,A.elements[5]=w*o+m,A.elements[6]=w*r-t,A.elements[7]=0,A.elements[8]=w*s-u,A.elements[9]=w*r+t,A.elements[10]=w*p+m,A.elements[11]=0,A.elements[12]=0,A.elements[13]=0,A.elements[14]=0,A.elements[15]=1,A=A.multiply(b),b.elements=A.elements}return b}),c.multiply=new Sk.builtin.func(function(b,c){for(var d=Sk.misceval.callsimArray(a.Mat44),e=0;4>e;e++)d.elements[4*e+0]=b.elements[4*e+0]*c.elements[0]+b.elements[4*e+1]*c.elements[4]+b.elements[4*e+2]*c.elements[8]+b.elements[4*e+3]*c.elements[12],d.elements[4*e+1]=b.elements[4*e+0]*c.elements[1]+b.elements[4*e+1]*c.elements[5]+b.elements[4*e+2]*c.elements[9]+b.elements[4*e+3]*c.elements[13],d.elements[4*e+2]=b.elements[4*e+0]*c.elements[2]+b.elements[4*e+1]*c.elements[6]+b.elements[4*e+2]*c.elements[10]+b.elements[4*e+3]*c.elements[14],d.elements[4*e+3]=b.elements[4*e+0]*c.elements[3]+b.elements[4*e+1]*c.elements[7]+b.elements[4*e+2]*c.elements[11]+b.elements[4*e+3]*c.elements[15];return b.elements=d.elements,b}),c.lookAt=new Sk.builtin.func(function(b,c,e,f,g,h,i,j,k,l){var m=[c-g,e-h,f-i],n=d(m[0]*m[0]+m[1]*m[1]+m[2]*m[2]);n&&(m[0]/=n,m[1]/=n,m[2]/=n);var o=[j,k,l],p=[];p[0]=o[1]*m[2]-o[2]*m[1],p[1]=-o[0]*m[2]+o[2]*m[0],p[2]=o[0]*m[1]-o[1]*m[0],o[0]=m[1]*p[2]-m[2]*p[1],o[1]=-m[0]*p[2]+m[2]*p[0],o[2]=m[0]*p[1]-m[1]*p[0],n=d(p[0]*p[0]+p[1]*p[1]+p[2]*p[2]),n&&(p[0]/=n,p[1]/=n,p[2]/=n),n=d(o[0]*o[0]+o[1]*o[1]+o[2]*o[2]),n&&(o[0]/=n,o[1]/=n,o[2]/=n);var q=Sk.misceval.callsimArray(a.Mat44);return q.elements[0]=p[0],q.elements[4]=p[1],q.elements[8]=p[2],q.elements[12]=0,q.elements[1]=o[0],q.elements[5]=o[1],q.elements[9]=o[2],q.elements[13]=0,q.elements[2]=m[0],q.elements[6]=m[1],q.elements[10]=m[2],q.elements[14]=0,q.elements[3]=0,q.elements[7]=0,q.elements[11]=0,q.elements[15]=1,q=q.multiply(b),b.elements=q.elements,b.translate(-c,-e,-f),b})},\"Mat44\",[]),a.Mat33=Sk.misceval.buildClass(a,function(a,b){b.__init__=new Sk.builtin.func(function(a){Sk.misceval.callsimArray(b.loadIdentity,[a])}),b.loadIdentity=new Sk.builtin.func(function(a){a.elements=[1,0,0,0,1,0,0,0,1]})},\"Mat33\",[]),a.Vec3=Sk.misceval.buildClass(a,function(b,c){c.__init__=new Sk.builtin.func(function(a,b,c,d){a.x=b,a.y=c,a.z=d}),c.__sub__=new Sk.builtin.func(function(b,c){return Sk.misceval.callsimArray(a.Vec3,[b.x-c.x,b.y-c.y,b.z-c.z])})},\"Vec3\",[]),a.cross=new Sk.builtin.func(function(b,c){return Sk.asserts.assert(b instanceof a.Vec3&&c instanceof a.Vec3),Sk.misceval.callsimArray(a.Vec3,[b.y*c.z-b.z*c.y,b.z*c.x-b.x*c.z,b.x*c.y-b.y*c.x])}),a};","src/lib/webgl/matrix4.js":"var $builtinmodule=function(){var a=Math.PI,b={},c=new Float32Array(3),d=new Float32Array(3),e=new Float32Array(3),f=new Float32Array(4),g=new Float32Array(4),h=new Float32Array(4),i=new Float32Array(16),j=new Float32Array(16),k=new Float32Array(16),l=function(b,c){for(var a=Math.sqrt,d=0,e=c.length,f=0;f<e;++f)d+=c[f]*c[f];if(d=a(d),1e-5<d)for(var f=0;f<e;++f)b[f]=c[f]/d;else for(var f=0;f<e;++f)b[f]=0;return b},m=function(c,d,a){return c[0]=d[1]*a[2]-d[2]*a[1],c[1]=d[2]*a[0]-d[0]*a[2],c[2]=d[0]*a[1]-d[1]*a[0],c},n=function(c,d,a){for(var b=d.length,e=0;e<b;++e)c[e]=d[e]-a[e];return c},o=function(c,a){return c[0]*a[0]+c[1]*a[1]+c[2]*a[2]};return b.lookAt=new Sk.builtin.func(function(a,b,f,g){var h=c,i=d,j=l(h,n(h,b.v,f.v)),k=l(i,m(i,g.v,j)),p=m(e,j,k),q=a.v;return q[0]=k[0],q[1]=p[0],q[2]=j[0],q[3]=0,q[4]=k[1],q[5]=p[1],q[6]=j[1],q[7]=0,q[8]=k[2],q[9]=p[2],q[10]=j[2],q[11]=0,q[12]=-o(k,b.v),q[13]=-o(p,b.v),q[14]=-o(j,b.v),q[15]=1,a}),b.perspective=new Sk.builtin.func(function(b,c,d,e,g){var h=Math.tan,i=h(.5*a-.5*(c*a/180)),f=1/(e-g),j=b.v;return j[0]=i/d,j[1]=0,j[2]=0,j[3]=0,j[4]=0,j[5]=i,j[6]=0,j[7]=0,j[8]=0,j[9]=0,j[10]=(e+g)*f,j[11]=-1,j[12]=0,j[13]=0,j[14]=2*(e*g*f),j[15]=0,b}),b.rotationY=new Sk.builtin.func(function(b,d){var e=Math.sin,f=Math.cos,g=b.v,h=f(d*a/180),c=e(d*a/180);return g[0]=h,g[1]=0,g[2]=-c,g[3]=0,g[4]=0,g[5]=1,g[6]=0,g[7]=0,g[8]=c,g[9]=0,g[10]=h,g[11]=0,g[12]=0,g[13]=0,g[14]=0,g[15]=1,b}),b.identity=new Sk.builtin.func(function(a){var b=a.v;return b[0]=1,b[1]=0,b[2]=0,b[3]=0,b[4]=0,b[5]=1,b[6]=0,b[7]=0,b[8]=0,b[9]=0,b[10]=1,b[11]=0,b[12]=0,b[13]=0,b[14]=0,b[15]=1,a}),b.mul=new Sk.builtin.func(function(c,d,e){var f=c.v,g=d.v,a=e.v,b=g[0],h=g[1],i=g[2],j=g[3],k=g[4],l=g[5],m=g[6],n=g[7],o=g[8],p=g[9],q=g[10],r=g[11],s=g[12],t=g[13],u=g[14],v=g[15],w=a[0],x=a[1],y=a[2],z=a[3],A=a[4],B=a[5],C=a[6],D=a[7],E=a[8],F=a[9],G=a[10],H=a[11],I=a[12],J=a[13],K=a[14],L=a[15];return f[0]=b*w+h*A+i*E+j*I,f[1]=b*x+h*B+i*F+j*J,f[2]=b*y+h*C+i*G+j*K,f[3]=b*z+h*D+i*H+j*L,f[4]=k*w+l*A+m*E+n*I,f[5]=k*x+l*B+m*F+n*J,f[6]=k*y+l*C+m*G+n*K,f[7]=k*z+l*D+m*H+n*L,f[8]=o*w+p*A+q*E+r*I,f[9]=o*x+p*B+q*F+r*J,f[10]=o*y+p*C+q*G+r*K,f[11]=o*z+p*D+q*H+r*L,f[12]=s*w+t*A+u*E+v*I,f[13]=s*x+t*B+u*F+v*J,f[14]=s*y+t*C+u*G+v*K,f[15]=s*z+t*D+u*H+v*L,c}),b.invert=new Sk.builtin.func(function(a,b){var c=a.v,e=b.v,f=e[0],g=e[1],h=e[2],i=e[3],j=e[4],k=e[5],l=e[6],m=e[7],n=e[8],o=e[9],p=e[10],q=e[11],r=e[12],s=e[13],t=e[14],u=e[15],v=p*u,w=t*q,x=l*u,y=t*m,z=l*q,A=p*m,B=h*u,C=t*i,D=h*q,E=p*i,F=h*m,G=l*i,H=n*s,I=r*o,J=j*s,K=r*k,L=j*o,M=n*k,N=f*s,O=r*g,P=f*o,Q=n*g,R=f*k,S=j*g,T=v*k+y*o+z*s-(w*k+x*o+A*s),U=w*g+B*o+E*s-(v*g+C*o+D*s),V=x*g+C*k+F*s-(y*g+B*k+G*s),W=A*g+D*k+G*o-(z*g+E*k+F*o),X=1/(f*T+j*U+n*V+r*W);return c[0]=X*T,c[1]=X*U,c[2]=X*V,c[3]=X*W,c[4]=X*(w*j+x*n+A*r-(v*j+y*n+z*r)),c[5]=X*(v*f+C*n+D*r-(w*f+B*n+E*r)),c[6]=X*(y*f+B*j+G*r-(x*f+C*j+F*r)),c[7]=X*(z*f+E*j+F*n-(A*f+D*j+G*n)),c[8]=X*(H*m+K*q+L*u-(I*m+J*q+M*u)),c[9]=X*(I*i+N*q+Q*u-(H*i+O*q+P*u)),c[10]=X*(J*i+O*m+R*u-(K*i+N*m+S*u)),c[11]=X*(M*i+P*m+S*q-(L*i+Q*m+R*q)),c[12]=X*(J*p+M*t+I*l-(L*t+H*l+K*p)),c[13]=X*(P*t+H*h+O*p-(N*p+Q*t+I*h)),c[14]=X*(N*l+S*t+K*h-(R*t+J*h+O*l)),c[15]=X*(R*p+L*h+Q*l-(P*l+S*p+M*h)),a}),b.transpose=new Sk.builtin.func(function(a,b){for(var c=a.v,d=b.v,e=0;4>e;++e)for(var f=0;4>f;++f)c[4*e+f]=d[4*f+e];return c}),b};","src/lib/webgl/models.js":"var $builtinmodule=function(a){var c={},d=function(a,c){var d=c||gl.ARRAY_BUFFER,e=gl.createBuffer();if(this.target=d,this.buf=e,this.set(a),this.numComponents_=a.numComponents,this.numElements_=a.numElements,this.totalComponents_=this.numComponents_*this.numElements_,a.buffer instanceof Float32Array)this.type_=gl.FLOAT;else if(a.buffer instanceof Uint8Array)this.type_=gl.UNSIGNED_BYTE;else if(a.buffer instanceof Int8Array)this.type_=gl._BYTE;else if(a.buffer instanceof Uint16Array)this.type_=gl.UNSIGNED_SHORT;else if(a.buffer instanceof Int16Array)this.type_=gl.SHORT;else throw\"unhandled type:\"+typeof a.buffer};return d.prototype.set=function(a){gl.bindBuffer(this.target,this.buf),gl.bufferData(this.target,a.buffer,gl.STATIC_DRAW)},d.prototype.type=function(){return this.type_},d.prototype.numComponents=function(){return this.numComponents_},d.prototype.numElements=function(){return this.numElements_},d.prototype.totalComponents=function(){return this.totalComponents_},d.prototype.buffer=function(){return this.buf},d.prototype.stride=function(){return 0},d.prototype.offset=function(){return 0},c.Model=Sk.misceval.buildClass(c,function(c,e){e.__init__=new Sk.builtin.func(function(c,e,f,g){c.buffers={};var h=function(a,e){var f=\"indices\"==a?gl.ELEMENT_ARRAY_BUFFER:gl.ARRAY_BUFFER;b=c.buffers[a],b?b.set(e):b=new d(e,f),c.buffers[a]=b};for(a in f)h(a,f[a]);var i={},j=0;for(var k in g)i[k]=j++;c.mode=gl.TRIANGLES,c.textures=g.v,c.textureUnits=i,c.shader=e}),e.drawPrep=new Sk.builtin.func(function(a,c){var d=a.shader,e=a.buffers,f=a.textures;for(var g in c=Sk.ffi.remapToJs(c),Sk.misceval.callsimArray(d.use,[d]),e){var h=e[g];if(\"indices\"==g)gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,h.buffer());else{var i=d.attrib[g];i&&i(h)}}for(var j in f){var k=a.textureUnits[j];d.setUniform$impl(d,textuer,k),f[j].bindToUnit(k)}for(var l in c)d.setUniform$impl(d,l,c[l])}),e.draw=new Sk.builtin.func(function(a,c,d){var e=a.shader;for(uniform in c=Sk.ffi.remapToJs(c),c)e.setUniform$impl(e,uniform,c[uniform]);if(d)for(var f in d){var g=a.textureUnits[f];e.setUniform$impl(e,f,g),d[f].bindToUnit(g)}var h=a.buffers;gl.drawElements(a.mode,h.indices.totalComponents(),gl.UNSIGNED_SHORT,0)})},\"Model\",[]),c};","src/lib/webgl/primitives.js":"var $builtinmodule=function(){var a={},b=function(a,b,c){c=c||\"Float32Array\";var d=window[c];b.length?(this.buffer=new d(b),b=this.buffer.length/a,this.cursor=b):(this.buffer=new d(a*b),this.cursor=0),this.numComponents=a,this.numElements=b,this.type=c};return b.prototype.stride=function(){return 0},b.prototype.offset=function(){return 0},b.prototype.getElement=function(a){for(var b=a*this.numComponents,c=[],d=0;d<this.numComponents;++d)c.push(this.buffer[b+d]);return c},b.prototype.setElement=function(a,b){for(var c=a*this.numComponents,d=0;d<this.numComponents;++d)this.buffer[c+d]=b[d]},b.prototype.clone=function(){var a=new b(this.numComponents,this.numElements,this.type);return a.pushArray(this),a},b.prototype.push=function(a){this.setElement(this.cursor++,a)},b.prototype.pushArray=function(a){for(var b=0;b<a.numElements;++b)this.push(a.getElement(b))},b.prototype.pushArrayWithOffset=function(a,b){for(var c,d=0;d<a.numElements;++d){c=a.getElement(d);for(var e=0;e<b.length;++e)c[e]+=b[e];this.push(c)}},b.prototype.computeExtents=function(){for(var a=Math.max,b=Math.min,c,d=this.numElements,e=this.numComponents,f=this.getElement(0),g=this.getElement(0),h=1;h<d;++h){c=this.getElement(h);for(var i=0;i<e;++i)f[i]=b(f[i],c[i]),g[i]=a(g[i],c[i])}return{min:f,max:g}},a.createCube=new Sk.builtin.func(function(a){for(var c,d=[[3,7,5,1],[0,4,6,2],[6,7,3,2],[0,1,5,4],[5,7,6,4],[2,3,1,0]],e=a/2,g=[[-e,-e,-e],[+e,-e,-e],[-e,+e,-e],[+e,+e,-e],[-e,-e,+e],[+e,-e,+e],[-e,+e,+e],[+e,+e,+e]],h=[[1,0,0],[-1,0,0],[0,1,0],[0,-1,0],[0,0,1],[0,0,-1]],i=[[0,0],[1,0],[1,1],[0,1]],j=24,k=new b(3,j),l=new b(3,j),m=new b(2,j),n=new b(3,12,\"Uint16Array\"),o=0;6>o;++o){c=d[o];for(var p=0;4>p;++p){var q=g[c[p]],r=h[o],s=i[p];k.push(q),l.push(r),m.push(s)}var t=4*o;n.push([t+0,t+1,t+2]),n.push([t+0,t+2,t+3])}return{position:k,normal:l,texCoord:m,indices:n}}),a};","src/lib/whichdb.py":"raise NotImplementedError(\"whichdb is not yet implemented in Skulpt\")\n","src/lib/wsgiref/__init__.py":"raise NotImplementedError(\"wsgiref is not yet implemented in Skulpt\")\n","src/lib/xdrlib.py":"raise NotImplementedError(\"xdrlib is not yet implemented in Skulpt\")\n","src/lib/xml/__init__.py":"raise NotImplementedError(\"xml is not yet implemented in Skulpt\")\n","src/lib/xml/dom/__init__.py":"raise NotImplementedError(\"dom is not yet implemented in Skulpt\")\n","src/lib/xml/etree/__init__.py":"raise NotImplementedError(\"etree is not yet implemented in Skulpt\")\n","src/lib/xml/parsers/__init__.py":"raise NotImplementedError(\"parsers is not yet implemented in Skulpt\")\n","src/lib/xml/sax/__init__.py":"raise NotImplementedError(\"sax is not yet implemented in Skulpt\")\n","src/lib/xmllib.py":"raise NotImplementedError(\"xmllib is not yet implemented in Skulpt\")\n","src/lib/xmlrpclib.py":"raise NotImplementedError(\"xmlrpclib is not yet implemented in Skulpt\")\n","src/lib/zipfile.py":"raise NotImplementedError(\"zipfile is not yet implemented in Skulpt\")\n"}}

/***/ }),

/***/ "./runestone/activecode/js/skulpt.min.js":
/*!***********************************************!*\
  !*** ./runestone/activecode/js/skulpt.min.js ***!
  \***********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(global) {(function(){/*
 https://mths.be/fromcodepoint v0.2.1 by @mathias */
'use strict';var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(m,p,a){m!=Array.prototype&&m!=Object.prototype&&(m[p]=a.value)};$jscomp.getGlobal=function(m){return"undefined"!=typeof window&&window===m?m:"undefined"!=typeof global&&null!=global?global:m};$jscomp.global=$jscomp.getGlobal(this);
$jscomp.polyfill=function(m,p,a,b){if(p){a=$jscomp.global;m=m.split(".");for(b=0;b<m.length-1;b++){var c=m[b];c in a||(a[c]={});a=a[c]}m=m[m.length-1];b=a[m];p=p(b);p!=b&&null!=p&&$jscomp.defineProperty(a,m,{configurable:!0,writable:!0,value:p})}};$jscomp.polyfill("Array.prototype.includes",function(m){return m?m:function(m,a){var b=this;b instanceof String&&(b=String(b));var c=b.length;a=a||0;for(0>a&&(a=Math.max(a+c,0));a<c;a++){var d=b[a];if(d===m||Object.is(d,m))return!0}return!1}},"es7","es3");
$jscomp.owns=function(m,p){return Object.prototype.hasOwnProperty.call(m,p)};$jscomp.polyfill("Object.values",function(m){return m?m:function(m){var a=[],b;for(b in m)$jscomp.owns(m,b)&&a.push(m[b]);return a}},"es8","es3");$jscomp.arrayIteratorImpl=function(m){var p=0;return function(){return p<m.length?{done:!1,value:m[p++]}:{done:!0}}};$jscomp.arrayIterator=function(m){return{next:$jscomp.arrayIteratorImpl(m)}};$jscomp.SYMBOL_PREFIX="jscomp_symbol_";
$jscomp.initSymbol=function(){$jscomp.initSymbol=function(){};$jscomp.global.Symbol||($jscomp.global.Symbol=$jscomp.Symbol)};$jscomp.SymbolClass=function(m,p){this.$jscomp$symbol$id_=m;$jscomp.defineProperty(this,"description",{configurable:!0,writable:!0,value:p})};$jscomp.SymbolClass.prototype.toString=function(){return this.$jscomp$symbol$id_};
$jscomp.Symbol=function(){function m(a){if(this instanceof m)throw new TypeError("Symbol is not a constructor");return new $jscomp.SymbolClass($jscomp.SYMBOL_PREFIX+(a||"")+"_"+p++,a)}var p=0;return m}();
$jscomp.initSymbolIterator=function(){$jscomp.initSymbol();var m=$jscomp.global.Symbol.iterator;m||(m=$jscomp.global.Symbol.iterator=$jscomp.global.Symbol("Symbol.iterator"));"function"!=typeof Array.prototype[m]&&$jscomp.defineProperty(Array.prototype,m,{configurable:!0,writable:!0,value:function(){return $jscomp.iteratorPrototype($jscomp.arrayIteratorImpl(this))}});$jscomp.initSymbolIterator=function(){}};
$jscomp.initSymbolAsyncIterator=function(){$jscomp.initSymbol();var m=$jscomp.global.Symbol.asyncIterator;m||(m=$jscomp.global.Symbol.asyncIterator=$jscomp.global.Symbol("Symbol.asyncIterator"));$jscomp.initSymbolAsyncIterator=function(){}};$jscomp.iteratorPrototype=function(m){$jscomp.initSymbolIterator();m={next:m};m[$jscomp.global.Symbol.iterator]=function(){return this};return m};
$jscomp.iteratorFromArray=function(m,p){$jscomp.initSymbolIterator();m instanceof String&&(m+="");var a=0,b={next:function(){if(a<m.length){var c=a++;return{value:p(c,m[c]),done:!1}}b.next=function(){return{done:!0,value:void 0}};return b.next()}};b[Symbol.iterator]=function(){return b};return b};$jscomp.polyfill("Array.prototype.values",function(m){return m?m:function(){return $jscomp.iteratorFromArray(this,function(m,a){return a})}},"es8","es3");
(function(m){function p(b){if(a[b])return a[b].exports;var c=a[b]={i:b,l:!1,exports:{}};m[b].call(c.exports,c,c.exports,p);c.l=!0;return c.exports}var a={};p.m=m;p.c=a;p.d=function(a,c,d){p.o(a,c)||Object.defineProperty(a,c,{enumerable:!0,get:d})};p.r=function(a){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(a,Symbol.toStringTag,{value:"Module"});Object.defineProperty(a,"__esModule",{value:!0})};p.t=function(a,c){c&1&&(a=p(a));if(c&8)return a;if(c&4&&"object"===typeof a&&
a&&a.__esModule)return a;var b=Object.create(null);p.r(b);Object.defineProperty(b,"default",{enumerable:!0,value:a});if(c&2&&"string"!=typeof a)for(var e in a)p.d(b,e,function(c){return a[c]}.bind(null,e));return b};p.n=function(a){var c=a&&a.__esModule?function(){return a["default"]}:function(){return a};p.d(c,"a",c);return c};p.o=function(a,c){return Object.prototype.hasOwnProperty.call(a,c)};p.p="";return p(p.s=1)})([function(m,p){p=function(){return this}();try{p=p||(new Function("return this"))()}catch(a){"object"===
typeof window&&(p=window)}m.exports=p},function(m,p,a){a(2);Sk.global.strftime=a(3);Sk.global.strptime=a(4);a(5);a(7);a(8);a(9);a(10);a(11);a(12);a(13);a(14);a(15);a(16);a(17);a(18);a(19);a(20);a(21);a(23);a(24);a(25);a(26);a(27);a(28);a(29);a(30);a(31);a(32);a(33);a(34);a(35);a(36);a(37);a(38);a(39);a(40);a(41);a(42);a(43);a(44);a(45);a(46);a(47);a(48);a(49);a(50);a(51);a(52);a(53);a(54);a(55);a(56);a(57);a(58);a(59);a(60);a(61);a(62);a(63)},function(m,p,a){(function(a){var c={build:{githash:"a1d87961168fd8d786e137aa0a2d3e849811664d",
date:"2020-11-24T21:07:10.663Z"}};c.global="undefined"!==typeof a?a:"undefined"!==typeof self?self:"undefined"!==typeof window?window:{};c.exportSymbol=function(a,b){a=a.split(".");var d=c.global,e;for(e=0;e<a.length-1;e++){var f=a[e];d=d.hasOwnProperty(f)?d[f]:d[f]={}}"undefined"!==typeof b&&(f=a[e],d[f]=b)};c.isArrayLike=function(a){return a instanceof Array||a&&a.length&&"number"==typeof a.length?!0:!1};c.js_beautify=function(a){return a};c.exportSymbol("Sk",c);c.exportSymbol("Sk.global",c.global);
c.exportSymbol("Sk.build",c.build);c.exportSymbol("Sk.exportSymbol",c.exportSymbol);c.exportSymbol("Sk.isArrayLike",c.isArrayLike);c.exportSymbol("Sk.js_beautify",c.js_beautify)}).call(this,a(0))},function(m,p){(function(){function a(n,l,z){function u(a,f,k,n){for(var l="",z=null,y=!1,t=a.length,H=!1,m=0;m<t;m++){var C=a.charCodeAt(m);if(!0===y)if(45===C)z="";else if(95===C)z=" ";else if(48===C)z="0";else if(58===C)H&&g("[WARNING] detected use of unsupported %:: or %::: modifiers to strftime"),H=
!0;else{switch(C){case 37:l+="%";break;case 65:l+=k.days[f.getDay()];break;case 66:l+=k.months[f.getMonth()];break;case 67:l+=b(Math.floor(f.getFullYear()/100),z);break;case 68:l+=u(k.formats.D,f,k,n);break;case 70:l+=u(k.formats.F,f,k,n);break;case 72:l+=b(f.getHours(),z);break;case 73:l+=b(d(f.getHours()),z);break;case 76:l+=c(Math.floor(n%1E3));break;case 77:l+=b(f.getMinutes(),z);break;case 80:l+=12>f.getHours()?k.am:k.pm;break;case 82:l+=u(k.formats.R,f,k,n);break;case 83:l+=b(f.getSeconds(),
z);break;case 84:l+=u(k.formats.T,f,k,n);break;case 85:l+=b(e(f,"sunday"),z);break;case 87:l+=b(e(f,"monday"),z);break;case 88:l+=u(k.formats.X,f,k,n);break;case 89:l+=f.getFullYear();break;case 90:K&&0===q?l+="GMT":(z=f.toString().match(/\(([\w\s]+)\)/),l+=z&&z[1]||"");break;case 97:l+=k.shortDays[f.getDay()];break;case 98:l+=k.shortMonths[f.getMonth()];break;case 99:l+=u(k.formats.c,f,k,n);break;case 100:l+=b(f.getDate(),z);break;case 101:l+=b(f.getDate(),null==z?" ":z);break;case 104:l+=k.shortMonths[f.getMonth()];
break;case 106:z=new Date(f.getFullYear(),0,1);z=Math.ceil((f.getTime()-z.getTime())/864E5);l+=c(z);break;case 107:l+=b(f.getHours(),null==z?" ":z);break;case 108:l+=b(d(f.getHours()),null==z?" ":z);break;case 109:l+=b(f.getMonth()+1,z);break;case 110:l+="\n";break;case 111:z=f.getDate();l=k.ordinalSuffixes?l+(String(z)+(k.ordinalSuffixes[z-1]||h(z))):l+(String(z)+h(z));break;case 112:l+=12>f.getHours()?k.AM:k.PM;break;case 114:l+=u(k.formats.r,f,k,n);break;case 115:l+=Math.floor(n/1E3);break;case 116:l+=
"\t";break;case 117:z=f.getDay();l+=0===z?7:z;break;case 118:l+=u(k.formats.v,f,k,n);break;case 119:l+=f.getDay();break;case 120:l+=u(k.formats.x,f,k,n);break;case 121:l+=(""+f.getFullYear()).slice(2);break;case 122:K&&0===q?l+=H?"+00:00":"+0000":(z=0!==q?q/6E4:-f.getTimezoneOffset(),y=H?":":"",C=Math.abs(z%60),l+=(0>z?"-":"+")+b(Math.floor(Math.abs(z/60)))+y+b(C));break;default:y&&(l+="%"),l+=a[m]}z=null;y=!1}else 37===C?y=!0:l+=a[m]}return l}var t=n||k,q=l||0,K=z||!1,m=0,F,L=function(a,c){if(c){var b=
c.getTime();if(K){var d=6E4*(c.getTimezoneOffset()||0);c=new Date(b+d+q);6E4*(c.getTimezoneOffset()||0)!==d&&(c=6E4*(c.getTimezoneOffset()||0),c=new Date(b+c+q))}}else b=Date.now(),b>m?(m=b,F=new Date(m),b=m,K&&(F=new Date(m+6E4*(F.getTimezoneOffset()||0)+q))):b=m,c=F;return u(a,c,t,b)};L.localize=function(c){return new a(c||t,q,K)};L.localizeByIdentifier=function(a){var c=f[a];return c?L.localize(c):(g('[WARNING] No locale found with identifier "'+a+'".'),L)};L.timezone=function(c){var b=q,d=K,f=
typeof c;if("number"===f||"string"===f)d=!0,"string"===f?(b="-"===c[0]?-1:1,f=parseInt(c.slice(1,3),10),c=parseInt(c.slice(3,5),10),b=b*(60*f+c)*6E4):"number"===f&&(b=6E4*c);return new a(t,b,d)};L.utc=function(){return new a(t,q,!0)};return L}function b(a,c){if(""===c||9<a)return a;null==c&&(c="0");return c+a}function c(a){return 99<a?a:9<a?"0"+a:"00"+a}function d(a){return 0===a?12:12<a?a-12:a}function e(a,c){c=c||"sunday";var b=a.getDay();"monday"===c&&(0===b?b=6:b--);c=Date.UTC(a.getFullYear(),
0,1);a=Date.UTC(a.getFullYear(),a.getMonth(),a.getDate());return Math.floor((Math.floor((a-c)/864E5)+7-b)/7)}function h(a){var c=a%10;a%=100;if(11<=a&&13>=a||0===c||4<=c)return"th";switch(c){case 1:return"st";case 2:return"nd";case 3:return"rd"}}function g(a){"undefined"!==typeof console&&"function"==typeof console.warn&&console.warn(a)}var f={de_DE:{days:"Sonntag Montag Dienstag Mittwoch Donnerstag Freitag Samstag".split(" "),shortDays:"So Mo Di Mi Do Fr Sa".split(" "),months:"Januar Februar M\u00e4rz April Mai Juni Juli August September Oktober November Dezember".split(" "),
shortMonths:"Jan Feb M\u00e4r Apr Mai Jun Jul Aug Sep Okt Nov Dez".split(" "),AM:"AM",PM:"PM",am:"am",pm:"pm",formats:{c:"%a %d %b %Y %X %Z",D:"%d.%m.%Y",F:"%Y-%m-%d",R:"%H:%M",r:"%I:%M:%S %p",T:"%H:%M:%S",v:"%e-%b-%Y",X:"%T",x:"%D"}},en_CA:{days:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),shortDays:"Sun Mon Tue Wed Thu Fri Sat".split(" "),months:"January February March April May June July August September October November December".split(" "),shortMonths:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),
ordinalSuffixes:"st nd rd th th th th th th th th th th th th th th th th th st nd rd th th th th th th th st".split(" "),AM:"AM",PM:"PM",am:"am",pm:"pm",formats:{c:"%a %d %b %Y %X %Z",D:"%d/%m/%y",F:"%Y-%m-%d",R:"%H:%M",r:"%I:%M:%S %p",T:"%H:%M:%S",v:"%e-%b-%Y",X:"%r",x:"%D"}},en_US:{days:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),shortDays:"Sun Mon Tue Wed Thu Fri Sat".split(" "),months:"January February March April May June July August September October November December".split(" "),
shortMonths:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),ordinalSuffixes:"st nd rd th th th th th th th th th th th th th th th th th st nd rd th th th th th th th st".split(" "),AM:"AM",PM:"PM",am:"am",pm:"pm",formats:{c:"%a %d %b %Y %X %Z",D:"%m/%d/%y",F:"%Y-%m-%d",R:"%H:%M",r:"%I:%M:%S %p",T:"%H:%M:%S",v:"%e-%b-%Y",X:"%r",x:"%D"}},es_MX:{days:"domingo lunes martes mi\u00e9rcoles jueves viernes s\u00e1bado".split(" "),shortDays:"dom lun mar mi\u00e9 jue vie s\u00e1b".split(" "),
months:"enero;febrero;marzo;abril;mayo;junio;julio;agosto;septiembre;octubre;noviembre; diciembre".split(";"),shortMonths:"ene feb mar abr may jun jul ago sep oct nov dic".split(" "),AM:"AM",PM:"PM",am:"am",pm:"pm",formats:{c:"%a %d %b %Y %X %Z",D:"%d/%m/%Y",F:"%Y-%m-%d",R:"%H:%M",r:"%I:%M:%S %p",T:"%H:%M:%S",v:"%e-%b-%Y",X:"%T",x:"%D"}},fr_FR:{days:"dimanche lundi mardi mercredi jeudi vendredi samedi".split(" "),shortDays:"dim. lun. mar. mer. jeu. ven. sam.".split(" "),months:"janvier f\u00e9vrier mars avril mai juin juillet ao\u00fbt septembre octobre novembre d\u00e9cembre".split(" "),
shortMonths:"janv. f\u00e9vr. mars avril mai juin juil. ao\u00fbt sept. oct. nov. d\u00e9c.".split(" "),AM:"AM",PM:"PM",am:"am",pm:"pm",formats:{c:"%a %d %b %Y %X %Z",D:"%d/%m/%Y",F:"%Y-%m-%d",R:"%H:%M",r:"%I:%M:%S %p",T:"%H:%M:%S",v:"%e-%b-%Y",X:"%T",x:"%D"}},it_IT:{days:"domenica luned\u00ec marted\u00ec mercoled\u00ec gioved\u00ec venerd\u00ec sabato".split(" "),shortDays:"dom lun mar mer gio ven sab".split(" "),months:"gennaio febbraio marzo aprile maggio giugno luglio agosto settembre ottobre novembre dicembre".split(" "),
shortMonths:"pr mag giu lug ago set ott nov dic".split(" "),AM:"AM",PM:"PM",am:"am",pm:"pm",formats:{c:"%a %d %b %Y %X %Z",D:"%d/%m/%Y",F:"%Y-%m-%d",R:"%H:%M",r:"%I:%M:%S %p",T:"%H:%M:%S",v:"%e-%b-%Y",X:"%T",x:"%D"}},nl_NL:{days:"zondag maandag dinsdag woensdag donderdag vrijdag zaterdag".split(" "),shortDays:"zo ma di wo do vr za".split(" "),months:"januari februari maart april mei juni juli augustus september oktober november december".split(" "),shortMonths:"jan feb mrt apr mei jun jul aug sep okt nov dec".split(" "),
AM:"AM",PM:"PM",am:"am",pm:"pm",formats:{c:"%a %d %b %Y %X %Z",D:"%d-%m-%y",F:"%Y-%m-%d",R:"%H:%M",r:"%I:%M:%S %p",T:"%H:%M:%S",v:"%e-%b-%Y",X:"%T",x:"%D"}},pt_BR:{days:"domingo segunda ter\u00e7a quarta quinta sexta s\u00e1bado".split(" "),shortDays:"Dom Seg Ter Qua Qui Sex S\u00e1b".split(" "),months:"janeiro fevereiro mar\u00e7o abril maio junho julho agosto setembro outubro novembro dezembro".split(" "),shortMonths:"Jan Fev Mar Abr Mai Jun Jul Ago Set Out Nov Dez".split(" "),AM:"AM",PM:"PM",am:"am",
pm:"pm",formats:{c:"%a %d %b %Y %X %Z",D:"%d-%m-%Y",F:"%Y-%m-%d",R:"%H:%M",r:"%I:%M:%S %p",T:"%H:%M:%S",v:"%e-%b-%Y",X:"%T",x:"%D"}},ru_RU:{days:"\u0412\u043e\u0441\u043a\u0440\u0435\u0441\u0435\u043d\u044c\u0435 \u041f\u043e\u043d\u0435\u0434\u0435\u043b\u044c\u043d\u0438\u043a \u0412\u0442\u043e\u0440\u043d\u0438\u043a \u0421\u0440\u0435\u0434\u0430 \u0427\u0435\u0442\u0432\u0435\u0440\u0433 \u041f\u044f\u0442\u043d\u0438\u0446\u0430 \u0421\u0443\u0431\u0431\u043e\u0442\u0430".split(" "),shortDays:"\u0412\u0441 \u041f\u043d \u0412\u0442 \u0421\u0440 \u0427\u0442 \u041f\u0442 \u0421\u0431".split(" "),
months:"\u042f\u043d\u0432\u0430\u0440\u044c \u0424\u0435\u0432\u0440\u0430\u043b\u044c \u041c\u0430\u0440\u0442 \u0410\u043f\u0440\u0435\u043b\u044c \u041c\u0430\u0439 \u0418\u044e\u043d\u044c \u0418\u044e\u043b\u044c \u0410\u0432\u0433\u0443\u0441\u0442 \u0421\u0435\u043d\u0442\u044f\u0431\u0440\u044c \u041e\u043a\u0442\u044f\u0431\u0440\u044c \u041d\u043e\u044f\u0431\u0440\u044c \u0414\u0435\u043a\u0430\u0431\u0440\u044c".split(" "),shortMonths:"\u044f\u043d\u0432 \u0444\u0435\u0432 \u043c\u0430\u0440 \u0430\u043f\u0440 \u043c\u0430\u0439 \u0438\u044e\u043d \u0438\u044e\u043b \u0430\u0432\u0433 \u0441\u0435\u043d \u043e\u043a\u0442 \u043d\u043e\u044f \u0434\u0435\u043a".split(" "),
AM:"AM",PM:"PM",am:"am",pm:"pm",formats:{c:"%a %d %b %Y %X",D:"%d.%m.%y",F:"%Y-%m-%d",R:"%H:%M",r:"%I:%M:%S %p",T:"%H:%M:%S",v:"%e-%b-%Y",X:"%T",x:"%D"}},tr_TR:{days:"Pazar Pazartesi Sal\u0131 \u00c7ar\u015famba Per\u015fembe Cuma Cumartesi".split(" "),shortDays:"Paz Pzt Sal \u00c7r\u015f Pr\u015f Cum Cts".split(" "),months:"Ocak \u015eubat Mart Nisan May\u0131s Haziran Temmuz A\u011fustos Eyl\u00fcl Ekim Kas\u0131m Aral\u0131k".split(" "),shortMonths:"Oca \u015eub Mar Nis May Haz Tem A\u011fu Eyl Eki Kas Ara".split(" "),
AM:"\u00d6\u00d6",PM:"\u00d6S",am:"\u00d6\u00d6",pm:"\u00d6S",formats:{c:"%a %d %b %Y %X %Z",D:"%d-%m-%Y",F:"%Y-%m-%d",R:"%H:%M",r:"%I:%M:%S %p",T:"%H:%M:%S",v:"%e-%b-%Y",X:"%T",x:"%D"}},zh_CN:{days:"\u661f\u671f\u65e5 \u661f\u671f\u4e00 \u661f\u671f\u4e8c \u661f\u671f\u4e09 \u661f\u671f\u56db \u661f\u671f\u4e94 \u661f\u671f\u516d".split(" "),shortDays:"\u65e5\u4e00\u4e8c\u4e09\u56db\u4e94\u516d".split(""),months:"\u4e00\u6708\u4efd \u4e8c\u6708\u4efd \u4e09\u6708\u4efd \u56db\u6708\u4efd \u4e94\u6708\u4efd \u516d\u6708\u4efd \u4e03\u6708\u4efd \u516b\u6708\u4efd \u4e5d\u6708\u4efd \u5341\u6708\u4efd \u5341\u4e00\u6708\u4efd \u5341\u4e8c\u6708\u4efd".split(" "),
shortMonths:"\u4e00\u6708 \u4e8c\u6708 \u4e09\u6708 \u56db\u6708 \u4e94\u6708 \u516d\u6708 \u4e03\u6708 \u516b\u6708 \u4e5d\u6708 \u5341\u6708 \u5341\u4e00\u6708 \u5341\u4e8c\u6708".split(" "),AM:"\u4e0a\u5348",PM:"\u4e0b\u5348",am:"\u4e0a\u5348",pm:"\u4e0b\u5348",formats:{c:"%a %d %b %Y %X %Z",D:"%d/%m/%y",F:"%Y-%m-%d",R:"%H:%M",r:"%I:%M:%S %p",T:"%H:%M:%S",v:"%e-%b-%Y",X:"%r",x:"%D"}}},k=f.en_US,n=new a(k,0,!1);if("undefined"!==typeof m)var l=m.exports=n;else l=function(){return this||(0,eval)("this")}(),
l.strftime=n;"function"!==typeof Date.now&&(Date.now=function(){return+new Date})})()},function(m,p,a){(function(){var a=function(c,b,e){return a.parse(c,b,e)};a.version="0.0.1";(m.exports=a).strptime=a;a.locale={a:"Sun Mon Tue Wed Thu Fri Sat".split(" "),A:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),b:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),B:"January February March April May June July August September October November December".split(" "),f:"Jan. Feb. Mar. Apr. May Jun. Jul. Aug. Sep. Oct. Nov. Dec.".split(" "),
c:"%Y-%m-%d %H:%M:%S",P:["am","pm"],r:"%I:%M:%S %p",x:"%m/%d/%y",X:"%H:%M:%S",day:["Yesterday","Today","Tomorrow"],bg:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),Bg:"January February March April May June July August September October November December".split(" "),fg:"Jan. Feb. Mar. Apr. May Jun. Jul. Aug. Sep. Oct. Nov. Dec.".split(" "),Date_dBY_year_in_HM:"%#B %-d, %Y at %-H:%M",Date_dBY_year:"%#B %-d, %Y",Date_dBY:"%#B %-d, %Y",Date_AdBY:"%A, %#B %-d, %Y",Date_dBA:"%#B %-d, %A",
Date_df_in_HM:"%#f, %-d at %-H:%M",Date_dfY:"%-d %#f %Y",Date_dB_in_HM:"%#B %-d at %-H:%M",Date_df:"%-d %#f"};(function(a){function c(a,b,d,e,g,h){b=String(b);d=String(d);b=b.replace(/^[#_0\^\-!~]+/,"");e=f[b];if(!e)return a;var k=!1;-1===d.indexOf("!")&&1===b.length&&(-1<d.indexOf("~")||-1<"bBf".indexOf(b)&&/%[0\-_]?d[\s]+$/.test(h.substr(0,g)))&&(k=!0);if(("I"===b||"l"===b)&&!/%[pP]/.test(h))throw Error("Undefined AM/PM");switch(typeof e){case "function":return e();case "string":return e;case "object":return c.make.push([e.make,
d,k]),"("+e.reg+")";default:return a}}function b(a,c){a=String(a);c=String(c);return-1!==c.indexOf("#")?a.substr(0,1).toUpperCase()+a.substr(1):-1!==c.indexOf("^")?a.substr(0,1)+a.substr(1).toLowerCase():a}var h=Array.prototype.indexOf||function(a){for(var c=this.length,b=0;b<c;){if(a==this[b])return b;b++}return-1},g=a.locale,f={"%":"\\%",a:"\\S+",A:"\\S+",b:{reg:"\\S+",make:function(a,c,d,f){c=h.call(f?g.bg:g.b,b(c,d));if(-1===c)return!1;a.setUTCMonth(c);return!0}},h:{reg:"\\S+",make:function(a,
c,d,f){c=h.call(f?g.bg:g.b,b(c,d));if(-1===c)return!1;a.setUTCMonth(c);return!0}},B:{reg:"\\S+",make:function(a,c,d,f){c=h.call(f?g.Bg:g.B,b(c,d));if(-1===c)return!1;a.setUTCMonth(c);return!0}},f:{reg:"\\S+",make:function(a,c,d,f){c=h.call(f?g.fg:g.f,b(c,d));if(-1===c)return!1;a.setUTCMonth(c);return!0}},g:{reg:"[\\d\\s]?\\d",make:function(a,c){c=parseInt(c,10);if(0>c||99<c)return!1;c+=100*parseInt((new Date).getUTCFullYear()/100,10);a.setUTCFullYear(c);return!0}},G:{reg:"\\d{4}",make:function(a,
c){c=parseInt(c,10);a.setUTCFullYear(c);return!0}},d:{reg:"[\\d\\s]?\\d",make:function(a,c){c=parseInt(c,10);if(1>c||31<c)return!1;a.setUTCDate(c);return!0}},e:{reg:"[\\d\\s]?\\d",make:function(a,c){c=parseInt(c,10);if(1>c||31<c)return!1;a.setUTCDate(c);return!0}},H:{reg:"[\\d\\s]?\\d",make:function(a,c){c=parseInt(c,10);if(0>c||23<c)return!1;a.setUTCHours(c);return!0}},I:{reg:"[\\d\\s]?\\d",make:function(a,c){c=parseInt(c,10);if(1>c||12<c)return!1;a.setUTCHours(a.getUTCHours()+c);return!0}},m:{reg:"[\\d\\s]?\\d",
make:function(a,c){c=parseInt(c,10);if(1>c||12<c)return!1;a.setUTCMonth(c-1);return!0}},M:{reg:"[\\d\\s]?\\d",make:function(a,c){c=parseInt(c,10);if(0>c||59<c)return!1;a.setUTCMinutes(c);return!0}},n:"\\n",p:{reg:"\\S+",make:function(a,c){c=h.call(g.P,c.toLowerCase());if(-1===c)return!1;1===c&&a.setUTCHours(a.getUTCHours()+12);return!0}},P:{reg:"\\S+",make:function(a,c){c=h.call(g.P,c.toLowerCase());if(-1===c)return!1;1===c&&a.setUTCHours(a.getUTCHours()+12);return!0}},S:{reg:"[\\d\\s]?\\d",make:function(a,
c){c=parseInt(c,10);if(0>c||60<c)return!1;a.setUTCSeconds(c);return!0}},t:"\\t",u:"\\d",U:"[\\d\\s]?\\d",w:"\\d",W:"[\\d\\s]?\\d",y:{reg:"[\\d\\s]?\\d",make:function(a,c){c=parseInt(c,10);if(0>c||99<c)return!1;c+=100*parseInt((new Date).getUTCFullYear()/100,10);a.setUTCFullYear(c);return!0}},Y:{reg:"\\d{4}",make:function(a,c){c=parseInt(c,10);a.setUTCFullYear(c);return!0}},z:{reg:"[+\\-]\\d{4}",make:function(a,c){c=c.match(/^([+\-])(\d{2})(\d{2})$/);if(!c)return!1;var b=6E4*(60*parseInt(c[2],10)+
parseInt(c[3],10));"+"===c[1]&&(b=-b);a.setTime(a.getTime()+b);return!0}},l:{reg:"[\\d\\s]?\\d",make:function(a,c){c=parseInt(c,10);if(1>c||12<c)return!1;a.setUTCHours(a.getUTCHours()+c);return!0}},s:{reg:"\\d+",make:function(a,c){c=parseInt(c,10);a.setTime(1E3*c);return!0}},c:g.c,r:g.r,R:"%H:%M",T:"%H:%M:%S",x:g.x,X:g.X,D:"%m/%d/%y",F:"%Y-%m-%d",Date_iso:"%Y-%m-%dT%H:%M:%S",Date_dBY_year_in_HM:g.Date_dBY_year_in_HM,Date_dBY_year:g.Date_dBY_year,Date_dBY:g.Date_dBY,Date_dBA:g.Date_dBA,Date_AdBY:g.Date_AdBY,
Date_df_in_HM:g.Date_df_in_HM,Date_dfY:g.Date_dfY,Date_dB_in_HM:g.Date_dB_in_HM,Date_dmY__dot:"%d.%m.%Y",Date_df:g.Date_df,Date_FT:"%F %T",Date_dmY__minus:"%d-%m-%Y"};a.parse=function(a,b,d){a=String(a);b=String(b);for(var f=5;/%(Date_[a-zA-Z0-9_]+|[cDFrRTxX])/g.test(b)&&f;)b=b.replace(/%(Date_[a-zA-Z0-9_]+|[cDFrRTxX])/,c),f--;c.make=[];b=b.replace(/%(([#\^!~]{0,2})[aAbBfh]|([0\-_]?)[degHImMSVWyl]|[GnpPtuUwYzZs%])/g,c);a=a.match(new RegExp(b));if(!a||!c.make.length)return null;b=new Date(Date.UTC(0,
0));f=0;for(var e=c.make.length;f<e;f++){var g=c.make[f];if(!g[0](b,a[f+1],g[1],g[2]))return null}d&&b.setTime(b.getTime()+6E4*b.getTimezoneOffset());return b}})(a)})()},function(m,p,a){(function(a,c){(function(a,b){function d(a){delete y[a]}function e(a){if(t)setTimeout(e,0,a);else{var c=y[a];if(c){t=!0;try{var f=c.callback,g=c.args;switch(g.length){case 0:f();break;case 1:f(g[0]);break;case 2:f(g[0],g[1]);break;case 3:f(g[0],g[1],g[2]);break;default:f.apply(b,g)}}finally{d(a),t=!1}}}}function f(){m=
function(a){c.nextTick(function(){e(a)})}}function k(){if(a.postMessage&&!a.importScripts){var c=!0,b=a.onmessage;a.onmessage=function(){c=!1};a.postMessage("","*");a.onmessage=b;return c}}function n(){var c="setImmediate$"+Math.random()+"$",b=function(b){b.source===a&&"string"===typeof b.data&&0===b.data.indexOf(c)&&e(+b.data.slice(c.length))};a.addEventListener?a.addEventListener("message",b,!1):a.attachEvent("onmessage",b);m=function(b){a.postMessage(c+b,"*")}}function l(){var a=new MessageChannel;
a.port1.onmessage=function(a){e(a.data)};m=function(c){a.port2.postMessage(c)}}function q(){var a=H.documentElement;m=function(c){var b=H.createElement("script");b.onreadystatechange=function(){e(c);b.onreadystatechange=null;a.removeChild(b);b=null};a.appendChild(b)}}function u(){m=function(a){setTimeout(e,0,a)}}if(!a.setImmediate){var z=1,y={},t=!1,H=a.document,m,C=Object.getPrototypeOf&&Object.getPrototypeOf(a);C=C&&C.setTimeout?C:a;"[object process]"==={}.toString.call(a.process)?f():k()?n():a.MessageChannel?
l():H&&"onreadystatechange"in H.createElement("script")?q():u();C.setImmediate=function(a){"function"!==typeof a&&(a=new Function(""+a));for(var c=Array(arguments.length-1),b=0;b<c.length;b++)c[b]=arguments[b+1];y[z]={callback:a,args:c};m(z);return z++};C.clearImmediate=d}})("undefined"===typeof self?"undefined"===typeof a?this:a:self)}).call(this,a(0),a(6))},function(m,p){function a(){throw Error("setTimeout has not been defined");}function b(){throw Error("clearTimeout has not been defined");}function c(c){if(k===
setTimeout)return setTimeout(c,0);if((k===a||!k)&&setTimeout)return k=setTimeout,setTimeout(c,0);try{return k(c,0)}catch(t){try{return k.call(null,c,0)}catch(H){return k.call(this,c,0)}}}function d(a){if(n===clearTimeout)return clearTimeout(a);if((n===b||!n)&&clearTimeout)return n=clearTimeout,clearTimeout(a);try{return n(a)}catch(t){try{return n.call(null,a)}catch(H){return n.call(this,a)}}}function e(){q&&u&&(q=!1,u.length?l=u.concat(l):z=-1,l.length&&h())}function h(){if(!q){var a=c(e);q=!0;for(var b=
l.length;b;){u=l;for(l=[];++z<b;)u&&u[z].run();z=-1;b=l.length}u=null;q=!1;d(a)}}function g(a,c){this.fun=a;this.array=c}function f(){}m=m.exports={};try{var k="function"===typeof setTimeout?setTimeout:a}catch(y){k=a}try{var n="function"===typeof clearTimeout?clearTimeout:b}catch(y){n=b}var l=[],q=!1,u,z=-1;m.nextTick=function(a){var b=Array(arguments.length-1);if(1<arguments.length)for(var d=1;d<arguments.length;d++)b[d-1]=arguments[d];l.push(new g(a,b));1!==l.length||q||c(h)};g.prototype.run=function(){this.fun.apply(null,
this.array)};m.title="browser";m.browser=!0;m.env={};m.argv=[];m.version="";m.versions={};m.on=f;m.addListener=f;m.once=f;m.off=f;m.removeListener=f;m.removeAllListeners=f;m.emit=f;m.prependListener=f;m.prependOnceListener=f;m.listeners=function(a){return[]};m.binding=function(a){throw Error("process.binding is not supported");};m.cwd=function(){return"/"};m.chdir=function(a){throw Error("process.chdir is not supported");};m.umask=function(){return 0}},function(m,p){Sk.asserts={};Sk.asserts.assert=
function(a,b){return a};Sk.exportSymbol("Sk.asserts.assert",Sk.asserts.assert);Sk.asserts.fail=function(a){};Sk.exportSymbol("Sk.asserts.fail",Sk.asserts.fail)},function(m,p){Sk.bool_check=function(a,b){if(void 0===a||null===a||"boolean"!==typeof a)throw Error("must specify "+b+" and it must be a boolean");};Sk.python2={print_function:!1,division:!1,absolute_import:null,unicode_literals:!1,python3:!1,class_repr:!1,inherit_from_object:!1,super_args:!1,octal_number_literal:!1,bankers_rounding:!1,python_version:!1,
dunder_round:!1,exceptions:!1,no_long_type:!1,ceil_floor_int:!1,silent_octal_literal:!0};Sk.python3={print_function:!0,division:!0,absolute_import:null,unicode_literals:!0,python3:!0,class_repr:!0,inherit_from_object:!0,super_args:!0,octal_number_literal:!0,bankers_rounding:!0,python_version:!0,dunder_round:!0,exceptions:!0,no_long_type:!0,ceil_floor_int:!0,silent_octal_literal:!1};Sk.configure=function(a){Sk.output=a.output||Sk.output;Sk.asserts.assert("function"===typeof Sk.output);Sk.filewriter=
a.filewriter||Sk.filewriter;Sk.asserts.assert("function"===typeof Sk.filewriter);Sk.debugout=a.debugout||Sk.debugout;Sk.asserts.assert("function"===typeof Sk.debugout);Sk.uncaughtException=a.uncaughtException||Sk.uncaughtException;Sk.asserts.assert("function"===typeof Sk.uncaughtException);Sk.read=a.read||Sk.read;Sk.asserts.assert("function"===typeof Sk.read);Sk.nonreadopen=a.nonreadopen||!1;Sk.asserts.assert("boolean"===typeof Sk.nonreadopen);Sk.fileopen=a.fileopen||void 0;Sk.asserts.assert("function"===
typeof Sk.fileopen||"undefined"===typeof Sk.fileopen);Sk.filewrite=a.filewrite||void 0;Sk.asserts.assert("function"===typeof Sk.filewrite||"undefined"===typeof Sk.filewrite);Sk.timeoutMsg=a.timeoutMsg||Sk.timeoutMsg;Sk.asserts.assert("function"===typeof Sk.timeoutMsg);Sk.exportSymbol("Sk.timeoutMsg",Sk.timeoutMsg);Sk.sysargv=a.sysargv||Sk.sysargv;Sk.asserts.assert(Sk.isArrayLike(Sk.sysargv));Sk.__future__=a.__future__||Sk.python2;Sk.bool_check(Sk.__future__.print_function,"Sk.__future__.print_function");
Sk.bool_check(Sk.__future__.division,"Sk.__future__.division");Sk.bool_check(Sk.__future__.unicode_literals,"Sk.__future__.unicode_literals");Sk.bool_check(Sk.__future__.class_repr,"Sk.__future__.class_repr");Sk.bool_check(Sk.__future__.inherit_from_object,"Sk.__future__.inherit_from_object");Sk.bool_check(Sk.__future__.super_args,"Sk.__future__.super_args");Sk.bool_check(Sk.__future__.octal_number_literal,"Sk.__future__.octal_number_literal");Sk.bool_check(Sk.__future__.bankers_rounding,"Sk.__future__.bankers_rounding");
Sk.bool_check(Sk.__future__.python_version,"Sk.__future__.python_version");Sk.bool_check(Sk.__future__.dunder_round,"Sk.__future__.dunder_round");Sk.bool_check(Sk.__future__.exceptions,"Sk.__future__.exceptions");Sk.bool_check(Sk.__future__.no_long_type,"Sk.__future__.no_long_type");Sk.bool_check(Sk.__future__.ceil_floor_int,"Sk.__future__.ceil_floor_int");Sk.bool_check(Sk.__future__.silent_octal_literal,"Sk.__future__.silent_octal_literal");Sk.imageProxy=a.imageProxy||"http://localhost:8080/320x";
Sk.asserts.assert("string"===typeof Sk.imageProxy||"function"===typeof Sk.imageProxy);Sk.inputfun=a.inputfun||Sk.inputfun;Sk.asserts.assert("function"===typeof Sk.inputfun);Sk.inputfunTakesPrompt=a.inputfunTakesPrompt||!1;Sk.asserts.assert("boolean"===typeof Sk.inputfunTakesPrompt);Sk.retainGlobals=a.retainglobals||a.retainGlobals||!1;Sk.asserts.assert("boolean"===typeof Sk.retainGlobals);Sk.debugging=a.debugging||!1;Sk.asserts.assert("boolean"===typeof Sk.debugging);Sk.killableWhile=a.killableWhile||
!1;Sk.asserts.assert("boolean"===typeof Sk.killableWhile);Sk.killableFor=a.killableFor||!1;Sk.asserts.assert("boolean"===typeof Sk.killableFor);Sk.signals=a.signals;Sk.signals=!0===Sk.signals?{listeners:[],addEventListener:function(a){Sk.signals.listeners.push(a)},removeEventListener:function(a){a=Sk.signals.listeners.indexOf(a);0<=a&&Sk.signals.listeners.splice(a,1)},signal:function(a,c){for(var b=0;b<Sk.signals.listeners.length;b++)Sk.signals.listeners[b].call(null,a,c)}}:null;Sk.asserts.assert("object"===
typeof Sk.signals);Sk.breakpoints=a.breakpoints||function(){return!0};Sk.asserts.assert("function"===typeof Sk.breakpoints);Sk.setTimeout=a.setTimeout;void 0===Sk.setTimeout&&(Sk.setTimeout="function"===typeof setTimeout?function(a,c){setTimeout(a,c)}:function(a,c){a()});Sk.asserts.assert("function"===typeof Sk.setTimeout);"execLimit"in a&&(Sk.execLimit=a.execLimit);"yieldLimit"in a&&(Sk.yieldLimit=a.yieldLimit);a.syspath&&(Sk.syspath=a.syspath,Sk.asserts.assert(Sk.isArrayLike(Sk.syspath)),Sk.realsyspath=
void 0,Sk.sysmodules=new Sk.builtin.dict([]));Sk.misceval.softspace_=!1;Sk.switch_version("round$",Sk.__future__.dunder_round);Sk.switch_version("next$",Sk.__future__.python3);Sk.switch_version("haskey$",Sk.__future__.python3);Sk.switch_version("clear$",Sk.__future__.python3);Sk.switch_version("copy$",Sk.__future__.python3);Sk.builtin.lng.prototype.tp$name=Sk.__future__.no_long_type?"int":"long";Sk.builtin.lng.prototype.ob$type=Sk.__future__.no_long_type?Sk.builtin.int_:Sk.builtin.lng;Sk.builtin.str.$next=
Sk.__future__.python3?new Sk.builtin.str("__next__"):new Sk.builtin.str("next");Sk.setupOperators(Sk.__future__.python3);Sk.setupDunderMethods(Sk.__future__.python3);Sk.__future__.python3?(Sk.builtin.dict.prototype.keys=new Sk.builtin.func(Sk.builtin.dict.prototype.py3$keys),Sk.builtin.dict.prototype.values=new Sk.builtin.func(Sk.builtin.dict.prototype.py3$values),Sk.builtin.dict.prototype.items=new Sk.builtin.func(Sk.builtin.dict.prototype.py3$items)):(Sk.builtin.dict.prototype.keys=new Sk.builtin.func(Sk.builtin.dict.prototype.py2$keys),
Sk.builtin.dict.prototype.values=new Sk.builtin.func(Sk.builtin.dict.prototype.py2$values),Sk.builtin.dict.prototype.items=new Sk.builtin.func(Sk.builtin.dict.prototype.py2$items));Sk.setupObjects(Sk.__future__.python3)};Sk.exportSymbol("Sk.configure",Sk.configure);Sk.uncaughtException=function(a){throw a;};Sk.uncaughtException=function(a){throw a;};Sk.exportSymbol("Sk.uncaughtException",Sk.uncaughtException);Sk.timeoutMsg=function(){return"Program exceeded run time limit."};Sk.exportSymbol("Sk.timeoutMsg",
Sk.timeoutMsg);Sk.execLimit=Number.POSITIVE_INFINITY;Sk.yieldLimit=Number.POSITIVE_INFINITY;Sk.output=function(a){};Sk.read=function(a){if(void 0===Sk.builtinFiles)throw"skulpt-stdlib.js has not been loaded";if(void 0===Sk.builtinFiles.files[a])throw"File not found: '"+a+"'";return Sk.builtinFiles.files[a]};Sk.sysargv=[];Sk.getSysArgv=function(){return Sk.sysargv};Sk.exportSymbol("Sk.getSysArgv",Sk.getSysArgv);Sk.syspath=[];Sk.inBrowser=void 0!==Sk.global.document;Sk.debugout=function(a){};(function(){void 0!==
Sk.global.write?(Sk.output=Sk.global.write,Sk.filewriter=Sk.global.write):void 0!==Sk.global.console&&void 0!==Sk.global.console.log?(Sk.output=function(a){Sk.global.console.log(a)},Sk.filewriter=Sk.output):void 0!==Sk.global.print&&(Sk.output=Sk.global.print,Sk.filewriter=Sk.output);void 0!==Sk.global.console&&void 0!==Sk.global.console.log?Sk.debugout=function(a){Sk.global.console.log(a)}:void 0!==Sk.global.print&&(Sk.debugout=Sk.global.print)})();Sk.inputfun=function(a){return window.prompt(a)};
Sk.setup_method_mappings=function(){return{round$:{classes:[Sk.builtin.float_,Sk.builtin.int_,Sk.builtin.nmber],2:null,3:"__round__"},clear$:{classes:[Sk.builtin.list],2:null,3:"clear"},copy$:{classes:[Sk.builtin.list],2:null,3:"copy"},next$:{classes:[Sk.builtin.dict_iter_,Sk.builtin.list_iter_,Sk.builtin.set_iter_,Sk.builtin.str_iter_,Sk.builtin.tuple_iter_,Sk.builtin.generator,Sk.builtin.enumerate,Sk.builtin.filter_,Sk.builtin.zip_,Sk.builtin.map_,Sk.builtin.iterator],2:"next",3:"__next__"},haskey$:{classes:[Sk.builtin.dict],
2:"has_key",3:null}}};Sk.switch_version=function(a,b){var c;var d=Sk.setup_method_mappings()[a];if(b){b=d[3];var e=d[2]}else b=d[2],e=d[3];var h=d.classes;var g=h.length;for(c=0;c<g;c++)d=h[c],e&&d.prototype.hasOwnProperty(e)&&delete d.prototype[e],b&&(d.prototype[b]=new Sk.builtin.func(d.prototype[a]))};Sk.exportSymbol("Sk.__future__",Sk.__future__);Sk.exportSymbol("Sk.inputfun",Sk.inputfun)},function(m,p){void 0===Sk.builtin&&(Sk.builtin={});Sk.dunderToSkulpt={__eq__:"ob$eq",__ne__:"ob$ne",__lt__:"ob$lt",
__le__:"ob$le",__gt__:"ob$gt",__ge__:"ob$ge",__hash__:"tp$hash",__abs__:"nb$abs",__neg__:"nb$negative",__pos__:"nb$positive",__int__:"nb$int_",__long__:"nb$lng",__float__:"nb$float_",__add__:"nb$add",__radd__:"nb$reflected_add",__sub__:"nb$subtract",__rsub__:"nb$reflected_subtract",__mul__:"nb$multiply",__rmul__:"nb$reflected_multiply",__div__:"nb$divide",__rdiv__:"nb$reflected_divide",__floordiv__:"nb$floor_divide",__rfloordiv__:"nb$reflected_floor_divide",__invert__:"nb$invert",__mod__:"nb$remainder",
__rmod__:"nb$reflected_remainder",__divmod__:"nb$divmod",__rdivmod__:"nb$reflected_divmod",__pow__:"nb$power",__rpow__:"nb$reflected_power",__contains__:"sq$contains",__iter__:"tp$iter",__bool__:["nb$bool",1],__nonzero__:["nb$nonzero",1],__len__:["sq$length",1],__get__:["tp$descr_get",3],__set__:["tp$descr_set",3]};Sk.setupDunderMethods=function(a){a?(Sk.dunderToSkulpt.__matmul__="tp$matmul",Sk.dunderToSkulpt.__rmatmul__="tp$reflected_matmul"):(Sk.dunderToSkulpt.__matmul__&&delete Sk.dunderToSkulpt.__matmul__,
Sk.dunderToSkulpt.__rmatmul__&&delete Sk.dunderToSkulpt.__rmatmul__)};Sk.exportSymbol("Sk.setupDunderMethods",Sk.setupDunderMethods);Sk.builtin.type=function(a,b,c){if(void 0===b&&void 0===c)return a.ob$type;{if("dict"!==c.tp$name)throw new Sk.builtin.TypeError("type() argument 3 must be dict, not "+Sk.abstr.typeName(c));if(!Sk.builtin.checkString(a))throw new Sk.builtin.TypeError("type() argument 1 must be str, not "+Sk.abstr.typeName(a));if("tuple"!==b.tp$name)throw new Sk.builtin.TypeError("type() argument 2 must be tuple, not "+
Sk.abstr.typeName(b));var d=function(a,c){void 0!==d.prototype.tp$base&&(d.prototype.tp$base.sk$klass?d.prototype.tp$base.call(this,a,c):(a=a.slice(),a.unshift(d,this),Sk.abstr.superConstructor.apply(void 0,a)));this.$d=new Sk.builtin.dict([])};var e=Sk.ffi.remapToJs(a);var h=!1;d.tp$call=function(a,c){var b=Sk.builtin.type.typeLookup(d,Sk.builtin.str.$new);a=a||[];c=c||[];if(void 0===b||b===Sk.builtin.object.prototype.__new__){var f=new d(a,c);b=void 0}else{var e=a.slice();e.unshift(d);f=Sk.misceval.applyOrSuspend(b,
void 0,void 0,c,e)}return Sk.misceval.chain(f,function(d){var e=Sk.builtin.type.typeLookup(d.ob$type,Sk.builtin.str.$init);f=d;if(void 0!==e)return a.unshift(f),Sk.misceval.applyOrSuspend(e,void 0,void 0,c,a);if(void 0===b&&(0!==a.length||0!==c.length)&&!h)throw new Sk.builtin.TypeError("__init__() got unexpected argument(s)");},function(a){if(a!==Sk.builtin.none.none$&&void 0!==a)throw new Sk.builtin.TypeError("__init__() should return None, not "+Sk.abstr.typeName(a));return f})};0===b.v.length&&
Sk.__future__.inherit_from_object&&(b.v.push(Sk.builtin.object),Sk.abstr.setUpInheritance(e,d,Sk.builtin.object));var g,f,k=[];var n=b.tp$iter();for(g=n.tp$iternext();void 0!==g;g=n.tp$iternext()){for(void 0===f&&(f=g);g.sk$klass&&g.prototype.tp$base;)g=g.prototype.tp$base;!g.sk$klass&&0>k.indexOf(g)&&(k.push(g),h=!0)}if(1<k.length)throw new Sk.builtin.TypeError("Multiple inheritance with more than one builtin type is unsupported");void 0!==f&&(Sk.abstr.inherits(d,f),f.prototype instanceof Sk.builtin.object||
f===Sk.builtin.object)&&(d.prototype.tp$base=f);d.prototype.tp$name=e;d.prototype.ob$type=Sk.builtin.type.makeIntoTypeObj(e,d);void 0===c.mp$lookup(Sk.builtin.str.$module)&&c.mp$ass_subscript(Sk.builtin.str.$module,Sk.globals.__name__);n=c.tp$iter();for(g=n.tp$iternext();void 0!==g;g=n.tp$iternext())e=c.mp$subscript(g),void 0===e&&(e=null),d.prototype[g.v]=e,d[g.v]=e;d.__class__=d;d.__name__=a;d.sk$klass=!0;d.prototype.hp$type=!0;d.prototype.$r=function(){const a=Sk.abstr.lookupSpecial(this,Sk.builtin.str.$repr);
return void 0!==a&&a!==Sk.builtin.object.prototype.__repr__?Sk.misceval.callsimArray(a,[this]):void 0!==d.prototype.tp$base&&void 0!==d.prototype.tp$base.prototype.$r?d.prototype.tp$base.prototype.$r.call(this):Sk.builtin.object.prototype.$r.call(this)};d.prototype.tp$setattr=function(a,c,b){var d=Sk.builtin.object.prototype.GenericGetAttr.call(this,Sk.builtin.str.$setattr);return void 0!==d?(a=Sk.misceval.callsimOrSuspendArray(d,[a,c]),b?a:Sk.misceval.retryOptionalSuspensionOrThrow(a)):Sk.builtin.object.prototype.GenericSetAttr.call(this,
a,c,b)};d.prototype.tp$str=function(){const a=Sk.abstr.lookupSpecial(this,Sk.builtin.str.$str);return void 0!==a&&a!==Sk.builtin.object.prototype.__str__?Sk.misceval.callsimArray(a,[this]):void 0!==d.prototype.tp$base&&d.prototype.tp$base!==Sk.builtin.object&&void 0!==d.prototype.tp$base.prototype.tp$str?d.prototype.tp$base.prototype.tp$str.call(this):this.$r()};d.prototype.tp$length=function(a){var c=Sk.misceval.chain(Sk.abstr.gattr(this,Sk.builtin.str.$len,a),function(a){return Sk.misceval.applyOrSuspend(a,
void 0,void 0,void 0,[])});return a?c:Sk.misceval.retryOptionalSuspensionOrThrow(c)};d.prototype.tp$call=function(a,c){return Sk.misceval.chain(this.tp$getattr(Sk.builtin.str.$call,!0),function(b){if(void 0===b)throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(this)+"' object is not callable");return Sk.misceval.applyOrSuspend(b,void 0,void 0,c,a)})};const q=c.mp$lookup(Sk.builtin.str.$next);void 0!==q&&(d.prototype.tp$iternext=function(a){const c=this,b=Sk.misceval.tryCatch(()=>Sk.misceval.callsimOrSuspendArray(q,
[c]),a=>{if(!(a instanceof Sk.builtin.StopIteration))throw a;});return a?b:Sk.misceval.retryOptionalSuspensionOrThrow(b)});d.prototype.tp$getitem=function(a,c){var b=this.tp$getattr(Sk.builtin.str.$getitem,c);if(void 0!==b)return a=Sk.misceval.applyOrSuspend(b,void 0,void 0,void 0,[a]),c?a:Sk.misceval.retryOptionalSuspensionOrThrow(a);throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(this)+"' object does not support indexing");};d.prototype.tp$setitem=function(a,c,b){var d=this.tp$getattr(Sk.builtin.str.$setitem,
b);if(void 0!==d)return a=Sk.misceval.applyOrSuspend(d,void 0,void 0,void 0,[a,c]),b?a:Sk.misceval.retryOptionalSuspensionOrThrow(a);throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(this)+"' object does not support item assignment");};b&&(d.$d=new Sk.builtin.dict([]),d.$d.mp$ass_subscript(Sk.builtin.type.basesStr_,b),a=Sk.builtin.type.buildMRO(d),d.$d.mp$ass_subscript(Sk.builtin.type.mroStr_,a),d.tp$mro=a);d.tp$setattr=Sk.builtin.type.prototype.tp$setattr;for(var l in Sk.dunderToSkulpt)d.hasOwnProperty(l)&&
Sk.builtin.type.$allocateSlot(d,l);let u=Sk.builtin.type.typeLookup(d,Sk.builtin.str.$getattribute);void 0!==u&&u!==Sk.builtin.object.prototype.__getattribute__?d.prototype.tp$getattr=function(a,c){let b=Sk.misceval.tryCatch(()=>Sk.misceval.callsimOrSuspendArray(u,[this,a]),function(a){if(!(a instanceof Sk.builtin.AttributeError))throw a;});return c?b:Sk.misceval.retryOptionalSuspensionOrThrow(b)}:d.prototype.tp$getattr||(d.prototype.tp$getattr=Sk.builtin.object.prototype.GenericGetAttr);return d}};
Object.defineProperties(Sk.builtin.type.prototype,{call:{value:Function.prototype.call},apply:{value:Function.prototype.apply},ob$type:{value:Sk.builtin.type,writable:!0},tp$name:{value:"type",writable:!0},tp$base:{value:Sk.builtin.object,writable:!0},sk$type:{value:!0}});Sk.builtin.type.makeTypeObj=function(a,b){Sk.builtin.type.makeIntoTypeObj(a,b);return b};Sk.builtin.type.makeIntoTypeObj=function(a,b){Sk.asserts.assert(void 0!==a);Sk.asserts.assert(void 0!==b);Object.setPrototypeOf(b,Sk.builtin.type.prototype);
return b};Sk.builtin.type.prototype.$r=function(){let a=this.prototype.__module__,b="",c="class";a&&Sk.builtin.checkString(a)?b=a.v+".":a=null;a||this.sk$klass||Sk.__future__.class_repr||(c="type");return new Sk.builtin.str("<"+c+" '"+b+this.prototype.tp$name+"'>")};Sk.builtin.type.prototype.tp$getattr=function(a,b){if(this.$d){var c=this.$d.mp$lookup(a);if(void 0!==c)return c}a=Sk.builtin.type.typeLookup(this,a);if(void 0!==a&&null!==a&&void 0!==a.ob$type)var d=a.tp$descr_get;if(d)return d.call(a,
Sk.builtin.none.none$,this,b);if(void 0!==a)return a};Sk.builtin.type.prototype.tp$setattr=function(a,b){if(void 0===this.sk$klass)throw new Sk.builtin.TypeError("can't set attributes of built-in/extension type '"+this.prototype.tp$name+"'");a=Sk.fixReserved(a.$jsstr());this[a]=b;this.prototype[a]=b;a in Sk.dunderToSkulpt&&Sk.builtin.type.$allocateSlot(this,a)};Sk.builtin.type.typeLookup=function(a,b){var c=a.tp$mro,d,e=b.$mangled;if(c)for(d=0;d<c.v.length;++d){a=c.v[d];if(a.hasOwnProperty(e))return a[e];
var h=a.$d.mp$lookup(b);if(void 0!==h)return h;if(a.prototype&&void 0!==a.prototype[e])return a.prototype[e]}else if(a.prototype)return a.prototype[e]};Sk.builtin.type.mroMerge_=function(a){for(var b,c,d,e,h,g,f=[];;){for(c=0;c<a.length&&(b=a[c],0===b.length);++c);if(c===a.length)return f;d=[];for(c=0;c<a.length;++c)if(b=a[c],0!==b.length){g=b[0];h=0;a:for(;h<a.length;++h)for(e=a[h],b=1;b<e.length;++b)if(e[b]===g)break a;h===a.length&&d.push(g)}if(0===d.length)throw new Sk.builtin.TypeError("Inconsistent precedences in type hierarchy");
d=d[0];f.push(d);for(c=0;c<a.length;++c)b=a[c],0<b.length&&b[0]===d&&b.splice(0,1)}};Sk.builtin.type.buildMRO_=function(a){var b=[[a]],c=a.$d.mp$subscript(Sk.builtin.type.basesStr_);for(a=0;a<c.v.length;++a)b.push(Sk.builtin.type.buildMRO_(c.v[a]));var d=[];for(a=0;a<c.v.length;++a)d.push(c.v[a]);b.push(d);return Sk.builtin.type.mroMerge_(b)};Sk.builtin.type.buildMRO=function(a){return new Sk.builtin.tuple(Sk.builtin.type.buildMRO_(a))};Sk.builtin.type.prototype.__format__=function(a,b){Sk.builtin.pyCheckArgsLen("__format__",
arguments.length,1,2);return new Sk.builtin.str(a)};Sk.builtin.type.pythonFunctions=["__format__"];Sk.builtin.type.$allocateSlot=function(a,b){const c=a[b];b=Sk.dunderToSkulpt[b];if("string"===typeof b)a.prototype[b]=function(){let a,b,h;a=arguments.length;b=Array(a+1);b[0]=this;for(h=0;h<a;h++)b[h+1]=arguments[h];return Sk.misceval.callsimArray(c,b)};else{let d=b[1];b=b[0];a.prototype[b]=function(){let a,b,g,f,k=!1;a=arguments.length;b=d<=a?Array(a):Array(a+1);b[0]=this;f=1;for(g=0;g<a;g++)g===d-
1?k=arguments[g]:(b[f]=arguments[g],f+=1);return k?Sk.misceval.callsimOrSuspendArray(c,b):Sk.misceval.callsimArray(c,b)}}}},function(m,p){Sk.abstr={};Sk.abstr.typeName=function(a){return void 0!==a.tp$name?a.tp$name:"<invalid type>"};Sk.abstr.binop_type_error=function(a,b,c){a=Sk.abstr.typeName(a);b=Sk.abstr.typeName(b);throw new Sk.builtin.TypeError("unsupported operand type(s) for "+c+": '"+a+"' and '"+b+"'");};Sk.abstr.unop_type_error=function(a,b){a=Sk.abstr.typeName(a);throw new Sk.builtin.TypeError("bad operand type for unary "+
{UAdd:"+",USub:"-",Invert:"~"}[b]+": '"+a+"'");};Sk.abstr.boNameToSlotFuncLhs_=function(a,b){if(null!==a)switch(b){case "Add":return a.nb$add?a.nb$add:a.__add__;case "Sub":return a.nb$subtract?a.nb$subtract:a.__sub__;case "Mult":return a.nb$multiply?a.nb$multiply:a.__mul__;case "MatMult":if(Sk.__future__.python3)return a.tp$matmul?a.tp$matmul:a.__matmul__;case "Div":return a.nb$divide?a.nb$divide:a.__div__;case "FloorDiv":return a.nb$floor_divide?a.nb$floor_divide:a.__floordiv__;case "Mod":return a.nb$remainder?
a.nb$remainder:a.__mod__;case "DivMod":return a.nb$divmod?a.nb$divmod:a.__divmod__;case "Pow":return a.nb$power?a.nb$power:a.__pow__;case "LShift":return a.nb$lshift?a.nb$lshift:a.__lshift__;case "RShift":return a.nb$rshift?a.nb$rshift:a.__rshift__;case "BitAnd":return a.nb$and?a.nb$and:a.__and__;case "BitXor":return a.nb$xor?a.nb$xor:a.__xor__;case "BitOr":return a.nb$or?a.nb$or:a.__or__}};Sk.abstr.boNameToSlotFuncRhs_=function(a,b){if(null!==a)switch(b){case "Add":return a.nb$reflected_add?a.nb$reflected_add:
a.__radd__;case "Sub":return a.nb$reflected_subtract?a.nb$reflected_subtract:a.__rsub__;case "Mult":return a.nb$reflected_multiply?a.nb$reflected_multiply:a.__rmul__;case "MatMult":if(Sk.__future__.python3)return a.tp$reflected_matmul?a.tp$reflected_matmul:a.__rmatmul__;case "Div":return a.nb$reflected_divide?a.nb$reflected_divide:a.__rdiv__;case "FloorDiv":return a.nb$reflected_floor_divide?a.nb$reflected_floor_divide:a.__rfloordiv__;case "Mod":return a.nb$reflected_remainder?a.nb$reflected_remainder:
a.__rmod__;case "DivMod":return a.nb$reflected_divmod?a.nb$reflected_divmod:a.__rdivmod__;case "Pow":return a.nb$reflected_power?a.nb$reflected_power:a.__rpow__;case "LShift":return a.nb$reflected_lshift?a.nb$reflected_lshift:a.__rlshift__;case "RShift":return a.nb$reflected_rshift?a.nb$reflected_rshift:a.__rrshift__;case "BitAnd":return a.nb$reflected_and?a.nb$reflected_and:a.__rand__;case "BitXor":return a.nb$reflected_xor?a.nb$reflected_xor:a.__rxor__;case "BitOr":return a.nb$reflected_or?a.nb$reflected_or:
a.__ror__}};Sk.abstr.iboNameToSlotFunc_=function(a,b){switch(b){case "Add":return a.nb$inplace_add?a.nb$inplace_add:a.__iadd__;case "Sub":return a.nb$inplace_subtract?a.nb$inplace_subtract:a.__isub__;case "Mult":return a.nb$inplace_multiply?a.nb$inplace_multiply:a.__imul__;case "MatMult":if(Sk.__future__.python3)return a.tp$inplace_matmul?a.tp$inplace_matmul:a.__imatmul__;case "Div":return a.nb$inplace_divide?a.nb$inplace_divide:a.__idiv__;case "FloorDiv":return a.nb$inplace_floor_divide?a.nb$inplace_floor_divide:
a.__ifloordiv__;case "Mod":return a.nb$inplace_remainder;case "Pow":return a.nb$inplace_power;case "LShift":return a.nb$inplace_lshift?a.nb$inplace_lshift:a.__ilshift__;case "RShift":return a.nb$inplace_rshift?a.nb$inplace_rshift:a.__irshift__;case "BitAnd":return a.nb$inplace_and;case "BitOr":return a.nb$inplace_or;case "BitXor":return a.nb$inplace_xor?a.nb$inplace_xor:a.__ixor__}};Sk.abstr.uoNameToSlotFunc_=function(a,b){if(null!==a)switch(b){case "USub":return a.nb$negative?a.nb$negative:a.__neg__;
case "UAdd":return a.nb$positive?a.nb$positive:a.__pos__;case "Invert":return a.nb$invert?a.nb$invert:a.__invert__}};Sk.abstr.binary_op_=function(a,b,c){var d=a.constructor;d=b.constructor!==d&&b instanceof d;if(d){var e=Sk.abstr.boNameToSlotFuncRhs_(b,c);if(void 0!==e&&(e=e.call?e.call(b,a):Sk.misceval.callsimArray(e,[b,a]),void 0!==e&&e!==Sk.builtin.NotImplemented.NotImplemented$))return e}e=Sk.abstr.boNameToSlotFuncLhs_(a,c);if(void 0!==e&&(e=e.call?e.call(a,b):Sk.misceval.callsimArray(e,[a,b]),
void 0!==e&&e!==Sk.builtin.NotImplemented.NotImplemented$)||!d&&(e=Sk.abstr.boNameToSlotFuncRhs_(b,c),void 0!==e&&(e=e.call?e.call(b,a):Sk.misceval.callsimArray(e,[b,a]),void 0!==e&&e!==Sk.builtin.NotImplemented.NotImplemented$)))return e;Sk.abstr.binop_type_error(a,b,c)};Sk.abstr.binary_iop_=function(a,b,c){var d=Sk.abstr.iboNameToSlotFunc_(a,c);return void 0!==d&&(d=d.call?d.call(a,b):Sk.misceval.callsimArray(d,[a,b]),void 0!==d&&d!==Sk.builtin.NotImplemented.NotImplemented$)?d:Sk.abstr.binary_op_(a,
b,c)};Sk.abstr.unary_op_=function(a,b){var c=Sk.abstr.uoNameToSlotFunc_(a,b);if(void 0!==c&&(c=c.call?c.call(a):Sk.misceval.callsimArray(c,[a]),void 0!==c))return c;Sk.abstr.unop_type_error(a,b)};Sk.abstr.numberBinOp=function(a,b,c){return Sk.abstr.binary_op_(a,b,c)};Sk.exportSymbol("Sk.abstr.numberBinOp",Sk.abstr.numberBinOp);Sk.abstr.numberInplaceBinOp=function(a,b,c){return Sk.abstr.binary_iop_(a,b,c)};Sk.exportSymbol("Sk.abstr.numberInplaceBinOp",Sk.abstr.numberInplaceBinOp);Sk.abstr.numberUnaryOp=
function(a,b){return"Not"===b?Sk.misceval.isTrue(a)?Sk.builtin.bool.false$:Sk.builtin.bool.true$:Sk.abstr.unary_op_(a,b)};Sk.exportSymbol("Sk.abstr.numberUnaryOp",Sk.abstr.numberUnaryOp);Sk.abstr.fixSeqIndex_=function(a,b){b=Sk.builtin.asnum$(b);0>b&&a.sq$length&&(b+=a.sq$length());return b};Sk.abstr.sequenceContains=function(a,b,c){if(a.sq$contains)return a.sq$contains(b);var d=Sk.abstr.lookupSpecial(a,Sk.builtin.str.$contains);if(null!=d)return Sk.misceval.isTrue(Sk.misceval.callsimArray(d,[a,b]));
if(!Sk.builtin.checkIterable(a))throw c=Sk.abstr.typeName(a),new Sk.builtin.TypeError("argument of type '"+c+"' is not iterable");a=Sk.misceval.iterFor(Sk.abstr.iter(a),function(a){return Sk.misceval.richCompareBool(a,b,"Eq")?new Sk.misceval.Break(!0):!1},!1);return c?a:Sk.misceval.retryOptionalSuspensionOrThrow(a)};Sk.abstr.sequenceConcat=function(a,b){if(a.sq$concat)return a.sq$concat(b);a=Sk.abstr.typeName(a);throw new Sk.builtin.TypeError("'"+a+"' object can't be concatenated");};Sk.abstr.sequenceGetIndexOf=
function(a,b){if(a.index)return Sk.misceval.callsimArray(a.index,[a,b]);if(Sk.builtin.checkIterable(a)){var c=0;var d=Sk.abstr.iter(a);for(a=d.tp$iternext();void 0!==a;a=d.tp$iternext()){if(Sk.misceval.richCompareBool(b,a,"Eq"))return new Sk.builtin.int_(c);c+=1}throw new Sk.builtin.ValueError("sequence.index(x): x not in sequence");}b=Sk.abstr.typeName(a);throw new Sk.builtin.TypeError("argument of type '"+b+"' is not iterable");};Sk.abstr.sequenceGetCountOf=function(a,b){if(a.count)return Sk.misceval.callsimArray(a.count,
[a,b]);if(Sk.builtin.checkIterable(a)){var c=0;var d=Sk.abstr.iter(a);for(a=d.tp$iternext();void 0!==a;a=d.tp$iternext())Sk.misceval.richCompareBool(b,a,"Eq")&&(c+=1);return new Sk.builtin.int_(c)}b=Sk.abstr.typeName(a);throw new Sk.builtin.TypeError("argument of type '"+b+"' is not iterable");};Sk.abstr.sequenceGetItem=function(a,b,c){if(a.mp$subscript)return a.mp$subscript(b);a=Sk.abstr.typeName(a);throw new Sk.builtin.TypeError("'"+a+"' object is unsubscriptable");};Sk.abstr.sequenceSetItem=function(a,
b,c,d){if(a.mp$ass_subscript)return a.mp$ass_subscript(b,c);a=Sk.abstr.typeName(a);throw new Sk.builtin.TypeError("'"+a+"' object does not support item assignment");};Sk.abstr.sequenceDelItem=function(a,b){if(a.sq$del_item)b=Sk.abstr.fixSeqIndex_(a,b),a.sq$del_item(b);else throw a=Sk.abstr.typeName(a),new Sk.builtin.TypeError("'"+a+"' object does not support item deletion");};Sk.abstr.sequenceRepeat=function(a,b,c){c=Sk.builtin.asnum$(c);if(void 0===Sk.misceval.asIndex(c))throw a=Sk.abstr.typeName(c),
new Sk.builtin.TypeError("can't multiply sequence by non-int of type '"+a+"'");return a.call(b,c)};Sk.abstr.sequenceGetSlice=function(a,b,c){if(a.sq$slice)return b=Sk.abstr.fixSeqIndex_(a,b),c=Sk.abstr.fixSeqIndex_(a,c),a.sq$slice(b,c);if(a.mp$subscript)return a.mp$subscript(new Sk.builtin.slice(b,c));a=Sk.abstr.typeName(a);throw new Sk.builtin.TypeError("'"+a+"' object is unsliceable");};Sk.abstr.sequenceDelSlice=function(a,b,c){if(a.sq$del_slice)b=Sk.abstr.fixSeqIndex_(a,b),c=Sk.abstr.fixSeqIndex_(a,
c),a.sq$del_slice(b,c);else throw a=Sk.abstr.typeName(a),new Sk.builtin.TypeError("'"+a+"' doesn't support slice deletion");};Sk.abstr.sequenceSetSlice=function(a,b,c,d){if(a.sq$ass_slice)b=Sk.abstr.fixSeqIndex_(a,b),c=Sk.abstr.fixSeqIndex_(a,c),a.sq$ass_slice(b,c,d);else if(a.mp$ass_subscript)a.mp$ass_subscript(new Sk.builtin.slice(b,c),d);else throw a=Sk.abstr.typeName(a),new Sk.builtin.TypeError("'"+a+"' object doesn't support slice assignment");};Sk.abstr.sequenceUnpack=function(a,b,c,d){if(!Sk.builtin.checkIterable(a))throw new Sk.builtin.TypeError("cannot unpack non-iterable "+
Sk.abstr.typeName(a)+" object");const e=Sk.abstr.iter(a),h=[];let g=0,f;0<b&&(f=Sk.misceval.iterFor(e,a=>{h.push(a);if(++g===b)return new Sk.misceval.Break}));return Sk.misceval.chain(f,()=>{if(h.length<b)throw new Sk.builtin.ValueError("not enough values to unpack (expected at least "+c+", got "+h.length+")");if(!d)return Sk.misceval.chain(e.tp$iternext(!0),a=>{if(void 0!==a)throw new Sk.builtin.ValueError("too many values to unpack (expected "+b+")");return h});const a=[];return Sk.misceval.chain(Sk.misceval.iterFor(e,
c=>{a.push(c)}),()=>{const d=a.length+b-c;if(0>d)throw new Sk.builtin.ValueError("not enough values to unpack (expected at least "+c+", got "+(c+d)+")");h.push(new Sk.builtin.list(a.slice(0,d)));h.push(...a.slice(d));return h})})};Sk.abstr.mappingUnpackIntoKeywordArray=function(a,b,c){return Sk.misceval.chain(b.tp$getattr(new Sk.builtin.str("items")),function(a){if(!a)throw new Sk.builtin.TypeError("Object is not a mapping");return Sk.misceval.callsimOrSuspend(a)},function(b){return Sk.misceval.iterFor(Sk.abstr.iter(b),
function(b){if(!b||!b.v)throw new Sk.builtin.TypeError("Object is not a mapping; items() does not return tuples");if(!(b.v[0]instanceof Sk.builtin.str))throw new Sk.builtin.TypeError((c.tp$name?c.tp$name+":":"")+"keywords must be strings");a.push(b.v[0].v,b.v[1])})})};Sk.abstr.objectFormat=function(a,b){var c=Sk.abstr.lookupSpecial(a,Sk.builtin.str.$format);if(null==c)return Sk.misceval.callsimArray(Sk.builtin.object.prototype.__format__,[a,b]);a=Sk.misceval.callsimArray(c,[a,b]);if(!Sk.builtin.checkString(a))throw new Sk.builtin.TypeError("__format__ must return a str, not "+
Sk.abstr.typeName(a));return a};Sk.abstr.objectAdd=function(a,b){if(a.nb$add)return a.nb$add(b);a=Sk.abstr.typeName(a);b=Sk.abstr.typeName(b);throw new Sk.builtin.TypeError("unsupported operand type(s) for +: '"+a+"' and '"+b+"'");};Sk.abstr.objectNegative=function(a){if(a.nb$negative)return a.nb$negative();throw new Sk.builtin.TypeError("bad operand type for unary -: '"+Sk.abstr.typeName(a)+"'");};Sk.abstr.objectPositive=function(a){if(a.nb$positive)return a.nb$positive();throw new Sk.builtin.TypeError("bad operand type for unary +: '"+
Sk.abstr.typeName(a)+"'");};Sk.abstr.objectDelItem=function(a,b){if(null!==a){if(a.mp$del_subscript){a.mp$del_subscript(b);return}if(a.sq$ass_item){var c=Sk.misceval.asIndex(b);if(void 0===c)throw a=Sk.abstr.typeName(b),new Sk.builtin.TypeError("sequence index must be integer, not '"+a+"'");Sk.abstr.sequenceDelItem(a,c);return}}a=Sk.abstr.typeName(a);throw new Sk.builtin.TypeError("'"+a+"' object does not support item deletion");};Sk.exportSymbol("Sk.abstr.objectDelItem",Sk.abstr.objectDelItem);Sk.abstr.objectGetItem=
function(a,b,c){if(null!==a){if(a.tp$getitem)return a.tp$getitem(b,c);if(a.mp$subscript)return a.mp$subscript(b,c);if(Sk.misceval.isIndex(b)&&a.sq$item)return Sk.abstr.sequenceGetItem(a,Sk.misceval.asIndex(b),c)}a=Sk.abstr.typeName(a);throw new Sk.builtin.TypeError("'"+a+"' does not support indexing");};Sk.exportSymbol("Sk.abstr.objectGetItem",Sk.abstr.objectGetItem);Sk.abstr.objectSetItem=function(a,b,c,d){if(null!==a){if(a.tp$setitem)return a.tp$setitem(b,c,d);if(a.mp$ass_subscript)return a.mp$ass_subscript(b,
c,d);if(Sk.misceval.isIndex(b)&&a.sq$ass_item)return Sk.abstr.sequenceSetItem(a,Sk.misceval.asIndex(b),c,d)}a=Sk.abstr.typeName(a);throw new Sk.builtin.TypeError("'"+a+"' does not support item assignment");};Sk.exportSymbol("Sk.abstr.objectSetItem",Sk.abstr.objectSetItem);Sk.abstr.gattr=function(a,b,c){if(null===a||!a.tp$getattr)throw c=Sk.abstr.typeName(a),new Sk.builtin.AttributeError("'"+c+"' object has no attribute '"+b.$jsstr()+"'");c=a.tp$getattr(b,c);let d;if(void 0===c)throw d=a.sk$type?"type object '"+
a.prototype.tp$name+"'":"'"+Sk.abstr.typeName(a)+"' object",new Sk.builtin.AttributeError(d+" has no attribute '"+b.$jsstr()+"'");return c.$isSuspension?Sk.misceval.chain(c,function(c){if(void 0===c)throw d=a.sk$type?"type object '"+a.prototype.tp$name+"'":"'"+Sk.abstr.typeName(a)+"' object",new Sk.builtin.AttributeError(d+" has no attribute '"+b.$jsstr()+"'");return c}):c};Sk.exportSymbol("Sk.abstr.gattr",Sk.abstr.gattr);Sk.abstr.sattr=function(a,b,c,d){var e=Sk.abstr.typeName(a);if(null===a)throw new Sk.builtin.AttributeError("'"+
e+"' object has no attribute '"+b.$jsstr()+"'");if(void 0!==a.tp$setattr)return a.tp$setattr(b,c,d);throw new Sk.builtin.AttributeError("'"+e+"' object has no attribute '"+b.$jsstr()+"'");};Sk.exportSymbol("Sk.abstr.sattr",Sk.abstr.sattr);Sk.abstr.iternext=function(a,b){return a.tp$iternext(b)};Sk.exportSymbol("Sk.abstr.iternext",Sk.abstr.iternext);Sk.abstr.iter=function(a){var b=function(a){this.idx=0;this.myobj=a;this.getitem=Sk.abstr.lookupSpecial(a,Sk.builtin.str.$getitem);this.tp$iternext=function(){try{var a=
Sk.misceval.callsimArray(this.getitem,[this.myobj,Sk.ffi.remapToPy(this.idx)])}catch(e){if(e instanceof Sk.builtin.IndexError||e instanceof Sk.builtin.StopIteration)return;throw e;}this.idx++;return a}};if(a.tp$iter){if(b=a.tp$iter(),b.tp$iternext)return b}else if(Sk.abstr.lookupSpecial(a,Sk.builtin.str.$getitem))return new b(a);throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(a)+"' object is not iterable");};Sk.exportSymbol("Sk.abstr.iter",Sk.abstr.iter);Sk.abstr.lookupSpecial=function(a,b){if(a.ob$type)a=
a.ob$type;else return null;return Sk.builtin.type.typeLookup(a,b)};Sk.exportSymbol("Sk.abstr.lookupSpecial",Sk.abstr.lookupSpecial);Sk.abstr.markUnhashable=function(a){a=a.prototype;a.__hash__=Sk.builtin.none.none$;a.tp$hash=Sk.builtin.none.none$};Sk.abstr.inherits=function(a,b){function c(){}c.prototype=b.prototype;a.superClass_=b.prototype;a.prototype=new c;a.prototype.constructor=a};Sk.abstr.setUpInheritance=function(a,b,c){Sk.abstr.inherits(b,c);b.prototype.tp$base=c;b.prototype.tp$name=a;b.prototype.ob$type=
Sk.builtin.type.makeIntoTypeObj(a,b)};Sk.abstr.superConstructor=function(a,b,c){var d=Array.prototype.slice.call(arguments,2);a.prototype.tp$base.apply(b,d)}},function(m,p){Sk.builtin.object=function(){return this instanceof Sk.builtin.object?this:new Sk.builtin.object};Object.defineProperties(Sk.builtin.object.prototype,{ob$type:{value:Sk.builtin.object,writable:!0},tp$name:{value:"object",writable:!0},tp$base:{value:void 0,writable:!0},sk$object:{value:!0}});Object.setPrototypeOf(Sk.builtin.type.prototype,
Sk.builtin.object.prototype);Object.setPrototypeOf(Sk.builtin.type,Sk.builtin.type.prototype);Object.setPrototypeOf(Sk.builtin.object,Sk.builtin.type.prototype);Sk.builtin.type.prototype.tp$base=Sk.builtin.object;Sk.builtin.object.prototype.__init__=function(){return Sk.builtin.none.none$};Sk.builtin.object.prototype.__init__.co_kwargs=1;Sk.builtin._tryGetSubscript=function(a,b){try{return a.mp$subscript(b)}catch(c){}};Sk.exportSymbol("Sk.builtin._tryGetSubscript",Sk.builtin._tryGetSubscript);Sk.builtin.object.prototype.GenericGetAttr=
function(a,b){var c,d;var e=this.ob$type;Sk.asserts.assert(void 0!==e,"object has no ob$type!");if(d=this.$d||this.constructor.$d){d.mp$lookup?c=d.mp$lookup(a):d.mp$subscript?c=Sk.builtin._tryGetSubscript(d,a):"object"===typeof d&&(c=d[a.$mangled]);if(void 0!==c)return c;if("__dict__"==a.$jsstr()&&d instanceof Sk.builtin.dict)return d}d=Sk.builtin.type.typeLookup(e,a);if(void 0!==d&&null!==d&&(c=d.tp$descr_get))return c.call(d,this,this.ob$type,b);if(void 0!==d)return d;d=Sk.builtin.type.typeLookup(e,
Sk.builtin.str.$getattr);if(void 0!==d&&null!==d){var h=(c=d.tp$descr_get)?c.call(d,this,this.ob$type):d;c=Sk.misceval.tryCatch(function(){return Sk.misceval.callsimOrSuspendArray(h,[a])},function(a){if(!(a instanceof Sk.builtin.AttributeError))throw a;});return b?c:Sk.misceval.retryOptionalSuspensionOrThrow(c)}};Sk.exportSymbol("Sk.builtin.object.prototype.GenericGetAttr",Sk.builtin.object.prototype.GenericGetAttr);Sk.builtin.object.prototype.GenericPythonGetAttr=function(a,b){a=Sk.builtin.object.prototype.GenericGetAttr.call(a,
b,!0);if(void 0===a)throw new Sk.builtin.AttributeError(b);return a};Sk.exportSymbol("Sk.builtin.object.prototype.GenericPythonGetAttr",Sk.builtin.object.prototype.GenericPythonGetAttr);Sk.builtin.object.prototype.GenericSetAttr=function(a,b,c){var d=Sk.abstr.typeName(this),e=a.$jsstr(),h=this.ob$type;Sk.asserts.assert(void 0!==h,"object has no ob$type!");var g=this.$d||this.constructor.$d;if("__class__"==e){if(void 0===b.tp$mro||void 0===b.sk$klass)throw new Sk.builtin.TypeError("attempted to assign non-class to __class__");
this.ob$type=b}else{e=Sk.builtin.type.typeLookup(h,a);if(void 0!==e&&null!==e&&(h=e.tp$descr_set))return h.call(e,this,b,c);if(g.mp$ass_subscript){if(this instanceof Sk.builtin.object&&!this.ob$type.sk$klass&&void 0===g.mp$lookup(a))throw new Sk.builtin.AttributeError("'"+d+"' object has no attribute '"+a.$jsstr()+"'");g.mp$ass_subscript(a,b)}else"object"===typeof g&&(g[a.$mangled]=b)}};Sk.exportSymbol("Sk.builtin.object.prototype.GenericSetAttr",Sk.builtin.object.prototype.GenericSetAttr);Sk.builtin.object.prototype.GenericPythonSetAttr=
function(a,b,c){return Sk.builtin.object.prototype.GenericSetAttr.call(a,b,c,!0)};Sk.exportSymbol("Sk.builtin.object.prototype.GenericPythonSetAttr",Sk.builtin.object.prototype.GenericPythonSetAttr);Sk.builtin.object.prototype.HashNotImplemented=function(){throw new Sk.builtin.TypeError("unhashable type: '"+Sk.abstr.typeName(this)+"'");};Sk.builtin.object.prototype.tp$getattr=Sk.builtin.object.prototype.GenericGetAttr;Sk.builtin.object.prototype.tp$setattr=Sk.builtin.object.prototype.GenericSetAttr;
Sk.builtin.object.prototype.__getattribute__=Sk.builtin.object.prototype.GenericPythonGetAttr;Sk.builtin.object.prototype.__setattr__=Sk.builtin.object.prototype.GenericPythonSetAttr;Sk.builtin.object.prototype.tp$descr_set=void 0;Sk.builtin.object.prototype.__new__=function(a){Sk.builtin.pyCheckArgsLen("__new__",arguments.length,1,1,!1,!1);return new a([],[])};Sk.builtin.object.prototype.__repr__=function(a){Sk.builtin.pyCheckArgsLen("__repr__",arguments.length,0,0,!1,!0);return a.$r()};Sk.builtin.object.prototype.__format__=
function(a,b){Sk.builtin.pyCheckArgsLen("__format__",arguments.length,2,2);if(Sk.builtin.checkString(b)){var c=Sk.ffi.remapToJs(b);if(""!==c)throw new Sk.builtin.NotImplementedError("format spec is not yet implemented");}else{if(Sk.__future__.exceptions)throw new Sk.builtin.TypeError("format() argument 2 must be str, not "+Sk.abstr.typeName(b));throw new Sk.builtin.TypeError("format expects arg 2 to be string or unicode, not "+Sk.abstr.typeName(b));}return new Sk.builtin.str(a)};Sk.builtin.object.prototype.__str__=
function(a){Sk.builtin.pyCheckArgsLen("__str__",arguments.length,0,0,!1,!0);return a.$r()};Sk.builtin.object.prototype.__hash__=function(a){Sk.builtin.pyCheckArgsLen("__hash__",arguments.length,0,0,!1,!0);return a.tp$hash()};Sk.builtin.object.prototype.__eq__=function(a,b){Sk.builtin.pyCheckArgsLen("__eq__",arguments.length,1,1,!1,!0);return a.ob$eq(b)};Sk.builtin.object.prototype.__ne__=function(a,b){Sk.builtin.pyCheckArgsLen("__ne__",arguments.length,1,1,!1,!0);return a.ob$ne(b)};Sk.builtin.object.prototype.__lt__=
function(a,b){Sk.builtin.pyCheckArgsLen("__lt__",arguments.length,1,1,!1,!0);return a.ob$lt(b)};Sk.builtin.object.prototype.__le__=function(a,b){Sk.builtin.pyCheckArgsLen("__le__",arguments.length,1,1,!1,!0);return a.ob$le(b)};Sk.builtin.object.prototype.__gt__=function(a,b){Sk.builtin.pyCheckArgsLen("__gt__",arguments.length,1,1,!1,!0);return a.ob$gt(b)};Sk.builtin.object.prototype.__ge__=function(a,b){Sk.builtin.pyCheckArgsLen("__ge__",arguments.length,1,1,!1,!0);return a.ob$ge(b)};Sk.builtin.object.prototype.$r=
function(){const a=Sk.abstr.lookupSpecial(this,Sk.builtin.str.$module);let b="";a&&Sk.builtin.checkString(a)&&(b=a.v+".");return new Sk.builtin.str("<"+b+Sk.abstr.typeName(this)+" object>")};Sk.builtin.object.prototype.tp$str=function(){return this.$r()};Sk.builtin.hashCount=1;Sk.builtin.idCount=1;Sk.builtin.object.prototype.tp$hash=function(){this.$savedHash_||(this.$savedHash_=new Sk.builtin.int_(Sk.builtin.hashCount++));return this.$savedHash_};Sk.builtin.object.prototype.ob$eq=function(a){return this===
a?Sk.builtin.bool.true$:Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.object.prototype.ob$ne=function(a){return this===a?Sk.builtin.bool.false$:Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.object.prototype.ob$lt=function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.object.prototype.ob$le=function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.object.prototype.ob$gt=function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.object.prototype.ob$ge=
function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.object.pythonFunctions="__repr__ __str__ __hash__ __eq__ __ne__ __lt__ __le__ __gt__ __ge__ __getattribute__ __setattr__ __format__".split(" ");Sk.builtin.none=function(){return Sk.builtin.none.none$};Sk.abstr.setUpInheritance("NoneType",Sk.builtin.none,Sk.builtin.object);Sk.builtin.none.prototype.$r=function(){return new Sk.builtin.str("None")};Sk.builtin.none.prototype.tp$hash=function(){return new Sk.builtin.int_(0)};Sk.builtin.none.none$=
Object.create(Sk.builtin.none.prototype,{v:{value:null,enumerable:!0}});Sk.builtin.NotImplemented=function(){return Sk.builtin.NotImplemented.NotImplemented$};Sk.abstr.setUpInheritance("NotImplementedType",Sk.builtin.NotImplemented,Sk.builtin.object);Sk.builtin.NotImplemented.prototype.$r=function(){return new Sk.builtin.str("NotImplemented")};Sk.builtin.NotImplemented.NotImplemented$=Object.create(Sk.builtin.NotImplemented.prototype,{v:{value:null,enumerable:!0}});Sk.exportSymbol("Sk.builtin.none",
Sk.builtin.none);Sk.exportSymbol("Sk.builtin.NotImplemented",Sk.builtin.NotImplemented)},function(m,p){Sk.builtin.pyCheckArgs=function(a,b,c,d,e,h){b=b.length;void 0===d&&(d=Infinity);e&&--b;h&&--b;if(b<c||b>d)throw new Sk.builtin.TypeError((c===d?a+"() takes exactly "+c+" arguments":b<c?a+"() takes at least "+c+" arguments":a+"() takes at most "+d+" arguments")+(" ("+b+" given)"));};Sk.exportSymbol("Sk.builtin.pyCheckArgs",Sk.builtin.pyCheckArgs);Sk.builtin.pyCheckArgsLen=function(a,b,c,d,e,h){void 0===
d&&(d=Infinity);e&&--b;h&&--b;if(b<c||b>d)throw new Sk.builtin.TypeError((c===d?a+"() takes exactly "+c+" arguments":b<c?a+"() takes at least "+c+" arguments":a+"() takes at most "+d+" arguments")+(" ("+b+" given)"));};Sk.builtin.pyCheckType=function(a,b,c){if(!c)throw new Sk.builtin.TypeError(a+" must be a "+b);};Sk.exportSymbol("Sk.builtin.pyCheckType",Sk.builtin.pyCheckType);Sk.builtin.checkSequence=function(a){return null!==a&&void 0!==a.mp$subscript};Sk.exportSymbol("Sk.builtin.checkSequence",
Sk.builtin.checkSequence);Sk.builtin.checkIterable=function(a){var b=!1;if(null!==a)try{return(b=Sk.abstr.iter(a))?!0:!1}catch(c){if(c instanceof Sk.builtin.TypeError)return!1;throw c;}return b};Sk.exportSymbol("Sk.builtin.checkIterable",Sk.builtin.checkIterable);Sk.builtin.checkCallable=function(a){return"function"===typeof a||a instanceof Sk.builtin.func||a instanceof Sk.builtin.method||void 0!==Sk.abstr.lookupSpecial(a,Sk.builtin.str.$call)?!0:!1};Sk.builtin.checkNumber=function(a){return null!==
a&&("number"===typeof a||a instanceof Sk.builtin.int_||a instanceof Sk.builtin.float_||a instanceof Sk.builtin.lng)};Sk.exportSymbol("Sk.builtin.checkNumber",Sk.builtin.checkNumber);Sk.builtin.checkComplex=function(a){return Sk.builtin.complex._complex_check(a)};Sk.exportSymbol("Sk.builtin.checkComplex",Sk.builtin.checkComplex);Sk.builtin.checkInt=function(a){return a instanceof Sk.builtin.int_||a instanceof Sk.builtin.lng||"number"===typeof a&&Number.isInteger(a)};Sk.exportSymbol("Sk.builtin.checkInt",
Sk.builtin.checkInt);Sk.builtin.checkFloat=function(a){return null!==a&&a instanceof Sk.builtin.float_};Sk.exportSymbol("Sk.builtin.checkFloat",Sk.builtin.checkFloat);Sk.builtin.checkString=function(a){return null!==a&&a.__class__==Sk.builtin.str};Sk.exportSymbol("Sk.builtin.checkString",Sk.builtin.checkString);Sk.builtin.checkBytes=function(a){return a instanceof Sk.builtin.bytes};Sk.builtin.checkClass=function(a){return null!==a&&a.sk$type};Sk.exportSymbol("Sk.builtin.checkClass",Sk.builtin.checkClass);
Sk.builtin.checkBool=function(a){return a instanceof Sk.builtin.bool};Sk.exportSymbol("Sk.builtin.checkBool",Sk.builtin.checkBool);Sk.builtin.checkNone=function(a){return a===Sk.builtin.none.none$};Sk.exportSymbol("Sk.builtin.checkNone",Sk.builtin.checkNone);Sk.builtin.checkFunction=function(a){return null!==a&&void 0!==a.tp$call};Sk.exportSymbol("Sk.builtin.checkFunction",Sk.builtin.checkFunction);Sk.builtin.func=function(a,b,c,d){if(!(this instanceof Sk.builtin.func))throw Error("builtin func should be called as a class with `new`");
var e;this.func_code=a;this.func_globals=b||null;if(void 0!==d)for(e in d)c[e]=d[e];this.$d={__name__:a.co_name,__class__:Sk.builtin.func};this.func_closure=c;this.tp$name=this.func_code&&this.func_code.co_name&&this.func_code.co_name.v||this.func_code.name||"<native JS>";this.$memoiseFlags();if(this.memoised=a.co_fastcall)this.tp$call=a;return this};Sk.abstr.setUpInheritance("function",Sk.builtin.func,Sk.builtin.object);Sk.exportSymbol("Sk.builtin.func",Sk.builtin.func);Sk.builtin.func.prototype.tp$name=
"function";Sk.builtin.func.prototype.$memoiseFlags=function(){this.co_varnames=this.func_code.co_varnames;this.co_argcount=this.func_code.co_argcount;void 0===this.co_argcount&&this.co_varnames&&(this.co_argcount=this.co_varnames.length);this.co_kwonlyargcount=this.func_code.co_kwonlyargcount||0;this.co_varargs=this.func_code.co_varargs;this.co_kwargs=this.func_code.co_kwargs;this.$defaults=this.func_code.$defaults||[];this.$kwdefs=this.func_code.$kwdefs||[]};Sk.builtin.func.prototype.tp$descr_get=
function(a,b){Sk.asserts.assert(!(void 0===a&&void 0===b));return b&&b.prototype&&b.prototype.tp$name in Sk.builtin&&Sk.builtin[b.prototype.tp$name]===b?new Sk.builtin.method(this,a,b,!0):new Sk.builtin.method(this,a,b)};Sk.builtin.func.pythonFunctions=["__get__"];Sk.builtin.func.prototype.__get__=function(a,b,c){Sk.builtin.pyCheckArgsLen("__get__",arguments.length,1,2,!1,!0);if(b===Sk.builtin.none.none$&&c===Sk.builtin.none.none$)throw new Sk.builtin.TypeError("__get__(None, None) is invalid");return a.tp$descr_get(b,
c)};Sk.builtin.func.prototype.tp$getname=function(){return this.func_code&&this.func_code.co_name&&this.func_code.co_name.v||this.func_code.name||"<native JS>"};Sk.builtin.func.prototype.$resolveArgs=function(a,b){var c=this.co_argcount;void 0===c&&(c=this.co_varnames?this.co_varnames.length:a.length);var d=this.co_varnames||[],e=this.co_kwonlyargcount||0;let h=c+e;if(!(0!==e||this.co_kwargs||b&&0!==b.length||this.co_varargs)){if(a.length==c)return a;if(0===a.length&&this.$defaults&&this.$defaults.length===
c){for(d=0;d!=this.$defaults.length;d++)a[d]=this.$defaults[d];return a}}let g;this.co_kwargs&&(g=[]);var f=a.length;let k=a.length<=c?a:a.slice(0,c);if(this.co_varargs)a=a.length>k.length?a.slice(k.length):[],k[h]=new Sk.builtin.tuple(a);else if(f>c)throw new Sk.builtin.TypeError(this.tp$getname()+"() takes "+c+" positional argument"+(1==c?"":"s")+" but "+f+(1==f?" was ":" were ")+" given");if(b){if(this.func_code.no_kw)throw new Sk.builtin.TypeError(this.tp$getname()+"() takes no keyword arguments");
for(a=0;a<b.length;a+=2){f=b[a];var n=b[a+1],l=d.indexOf(f);if(0<=l){if(void 0!==k[l])throw new Sk.builtin.TypeError(this.tp$getname()+"() got multiple values for argument '"+f+"'");k[l]=n}else if(g)g.push(new Sk.builtin.str(f),n);else throw new Sk.builtin.TypeError(this.tp$getname()+"() got an unexpected keyword argument '"+f+"'");}}b=this.$defaults||[];a=0;f=[];n=!1;for(l=c-b.length;a<l;a++)void 0===k[a]&&(f.push(d[a]),void 0===d[a]&&(n=!0));if(0!=f.length&&(this.co_argcount||this.co_varnames))throw new Sk.builtin.TypeError(this.tp$getname()+
"() missing "+f.length+" required argument"+(1==f.length?"":"s")+(n?"":": "+f.join(", ")));for(;a<c;a++)void 0===k[a]&&(k[a]=b[a-l]);if(0<e){e=[];b=this.$kwdefs;for(a=c;a<h;a++)void 0===k[a]&&(void 0!==b[a-c]?k[a]=b[a-c]:e.push(d[a]));if(0!==e.length)throw new Sk.builtin.TypeError(this.tp$getname()+"() missing "+e.length+" required keyword argument"+(1==e.length?"":"s")+": "+e.join(", "));}if(this.func_closure&&d)for(c=k.length;c<d.length;c++)k.push(void 0);g&&k.unshift(g);return k};Sk.builtin.func.prototype.tp$call=
function(a,b){this.memoised||(this.$memoiseFlags(),this.memoised=!0);if(void 0===this.co_argcount&&void 0===this.co_varnames&&!this.co_kwargs&&!this.func_closure){if(b&&0!==b.length)throw new Sk.builtin.TypeError(this.tp$getname()+"() takes no keyword arguments");return this.func_code.apply(this.func_globals,a)}a=this.$resolveArgs(a,b);this.func_closure&&a.push(this.func_closure);return this.func_code.apply(this.func_globals,a)};Sk.builtin.func.prototype.$r=function(){var a=this.tp$getname();return a in
Sk.builtins&&this===Sk.builtins[a]?new Sk.builtin.str("<built-in function "+a+">"):new Sk.builtin.str("<function "+a+">")}},function(m,p){Sk.builtin.range=function(a,b,c){var d=[],e;Sk.builtin.pyCheckArgsLen("range",arguments.length,1,3);Sk.builtin.pyCheckType("start","integer",Sk.misceval.isIndex(a));a=Sk.misceval.asIndex(a);void 0!==b&&(Sk.builtin.pyCheckType("stop","integer",Sk.misceval.isIndex(b)),b=Sk.misceval.asIndex(b));void 0!==c&&(Sk.builtin.pyCheckType("step","integer",Sk.misceval.isIndex(c)),
c=Sk.misceval.asIndex(c));void 0===b&&void 0===c?(b=a,a=0,c=1):void 0===c&&(c=1);if(0===c)throw new Sk.builtin.ValueError("range() step argument must not be zero");if("number"===typeof a&&"number"===typeof b&&"number"===typeof c)if(0<c)for(e=a;e<b;e+=c)d.push(new Sk.builtin.int_(e));else for(e=a;e>b;e+=c)d.push(new Sk.builtin.int_(e));else{e=new Sk.builtin.lng(a);var h=new Sk.builtin.lng(b),g=new Sk.builtin.lng(c);if(g.nb$ispositive())for(;Sk.misceval.isTrue(e.ob$lt(h));)d.push(e),e=e.nb$add(g);else for(;Sk.misceval.isTrue(e.ob$gt(h));)d.push(e),
e=e.nb$add(g)}d=new Sk.builtin.list(d);return Sk.__future__.python3?new Sk.builtin.range_(a,b,c,d):d};Sk.builtin.asnum$=function(a){return void 0===a||null===a?a:a===Sk.builtin.none.none$?null:a instanceof Sk.builtin.bool?a.v?1:0:"number"===typeof a?a:"string"===typeof a?a:a instanceof Sk.builtin.int_?a.v:a instanceof Sk.builtin.float_?a.v:a instanceof Sk.builtin.lng?a.cantBeInt()?a.str$(10,!0):a.toInt$():a.constructor===Sk.builtin.biginteger?0<a.trueCompare(new Sk.builtin.biginteger(Sk.builtin.int_.threshold$))||
0>a.trueCompare(new Sk.builtin.biginteger(-Sk.builtin.int_.threshold$))?a.toString():a.intValue():a};Sk.exportSymbol("Sk.builtin.asnum$",Sk.builtin.asnum$);Sk.builtin.assk$=function(a){return 0===a%1?new Sk.builtin.int_(a):new Sk.builtin.float_(a)};Sk.exportSymbol("Sk.builtin.assk$",Sk.builtin.assk$);Sk.builtin.asnum$nofloat=function(a){if(void 0===a||null===a)return a;if(a.constructor===Sk.builtin.none)return null;if(a.constructor===Sk.builtin.bool)return a.v?1:0;"number"===typeof a&&(a=a.toString());
a.constructor===Sk.builtin.int_&&(a=a.v.toString());a.constructor===Sk.builtin.float_&&(a=a.v.toString());a.constructor===Sk.builtin.lng&&(a=a.str$(10,!0));a.constructor===Sk.builtin.biginteger&&(a=a.toString());if(0>a.indexOf(".")&&0>a.indexOf("e")&&0>a.indexOf("E"))return a;var b=0;if(0<=a.indexOf("e")){var c=a.substr(0,a.indexOf("e"));b=a.substr(a.indexOf("e")+1)}else 0<=a.indexOf("E")?(c=a.substr(0,a.indexOf("e")),b=a.substr(a.indexOf("E")+1)):c=a;b=parseInt(b,10);a=c.indexOf(".");if(0>a){if(0<=
b){for(;0<b--;)c+="0";return c}return c.length>-b?c.substr(0,c.length+b):0}c=0===a?c.substr(1):a<c.length?c.substr(0,a)+c.substr(a+1):c.substr(0,a);for(a+=b;a>c.length;)c+="0";return c=0>=a?0:c.substr(0,a)};Sk.exportSymbol("Sk.builtin.asnum$nofloat",Sk.builtin.asnum$nofloat);Sk.builtin.round=function(a,b){Sk.builtin.pyCheckArgsLen("round",arguments.length,1,2);if(!Sk.builtin.checkNumber(a)){if(!Sk.builtin.checkFunction(a))throw new Sk.builtin.TypeError("a float is required");if(!Sk.__future__.exceptions)throw new Sk.builtin.AttributeError(Sk.abstr.typeName(a)+
" instance has no attribute '__float__'");}if(void 0!==b&&!Sk.misceval.isIndex(b))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(b)+"' object cannot be interpreted as an index");if(!Sk.__future__.dunder_round&&a.round$)return a.round$(a,b);var c=Sk.abstr.lookupSpecial(a,Sk.builtin.str.$round);if(null!=c)return Sk.builtin.checkFunction(a)?Sk.misceval.callsimArray(c,[a]):Sk.misceval.callsimArray(c,[a,b]);throw new Sk.builtin.TypeError("a float is required");};Sk.builtin.len=function(a){Sk.builtin.pyCheckArgsLen("len",
arguments.length,1,1);var b=function(a){return new Sk.builtin.int_(a)};var c=function(a){if(Sk.builtin.checkInt(a))return b(a);if(Sk.__future__.exceptions)throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(a)+"' object cannot be interpreted as an integer");throw new Sk.builtin.TypeError("__len__() should return an int");};if(a.sq$length)return Sk.misceval.chain(a.sq$length(!0),c);if(a.mp$length)return Sk.misceval.chain(a.mp$length(),b);if(a.tp$length)if(Sk.builtin.checkFunction(a)){c=Sk.abstr.lookupSpecial(a,
Sk.builtin.str.$len);if(null!=c)return Sk.misceval.callsimArray(c,[a]);if(!Sk.__future__.exceptions)throw new Sk.builtin.AttributeError(Sk.abstr.typeName(a)+" instance has no attribute '__len__'");}else return Sk.misceval.chain(a.tp$length(!0),c);throw new Sk.builtin.TypeError("object of type '"+Sk.abstr.typeName(a)+"' has no len()");};Sk.builtin.min=function(a,b,c){const d=c.sq$length();if(!d)throw new Sk.builtin.TypeError("min expected 1 argument, got 0");if(1<d&&null!==a)throw new Sk.builtin.TypeError("Cannot specify a default for min() with multiple positional arguments");
if(1==d&&(c=c.v[0],!Sk.builtin.checkIterable(c)))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(c)+"' object is not iterable");let e=Sk.abstr.iter(c);if(!Sk.builtin.checkNone(b)&&!Sk.builtin.checkCallable(b))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(b)+"' object is not callable");let h;return Sk.misceval.chain(e.tp$iternext(!0),a=>{h=a;if(void 0!==h)return Sk.builtin.checkNone(b)?Sk.misceval.iterFor(e,a=>{Sk.misceval.richCompareBool(a,h,"Lt")&&(h=a)}):Sk.misceval.chain(Sk.misceval.callsimOrSuspendArray(b,
[h]),a=>Sk.misceval.iterFor(e,c=>Sk.misceval.chain(Sk.misceval.callsimOrSuspendArray(b,[c]),b=>{Sk.misceval.richCompareBool(b,a,"Lt")&&(h=c,a=b)})))},()=>{if(void 0===h){if(null===a)throw new Sk.builtin.ValueError("min() arg is an empty sequence");h=a}return h})};Sk.builtin.min.co_argcount=0;Sk.builtin.min.co_kwonlyargcount=2;Sk.builtin.min.$kwdefs=[null,Sk.builtin.none.none$];Sk.builtin.min.co_varnames=["default","key"];Sk.builtin.min.co_varargs=1;Sk.builtin.max=function(a,b,c){const d=c.sq$length();
if(!d)throw new Sk.builtin.TypeError("max expected 1 argument, got 0");if(1<d&&null!==a)throw new Sk.builtin.TypeError("Cannot specify a default for max() with multiple positional arguments");if(1==d&&(c=c.v[0],!Sk.builtin.checkIterable(c)))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(c)+"' object is not iterable");let e=Sk.abstr.iter(c);if(!Sk.builtin.checkNone(b)&&!Sk.builtin.checkCallable(b))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(b)+"' object is not callable");let h;return Sk.misceval.chain(e.tp$iternext(!0),
a=>{h=a;if(void 0!==h)return Sk.builtin.checkNone(b)?Sk.misceval.iterFor(e,a=>{Sk.misceval.richCompareBool(a,h,"Gt")&&(h=a)}):Sk.misceval.chain(Sk.misceval.callsimOrSuspendArray(b,[h]),a=>Sk.misceval.iterFor(e,c=>Sk.misceval.chain(Sk.misceval.callsimOrSuspendArray(b,[c]),b=>{Sk.misceval.richCompareBool(b,a,"Gt")&&(h=c,a=b)})))},()=>{if(void 0===h){if(null===a)throw new Sk.builtin.ValueError("min() arg is an empty sequence");h=a}return h})};Sk.builtin.max.co_argcount=0;Sk.builtin.max.co_kwonlyargcount=
2;Sk.builtin.max.$kwdefs=[null,Sk.builtin.none.none$];Sk.builtin.max.co_varnames=["default","key"];Sk.builtin.max.co_varargs=1;Sk.builtin.any=function(a){Sk.builtin.pyCheckArgsLen("any",arguments.length,1,1);return Sk.misceval.chain(Sk.misceval.iterFor(Sk.abstr.iter(a),function(a){if(Sk.misceval.isTrue(a))return new Sk.misceval.Break(Sk.builtin.bool.true$)}),a=>a||Sk.builtin.bool.false$)};Sk.builtin.all=function(a){Sk.builtin.pyCheckArgsLen("all",arguments.length,1,1);return Sk.misceval.chain(Sk.misceval.iterFor(Sk.abstr.iter(a),
function(a){if(!Sk.misceval.isTrue(a))return new Sk.misceval.Break(Sk.builtin.bool.false$)}),a=>a||Sk.builtin.bool.true$)};Sk.builtin.sum=function(a,b){function c(){return Sk.misceval.iterFor(h,a=>{if(a.constructor===Sk.builtin.int_)g=g.nb$add(a);else{if(a.constructor===Sk.builtin.float_)return g=(new Sk.builtin.float_(g)).nb$add(a),new Sk.misceval.Break("float");g=Sk.abstr.numberBinOp(g,a,"Add");return new Sk.misceval.Break("slow")}})}function d(){return Sk.misceval.iterFor(h,a=>{if(a.constructor===
Sk.builtin.float_||a.constructor===Sk.builtin.int_)g=g.nb$add(a);else return g=Sk.abstr.numberBinOp(g,a,"Add"),new Sk.misceval.Break("slow")})}function e(){return Sk.misceval.iterFor(h,a=>{g=Sk.abstr.numberBinOp(g,a,"Add")})}Sk.builtin.pyCheckArgsLen("sum",arguments.length,1,2);const h=Sk.abstr.iter(a);if(void 0===b)var g=new Sk.builtin.int_(0);else{if(Sk.builtin.checkString(b))throw new Sk.builtin.TypeError("sum() can't sum strings [use ''.join(seq) instead]");g=b}let f;f=void 0===b||b.constructor===
Sk.builtin.int_?c():b.constructor===Sk.builtin.float_?"float":"slow";return Sk.misceval.chain(f,a=>"float"===a?d():a,a=>{if("slow"===a)return e()},()=>g)};Sk.builtin.zip=function(){var a,b;if(0===arguments.length)return new Sk.builtin.list([]);var c=[];for(b=0;b<arguments.length;b++)if(Sk.builtin.checkIterable(arguments[b]))c.push(Sk.abstr.iter(arguments[b]));else throw new Sk.builtin.TypeError("argument "+b+" must support iteration");var d=[];for(a=!1;!a;){var e=[];for(b=0;b<arguments.length;b++){var h=
c[b].tp$iternext();if(void 0===h){a=!0;break}e.push(h)}a||d.push(new Sk.builtin.tuple(e))}return new Sk.builtin.list(d)};Sk.builtin.abs=function(a){Sk.builtin.pyCheckArgsLen("abs",arguments.length,1,1);if(a.nb$abs)return a.nb$abs();throw new TypeError("bad operand type for abs(): '"+Sk.abstr.typeName(a)+"'");};Sk.builtin.fabs=function(a){return Sk.builtin.abs(a)};Sk.builtin.ord=function(a){Sk.builtin.pyCheckArgsLen("ord",arguments.length,1,1);if(Sk.builtin.checkString(a)){if(1!==a.v.length&&1!==a.sq$length())throw new Sk.builtin.TypeError("ord() expected a character, but string of length "+
a.v.length+" found");return new Sk.builtin.int_(a.v.codePointAt(0))}if(Sk.builtin.checkBytes(a)){if(1!==a.sq$length())throw new Sk.builtin.TypeError("ord() expected a character, but string of length "+a.v.length+" found");return new Sk.builtin.int_(a.v[0])}throw new Sk.builtin.TypeError("ord() expected a string of length 1, but "+Sk.abstr.typeName(a)+" found");};Sk.builtin.chr=function(a){Sk.builtin.pyCheckArgsLen("chr",arguments.length,1,1);if(!Sk.builtin.checkInt(a))throw new Sk.builtin.TypeError("an integer is required");
a=Sk.builtin.asnum$(a);if(Sk.__future__.python3){if(0>a||1114112<=a)throw new Sk.builtin.ValueError("chr() arg not in range(0x110000)");}else if(0>a||256<=a)throw new Sk.builtin.ValueError("chr() arg not in range(256)");return new Sk.builtin.str(String.fromCodePoint(a))};Sk.builtin.unichr=function(a){Sk.builtin.pyCheckArgsLen("chr",arguments.length,1,1);if(!Sk.builtin.checkInt(a))throw new Sk.builtin.TypeError("an integer is required");a=Sk.builtin.asnum$(a);try{return new Sk.builtin.str(String.fromCodePoint(a))}catch(b){if(b instanceof
RangeError)throw new Sk.builtin.ValueError(b.message);throw b;}};Sk.builtin.int2str_=function(a,b,c){if(a instanceof Sk.builtin.lng){var d="";2===b||Sk.__future__.python3||(d="L");b=a.str$(b,!1);return a.nb$isnegative()?new Sk.builtin.str("-"+c+b+d):new Sk.builtin.str(c+b+d)}a=Sk.misceval.asIndex(a);b=a.toString(b);return 0>a?new Sk.builtin.str("-"+c+b.slice(1)):new Sk.builtin.str(c+b)};Sk.builtin.hex=function(a){Sk.builtin.pyCheckArgsLen("hex",arguments.length,1,1);if(!Sk.misceval.isIndex(a))throw new Sk.builtin.TypeError("hex() argument can't be converted to hex");
return Sk.builtin.int2str_(a,16,"0x")};Sk.builtin.oct=function(a){Sk.builtin.pyCheckArgsLen("oct",arguments.length,1,1);if(!Sk.misceval.isIndex(a))throw new Sk.builtin.TypeError("oct() argument can't be converted to hex");return Sk.__future__.octal_number_literal?Sk.builtin.int2str_(a,8,"0o"):Sk.builtin.int2str_(a,8,"0")};Sk.builtin.bin=function(a){Sk.builtin.pyCheckArgsLen("bin",arguments.length,1,1);if(!Sk.misceval.isIndex(a))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(a)+"' object can't be interpreted as an index");
return Sk.builtin.int2str_(a,2,"0b")};Sk.builtin.dir=function(a){var b,c;Sk.builtin.pyCheckArgsLen("dir",arguments.length,1,1);var d=function(a){var c=null;if(-1!=="__bases__ __mro__ __class__ __name__ GenericGetAttr GenericSetAttr GenericPythonGetAttr GenericPythonSetAttr pythonFunctions HashNotImplemented constructor __dict__".split(" ").indexOf(a))return null;a=Sk.unfixReserved(a);-1!==a.indexOf("$")?c=Sk.builtin.dir.slotNameToRichName(a):"_"!==a.charAt(a.length-1)?c=a:"_"===a.charAt(0)&&(c=a);
return c};var e=[];var h=Sk.abstr.lookupSpecial(a,Sk.builtin.str.$dir);if(null!=h){var g=Sk.misceval.callsimArray(h,[a]);if(!Sk.builtin.checkSequence(g))throw new Sk.builtin.TypeError("__dir__ must return sequence.");g=Sk.ffi.remapToJs(g);for(b=0;b<g.length;++b)e.push(new Sk.builtin.str(g[b]))}else{for(b in a.constructor.prototype)(c=d(b))&&e.push(new Sk.builtin.str(c));if(a.$d)if(a.$d.tp$iter)for(h=a.$d.tp$iter(),b=h.tp$iternext();void 0!==b;b=h.tp$iternext())c=new Sk.builtin.str(b),(c=d(c.v))&&
e.push(new Sk.builtin.str(c));else for(c in a.$d)e.push(new Sk.builtin.str(c));var f=a.tp$mro;!f&&a.ob$type&&(f=a.ob$type.tp$mro);if(f)for(b=0;b<f.v.length;++b)for(g in h=f.v[b],h)h.hasOwnProperty(g)&&(c=d(g))&&e.push(new Sk.builtin.str(c))}e.sort(function(a,c){return(a.v>c.v)-(a.v<c.v)});return new Sk.builtin.list(e.filter(function(a,c,b){return a!==b[c+1]}))};Sk.builtin.dir.slotNameToRichName=function(a){};Sk.builtin.repr=function(a){Sk.builtin.pyCheckArgsLen("repr",arguments.length,1,1);return Sk.misceval.objectRepr(a)};
Sk.builtin.ascii=function(a){return Sk.misceval.chain(Sk.misceval.objectRepr(a),a=>{if(!(a instanceof Sk.builtin.str))throw new Sk.builtin.TypeError("__repr__ returned non-string (type "+Sk.abstr.typeName(a)+")");let c,b;for(b=0;b<a.v.length;b++)if(127<=a.v.charCodeAt(b)){c=a.v.substr(0,b);break}if(!c)return a;for(;b<a.v.length;b++){var e=a.v.charAt(b),h=a.v.charCodeAt(b);127<h&&255>=h?(e=h.toString(16),2>e.length&&(e="0"+e),c+="\\x"+e):127<h&&55296>h||57344<=h?c+="\\u"+("000"+h.toString(16)).slice(-4):
55296<=h?(e=a.v.codePointAt(b),b++,e=e.toString(16),h="0000000"+e.toString(16),c=4<e.length?c+("\\U"+h.slice(-8)):c+("\\u"+h.slice(-4))):c+=e}return new Sk.builtin.str(c)})};Sk.builtin.open=function(a,b,c){Sk.builtin.pyCheckArgsLen("open",arguments.length,1,3);void 0===b&&(b=new Sk.builtin.str("r"));if(/\+/.test(b.v))throw"todo; haven't implemented read/write mode";if(("w"===b.v||"wb"===b.v||"a"===b.v||"ab"===b.v)&&!Sk.nonreadopen)throw"todo; haven't implemented non-read opens";return new Sk.builtin.file(a,
b,c)};Sk.builtin.isinstance=function(a,b){var c;Sk.builtin.pyCheckArgsLen("isinstance",arguments.length,2,2);if(!(Sk.builtin.checkClass(b)||b instanceof Sk.builtin.tuple))throw new Sk.builtin.TypeError("isinstance() arg 2 must be a class, type, or tuple of classes and types");if(b===Sk.builtin.none.prototype.ob$type)return a===Sk.builtin.none.none$?Sk.builtin.bool.true$:Sk.builtin.bool.false$;if(a.ob$type===b)return Sk.builtin.bool.true$;if(b instanceof Sk.builtin.tuple){for(c=0;c<b.v.length;++c)if(Sk.misceval.isTrue(Sk.builtin.isinstance(a,
b.v[c])))return Sk.builtin.bool.true$;return Sk.builtin.bool.false$}if(a instanceof b)return Sk.builtin.bool.true$;var d=function(a,c){if(a===c)return Sk.builtin.bool.true$;if(void 0===a.$d)return Sk.builtin.bool.false$;var b=a.$d.mp$subscript(Sk.builtin.type.basesStr_);for(a=0;a<b.v.length;++a)if(Sk.misceval.isTrue(d(b.v[a],c)))return Sk.builtin.bool.true$;return Sk.builtin.bool.false$};return d(a.ob$type,b)};Sk.builtin.hash=function(a){Sk.builtin.pyCheckArgsLen("hash",arguments.length,1,1);if(a instanceof
Object){if(Sk.builtin.checkNone(a.tp$hash))throw new Sk.builtin.TypeError(new Sk.builtin.str("unhashable type: '"+Sk.abstr.typeName(a)+"'"));if(void 0!==a.tp$hash){if(a.$savedHash_)return a.$savedHash_;a.$savedHash_=a.tp$hash();return a.$savedHash_}void 0===a.__hash&&(Sk.builtin.hashCount+=1,a.__hash=Sk.builtin.hashCount);return new Sk.builtin.int_(a.__hash)}if("number"===typeof a||null===a||!0===a||!1===a)throw new Sk.builtin.TypeError("unsupported Javascript type");return new Sk.builtin.str(typeof a+
" "+String(a))};Sk.builtin.getattr=function(a,b,c){Sk.builtin.pyCheckArgsLen("getattr",arguments.length,2,3);if(!Sk.builtin.checkString(b))throw new Sk.builtin.TypeError("attribute name must be string");var d=a.tp$getattr(b);if(void 0===d){if(void 0!==c)return c;throw new Sk.builtin.AttributeError("'"+Sk.abstr.typeName(a)+"' object has no attribute '"+b.$jsstr()+"'");}return d};Sk.builtin.setattr=function(a,b,c){Sk.builtin.pyCheckArgsLen("setattr",arguments.length,3,3);if(!Sk.builtin.checkString(b))throw new Sk.builtin.TypeError("attribute name must be string");
if(a.tp$setattr)a.tp$setattr(b,c);else throw new Sk.builtin.AttributeError("object has no attribute "+b.$jsstr());return Sk.builtin.none.none$};Sk.builtin.raw_input=function(a){var b=a?a:"";return Sk.misceval.chain(Sk.importModule("sys",!1,!0),function(a){return Sk.inputfunTakesPrompt?Sk.misceval.callsimOrSuspendArray(Sk.builtin.file.$readline,[a.$d.stdin,null,b]):Sk.misceval.chain(void 0,function(){return Sk.misceval.callsimOrSuspendArray(a.$d.stdout.write,[a.$d.stdout,new Sk.builtin.str(b)])},function(){return Sk.misceval.callsimOrSuspendArray(a.$d.stdin.readline,
[a.$d.stdin])})})};Sk.builtin.input=Sk.builtin.raw_input;Sk.builtin.jseval=function(a){a=Sk.global.eval(Sk.ffi.remapToJs(a));return Sk.ffi.remapToPy(a)};Sk.builtin.jsmillis=function(){return(new Date).valueOf()};Sk.builtin.eval_=function(){throw new Sk.builtin.NotImplementedError("eval is not yet implemented");};Sk.builtin.map=function(a,b){var c=[],d,e;Sk.builtin.pyCheckArgsLen("map",arguments.length,2);if(2<arguments.length){var h=[];var g=Array.prototype.slice.apply(arguments).slice(1);for(e=0;e<
g.length;e++){if(!Sk.builtin.checkIterable(g[e])){var f=parseInt(e,10)+2;throw new Sk.builtin.TypeError("argument "+f+" to map() must support iteration");}g[e]=Sk.abstr.iter(g[e])}for(;;){var k=[];for(e=d=0;e<g.length;e++)f=g[e].tp$iternext(),void 0===f?(k.push(Sk.builtin.none.none$),d++):k.push(f);if(d!==g.length)h.push(k);else break}b=new Sk.builtin.list(h)}if(!Sk.builtin.checkIterable(b))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(b)+"' object is not iterable");return Sk.misceval.chain(Sk.misceval.iterFor(Sk.abstr.iter(b),
function(b){if(a===Sk.builtin.none.none$)b instanceof Array&&(b=new Sk.builtin.tuple(b)),c.push(b);else return b instanceof Array||(b=[b]),Sk.misceval.chain(Sk.misceval.applyOrSuspend(a,void 0,void 0,void 0,b),function(a){c.push(a)})}),function(){return new Sk.builtin.list(c)})};Sk.builtin.reduce=function(a,b,c){var d;Sk.builtin.pyCheckArgsLen("reduce",arguments.length,2,3);if(!Sk.builtin.checkIterable(b))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(b)+"' object is not iterable");var e=Sk.abstr.iter(b);
if(void 0===c&&(c=e.tp$iternext(),void 0===c))throw new Sk.builtin.TypeError("reduce() of empty sequence with no initial value");var h=c;for(d=e.tp$iternext();void 0!==d;d=e.tp$iternext())h=Sk.misceval.callsimArray(a,[h,d]);return h};Sk.builtin.filter=function(a,b){var c;Sk.builtin.pyCheckArgsLen("filter",arguments.length,2,2);if(!Sk.builtin.checkIterable(b))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(b)+"' object is not iterable");var d=function(){return[]};var e=function(a,c){a.push(c);
return a};var h=function(a){return new Sk.builtin.list(a)};b.__class__===Sk.builtin.str?(d=function(){return new Sk.builtin.str("")},e=function(a,c){return a.sq$concat(c)},h=function(a){return a}):b.__class__===Sk.builtin.tuple&&(h=function(a){return new Sk.builtin.tuple(a)});var g=d();var f=Sk.abstr.iter(b);for(c=f.tp$iternext();void 0!==c;c=f.tp$iternext())d=a===Sk.builtin.none.none$?new Sk.builtin.bool(c):Sk.misceval.callsimArray(a,[c]),Sk.misceval.isTrue(d)&&(g=e(g,c));return h(g)};Sk.builtin.hasattr=
function(a,b){Sk.builtin.pyCheckArgsLen("hasattr",arguments.length,2,2);if(!Sk.builtin.checkString(b))throw new Sk.builtin.TypeError("hasattr(): attribute name must be string");if(a.tp$getattr)return a.tp$getattr(b)?Sk.builtin.bool.true$:Sk.builtin.bool.false$;throw new Sk.builtin.AttributeError("Object has no tp$getattr method");};Sk.builtin.pow=function(a,b,c){Sk.builtin.pyCheckArgsLen("pow",arguments.length,2,3);c===Sk.builtin.none.none$&&(c=void 0);if(Sk.builtin.checkComplex(a))return a.nb$power(b,
c);var d=Sk.builtin.asnum$(a);var e=Sk.builtin.asnum$(b);var h=Sk.builtin.asnum$(c);if(!Sk.builtin.checkNumber(a)||!Sk.builtin.checkNumber(b)){if(void 0===c)throw new Sk.builtin.TypeError("unsupported operand type(s) for pow(): '"+Sk.abstr.typeName(a)+"' and '"+Sk.abstr.typeName(b)+"'");throw new Sk.builtin.TypeError("unsupported operand type(s) for pow(): '"+Sk.abstr.typeName(a)+"', '"+Sk.abstr.typeName(b)+"', '"+Sk.abstr.typeName(c)+"'");}if(0>d&&b instanceof Sk.builtin.float_)throw new Sk.builtin.ValueError("negative number cannot be raised to a fractional power");
if(void 0===c){if(a instanceof Sk.builtin.float_||b instanceof Sk.builtin.float_||0>e)return new Sk.builtin.float_(Math.pow(d,e));h=new Sk.builtin.int_(d);e=new Sk.builtin.int_(e);e=h.nb$power(e);return a instanceof Sk.builtin.lng||b instanceof Sk.builtin.lng?new Sk.builtin.lng(e):e}if(!Sk.builtin.checkInt(a)||!Sk.builtin.checkInt(b)||!Sk.builtin.checkInt(c))throw new Sk.builtin.TypeError("pow() 3rd argument not allowed unless all arguments are integers");if(0>e){if(Sk.__future__.exceptions)throw new Sk.builtin.ValueError("pow() 2nd argument cannot be negative when 3rd argument specified");
throw new Sk.builtin.TypeError("pow() 2nd argument cannot be negative when 3rd argument specified");}if(0===h)throw new Sk.builtin.ValueError("pow() 3rd argument cannot be 0");return a instanceof Sk.builtin.lng||b instanceof Sk.builtin.lng||c instanceof Sk.builtin.lng||Infinity===Math.pow(d,e)?(a=new Sk.builtin.lng(a),a.nb$power(b,c)):(new Sk.builtin.int_(Math.pow(d,e))).nb$remainder(c)};Sk.builtin.quit=function(a){a=(new Sk.builtin.str(a)).v;throw new Sk.builtin.SystemExit(a);};Sk.builtin.issubclass=
function(a,b){var c;Sk.builtin.pyCheckArgsLen("issubclass",arguments.length,2,2);if(!Sk.builtin.checkClass(a))throw new Sk.builtin.TypeError("issubclass() arg 1 must be a class");if(!(Sk.builtin.checkClass(b)||b instanceof Sk.builtin.tuple))throw new Sk.builtin.TypeError("issubclass() arg 2 must be a class or tuple of classes");var d=function(a,c){if(a===c)return!0;if(void 0!==a.$d&&a.$d.mp$subscript)if(a.$d.sq$contains(Sk.builtin.type.basesStr_))var b=a.$d.mp$subscript(Sk.builtin.type.basesStr_);
else return!1;else return!1;for(a=0;a<b.v.length;++a)if(d(b.v[a],c))return!0;return!1};if(Sk.builtin.checkClass(b))return a===b?!0:d(a,b);if(b instanceof Sk.builtin.tuple){for(c=0;c<b.v.length;++c)if(Sk.builtin.issubclass(a,b.v[c]))return!0;return!1}};Sk.builtin.globals=function(){var a,b=new Sk.builtin.dict([]);for(a in Sk.globals){var c=Sk.unfixReserved(a);b.mp$ass_subscript(new Sk.builtin.str(c),Sk.globals[a])}return b};Sk.builtin.divmod=function(a,b){return Sk.abstr.numberBinOp(a,b,"DivMod")};
Sk.builtin.format=function(a,b){Sk.builtin.pyCheckArgsLen("format",arguments.length,1,2);void 0===b&&(b=Sk.builtin.str.$emptystr);return Sk.abstr.objectFormat(a,b)};Sk.builtin.reversed=function(a){Sk.builtin.pyCheckArgsLen("reversed",arguments.length,1,1);var b=Sk.abstr.lookupSpecial(a,Sk.builtin.str.$reversed);if(null!=b)return Sk.misceval.callsimArray(b,[a]);if(!Sk.builtin.checkSequence(a))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(a)+"' object is not a sequence");return new function(a){this.idx=
a.sq$length()-1;this.myobj=a;this.getitem=Sk.abstr.lookupSpecial(a,Sk.builtin.str.$getitem);this.tp$iter=function(){return this};this.tp$iternext=function(){if(!(0>this.idx)){try{var a=Sk.misceval.callsimArray(this.getitem,[this.myobj,Sk.ffi.remapToPy(this.idx)])}catch(e){if(e instanceof Sk.builtin.IndexError)return;throw e;}this.idx--;return a}}}(a)};Sk.builtin.id=function(a){Sk.builtin.pyCheckArgsLen("id",arguments.length,1,1);void 0===a.__id&&(Sk.builtin.idCount+=1,a.__id=Sk.builtin.idCount);return new Sk.builtin.int_(a.__id)};
Sk.builtin.bytearray=function(){throw new Sk.builtin.NotImplementedError("bytearray is not yet implemented");};Sk.builtin.callable=function(a){Sk.builtin.pyCheckArgsLen("callable",arguments.length,1,1);return Sk.builtin.checkCallable(a)?Sk.builtin.bool.true$:Sk.builtin.bool.false$};Sk.builtin.delattr=function(a,b){Sk.builtin.pyCheckArgsLen("delattr",arguments.length,2,2);if(void 0!==a.$d[b.v])return Sk.misceval.tryCatch(function(){return Sk.builtin.setattr(a,b,void 0)},function(c){Sk.misceval.tryCatch(function(){return Sk.builtin.setattr(a.$d,
b,void 0)},function(c){if(c instanceof Sk.builtin.AttributeError)throw new Sk.builtin.AttributeError(Sk.abstr.typeName(a)+" instance has no attribute '"+b.v+"'");throw c;})});if("type"!==a.$r().v.slice(1,5)){if(a.ob$type===Sk.builtin.type&&void 0!==a[b.v])return a[b.v]=void 0,Sk.builtin.none.none$;throw new Sk.builtin.AttributeError(Sk.abstr.typeName(a)+" instance has no attribute '"+b.v+"'");}throw new Sk.builtin.TypeError("can't set attributes of built-in/extension type '"+a.prototype.tp$name+"'");
};Sk.builtin.execfile=function(){throw new Sk.builtin.NotImplementedError("execfile is not yet implemented");};Sk.builtin.help=function(){throw new Sk.builtin.NotImplementedError("help is not yet implemented");};Sk.builtin.iter=function(a,b){Sk.builtin.pyCheckArgsLen("iter",arguments.length,1,2);if(1===arguments.length){if(Sk.builtin.checkIterable(a))return new Sk.builtin.iterator(a);throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(a)+"' object is not iterable");}if(Sk.builtin.checkCallable(a))return new Sk.builtin.iterator(a,
b);throw new TypeError("iter(v, w): v must be callable");};Sk.builtin.locals=function(){throw new Sk.builtin.NotImplementedError("locals is not yet implemented");};Sk.builtin.memoryview=function(){throw new Sk.builtin.NotImplementedError("memoryview is not yet implemented");};Sk.builtin.next_=function(a,b){Sk.builtin.pyCheckArgsLen("next",arguments.length,1,2);if(!a.tp$iternext)throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(a)+"' object is not an iterator");var c=a.tp$iternext();if(void 0===
c){if(b)return b;throw new Sk.builtin.StopIteration;}return c};Sk.builtin.reload=function(){throw new Sk.builtin.NotImplementedError("reload is not yet implemented");};Sk.builtin.vars=function(){throw new Sk.builtin.NotImplementedError("vars is not yet implemented");};Sk.builtin.xrange=Sk.builtin.range;Sk.builtin.apply_=function(){throw new Sk.builtin.NotImplementedError("apply is not yet implemented");};Sk.builtin.buffer=function(){throw new Sk.builtin.NotImplementedError("buffer is not yet implemented");
};Sk.builtin.coerce=function(){throw new Sk.builtin.NotImplementedError("coerce is not yet implemented");};Sk.builtin.intern=function(){throw new Sk.builtin.NotImplementedError("intern is not yet implemented");}},function(m,p){String.fromCodePoint||function(){var a=function(){try{var a={},c=Object.defineProperty;var b=c(a,"foo",a)&&c}catch(f){}return b}(),b=String.fromCharCode,c=Math.floor,d=function(a){var d=[],e=-1,f=arguments.length;if(!f)return"";for(var k="";++e<f;){var n=Number(arguments[e]);
if(!isFinite(n)||0>n||1114111<n||c(n)!=n)throw RangeError("Invalid code point: "+n);if(65535>=n)d.push(n);else{n-=65536;var l=(n>>10)+55296;n=n%1024+56320;d.push(l,n)}if(e+1==f||16384<d.length)k+=b.apply(null,d),d.length=0}return k};a?a(String,"fromCodePoint",{value:d,configurable:!0,writable:!0}):String.fromCodePoint=d}()},function(m,p){Sk.builtin.BaseException=function(...a){if(!(this instanceof Sk.builtin.BaseException)){var b=Object.create(Sk.builtin.BaseException.prototype);b.constructor.apply(b,
arguments);return b}this.traceback=[];"string"===typeof a[0]?(this.args=new Sk.builtin.tuple([new Sk.builtin.str(a[0])]),3<=a.length&&this.traceback.push({lineno:a[2],filename:a[1]||"<unknown>"})):this.args=new Sk.builtin.tuple(a)};Sk.abstr.setUpInheritance("BaseException",Sk.builtin.BaseException,Sk.builtin.object);Sk.builtin.BaseException.prototype.$r=function(){let a=this.tp$name;a+="("+this.args.v.map(a=>Sk.misceval.objectRepr(a).v).join(", ")+")";return new Sk.builtin.str(a)};Sk.builtin.BaseException.prototype.tp$str=
function(){return 1>=this.args.v.length?new Sk.builtin.str(this.args.v[0]):this.args.$r()};Sk.builtin.BaseException.prototype.toString=function(){let a=this.tp$name;a+=": "+this.tp$str().v;return a=0!==this.traceback.length?a+(" on line "+this.traceback[0].lineno):a+" at <unknown>"};Sk.builtin.BaseException.prototype.args={tp$descr_get:function(a,b){return a.args}};Sk.exportSymbol("Sk.builtin.BaseException",Sk.builtin.BaseException);Sk.builtin.Exception=function(a){if(!(this instanceof Sk.builtin.Exception)){var b=
Object.create(Sk.builtin.Exception.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.BaseException.apply(this,arguments)};Sk.abstr.setUpInheritance("Exception",Sk.builtin.Exception,Sk.builtin.BaseException);Sk.exportSymbol("Sk.builtin.Exception",Sk.builtin.Exception);Sk.builtin.AssertionError=function(a){if(!(this instanceof Sk.builtin.AssertionError)){var b=Object.create(Sk.builtin.AssertionError.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.Exception.apply(this,arguments)};
Sk.abstr.setUpInheritance("AssertionError",Sk.builtin.AssertionError,Sk.builtin.Exception);Sk.exportSymbol("Sk.builtin.AssertionError",Sk.builtin.AssertionError);Sk.builtin.AttributeError=function(a){if(!(this instanceof Sk.builtin.AttributeError)){var b=Object.create(Sk.builtin.AttributeError.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.Exception.apply(this,arguments)};Sk.abstr.setUpInheritance("AttributeError",Sk.builtin.AttributeError,Sk.builtin.Exception);Sk.builtin.ImportError=
function(a){if(!(this instanceof Sk.builtin.ImportError)){var b=Object.create(Sk.builtin.ImportError.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.Exception.apply(this,arguments)};Sk.abstr.setUpInheritance("ImportError",Sk.builtin.ImportError,Sk.builtin.Exception);Sk.builtin.IndentationError=function(a){if(!(this instanceof Sk.builtin.IndentationError)){var b=Object.create(Sk.builtin.IndentationError.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.Exception.apply(this,
arguments)};Sk.abstr.setUpInheritance("IndentationError",Sk.builtin.IndentationError,Sk.builtin.Exception);Sk.builtin.IndexError=function(a){if(!(this instanceof Sk.builtin.IndexError)){var b=Object.create(Sk.builtin.IndexError.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.Exception.apply(this,arguments)};Sk.abstr.setUpInheritance("IndexError",Sk.builtin.IndexError,Sk.builtin.Exception);Sk.builtin.LookupError=function(a){if(!(this instanceof Sk.builtin.LookupError)){var b=Object.create(Sk.builtin.LookupError.prototype);
b.constructor.apply(b,arguments);return b}Sk.builtin.Exception.apply(this,arguments)};Sk.abstr.setUpInheritance("LookupError",Sk.builtin.LookupError,Sk.builtin.Exception);Sk.exportSymbol("Sk.builtin.LookupError",Sk.builtin.LookupError);Sk.builtin.KeyError=function(a){if(!(this instanceof Sk.builtin.KeyError)){var b=Object.create(Sk.builtin.KeyError.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.LookupError.apply(this,arguments)};Sk.abstr.setUpInheritance("KeyError",Sk.builtin.KeyError,
Sk.builtin.LookupError);Sk.builtin.NameError=function(a){if(!(this instanceof Sk.builtin.NameError)){var b=Object.create(Sk.builtin.NameError.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.Exception.apply(this,arguments)};Sk.abstr.setUpInheritance("NameError",Sk.builtin.NameError,Sk.builtin.Exception);Sk.builtin.UnboundLocalError=function(a){if(!(this instanceof Sk.builtin.UnboundLocalError)){var b=Object.create(Sk.builtin.UnboundLocalError.prototype);b.constructor.apply(b,arguments);
return b}Sk.builtin.Exception.apply(this,arguments)};Sk.abstr.setUpInheritance("UnboundLocalError",Sk.builtin.UnboundLocalError,Sk.builtin.Exception);Sk.builtin.OverflowError=function(a){if(!(this instanceof Sk.builtin.OverflowError)){var b=Object.create(Sk.builtin.OverflowError.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.Exception.apply(this,arguments)};Sk.abstr.setUpInheritance("OverflowError",Sk.builtin.OverflowError,Sk.builtin.Exception);Sk.builtin.SyntaxError=function(a){if(!(this instanceof
Sk.builtin.SyntaxError)){var b=Object.create(Sk.builtin.SyntaxError.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.Exception.apply(this,arguments)};Sk.abstr.setUpInheritance("SyntaxError",Sk.builtin.SyntaxError,Sk.builtin.Exception);Sk.builtin.RuntimeError=function(a){if(!(this instanceof Sk.builtin.RuntimeError)){var b=Object.create(Sk.builtin.RuntimeError.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.Exception.apply(this,arguments)};Sk.abstr.setUpInheritance("RuntimeError",
Sk.builtin.RuntimeError,Sk.builtin.Exception);Sk.exportSymbol("Sk.builtin.RuntimeError",Sk.builtin.RuntimeError);Sk.builtin.SuspensionError=function(a){if(!(this instanceof Sk.builtin.SuspensionError)){var b=Object.create(Sk.builtin.SuspensionError.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.Exception.apply(this,arguments)};Sk.abstr.setUpInheritance("SuspensionError",Sk.builtin.SuspensionError,Sk.builtin.Exception);Sk.exportSymbol("Sk.builtin.SuspensionError",Sk.builtin.SuspensionError);
Sk.builtin.SystemExit=function(a){if(!(this instanceof Sk.builtin.SystemExit)){var b=Object.create(Sk.builtin.SystemExit.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.BaseException.apply(this,arguments)};Sk.abstr.setUpInheritance("SystemExit",Sk.builtin.SystemExit,Sk.builtin.BaseException);Sk.exportSymbol("Sk.builtin.SystemExit",Sk.builtin.SystemExit);Sk.builtin.TypeError=function(a){if(!(this instanceof Sk.builtin.TypeError)){var b=Object.create(Sk.builtin.TypeError.prototype);
b.constructor.apply(b,arguments);return b}Sk.builtin.Exception.apply(this,arguments)};Sk.abstr.setUpInheritance("TypeError",Sk.builtin.TypeError,Sk.builtin.Exception);Sk.exportSymbol("Sk.builtin.TypeError",Sk.builtin.TypeError);Sk.builtin.ValueError=function(a){if(!(this instanceof Sk.builtin.ValueError)){var b=Object.create(Sk.builtin.ValueError.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.Exception.apply(this,arguments)};Sk.abstr.setUpInheritance("ValueError",Sk.builtin.ValueError,
Sk.builtin.Exception);Sk.exportSymbol("Sk.builtin.ValueError",Sk.builtin.ValueError);Sk.builtin.ZeroDivisionError=function(a){if(!(this instanceof Sk.builtin.ZeroDivisionError)){var b=Object.create(Sk.builtin.ZeroDivisionError.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.Exception.apply(this,arguments)};Sk.abstr.setUpInheritance("ZeroDivisionError",Sk.builtin.ZeroDivisionError,Sk.builtin.Exception);Sk.builtin.TimeLimitError=function(a){if(!(this instanceof Sk.builtin.TimeLimitError)){var b=
Object.create(Sk.builtin.TimeLimitError.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.Exception.apply(this,arguments)};Sk.abstr.setUpInheritance("TimeLimitError",Sk.builtin.TimeLimitError,Sk.builtin.Exception);Sk.exportSymbol("Sk.builtin.TimeLimitError",Sk.builtin.TimeLimitError);Sk.builtin.IOError=function(a){if(!(this instanceof Sk.builtin.IOError)){var b=Object.create(Sk.builtin.IOError.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.Exception.apply(this,arguments)};
Sk.abstr.setUpInheritance("IOError",Sk.builtin.IOError,Sk.builtin.Exception);Sk.exportSymbol("Sk.builtin.IOError",Sk.builtin.IOError);Sk.builtin.NotImplementedError=function(a){if(!(this instanceof Sk.builtin.NotImplementedError)){var b=Object.create(Sk.builtin.NotImplementedError.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.Exception.apply(this,arguments)};Sk.abstr.setUpInheritance("NotImplementedError",Sk.builtin.NotImplementedError,Sk.builtin.Exception);Sk.exportSymbol("Sk.builtin.NotImplementedError",
Sk.builtin.NotImplementedError);Sk.builtin.NegativePowerError=function(a){if(!(this instanceof Sk.builtin.NegativePowerError)){var b=Object.create(Sk.builtin.NegativePowerError.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.Exception.apply(this,arguments)};Sk.abstr.setUpInheritance("NegativePowerError",Sk.builtin.NegativePowerError,Sk.builtin.Exception);Sk.exportSymbol("Sk.builtin.NegativePowerError",Sk.builtin.NegativePowerError);Sk.builtin.ExternalError=function(a,b){if(!(this instanceof
Sk.builtin.ExternalError)){var c=Object.create(Sk.builtin.ExternalError.prototype);c.constructor.apply(c,arguments);return c}b=Array.prototype.slice.call(arguments);this.nativeError=b[0];b[0]instanceof Sk.builtin.str||(b[0]=""+b[0]);Sk.builtin.Exception.apply(this,b)};Sk.abstr.setUpInheritance("ExternalError",Sk.builtin.ExternalError,Sk.builtin.Exception);Sk.exportSymbol("Sk.builtin.ExternalError",Sk.builtin.ExternalError);Sk.builtin.OperationError=function(a){if(!(this instanceof Sk.builtin.OperationError)){var b=
Object.create(Sk.builtin.OperationError.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.Exception.apply(this,arguments)};Sk.abstr.setUpInheritance("OperationError",Sk.builtin.OperationError,Sk.builtin.Exception);Sk.exportSymbol("Sk.builtin.OperationError",Sk.builtin.OperationError);Sk.builtin.SystemError=function(a){if(!(this instanceof Sk.builtin.SystemError)){var b=Object.create(Sk.builtin.SystemError.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.Exception.apply(this,
arguments)};Sk.abstr.setUpInheritance("SystemError",Sk.builtin.SystemError,Sk.builtin.Exception);Sk.exportSymbol("Sk.builtin.SystemError",Sk.builtin.SystemError);Sk.builtin.UnicodeDecodeError=function(a){if(!(this instanceof Sk.builtin.UnicodeDecodeError)){var b=Object.create(Sk.builtin.UnicodeDecodeError.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.Exception.apply(this,arguments)};Sk.abstr.setUpInheritance("UnicodeDecodeError",Sk.builtin.UnicodeDecodeError,Sk.builtin.Exception);
Sk.exportSymbol("Sk.builtin.UnicodeDecodeError",Sk.builtin.UnicodeDecodeError);Sk.builtin.UnicodeEncodeError=function(a){if(!(this instanceof Sk.builtin.UnicodeEncodeError)){var b=Object.create(Sk.builtin.UnicodeEncodeError.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.Exception.apply(this,arguments)};Sk.abstr.setUpInheritance("UnicodeEncodeError",Sk.builtin.UnicodeEncodeError,Sk.builtin.Exception);Sk.exportSymbol("Sk.builtin.UnicodeEncodeError",Sk.builtin.UnicodeEncodeError);Sk.builtin.StopIteration=
function(a){if(!(this instanceof Sk.builtin.StopIteration)){var b=Object.create(Sk.builtin.StopIteration.prototype);b.constructor.apply(b,arguments);return b}Sk.builtin.Exception.apply(this,arguments)};Sk.abstr.setUpInheritance("StopIteration",Sk.builtin.StopIteration,Sk.builtin.Exception);Sk.exportSymbol("Sk.builtin.StopIteration",Sk.builtin.StopIteration);Sk.builtin.getExcInfo=function(a){return new Sk.builtin.tuple([a.ob$type||Sk.builtin.none.none$,a,Sk.builtin.none.none$])}},function(m,p){Sk.builtin.method=
function(a,b,c,d){if(!(this instanceof Sk.builtin.method)){Sk.builtin.pyCheckArgsLen("method",arguments.length,3,3);if(!Sk.builtin.checkCallable(a))throw new Sk.builtin.TypeError("First argument must be callable");if(void 0===b.ob$type)throw new Sk.builtin.TypeError("Second argument must be object of known type");return new Sk.builtin.method(a,b,c)}this.tp$name=a.tp$name;this.im_func=a;this.im_self=b||Sk.builtin.none.none$;this.im_class=c||Sk.builtin.none.none$;this.im_builtin=d;this.$d={im_func:a,
im_self:b,im_class:c}};Sk.exportSymbol("Sk.builtin.method",Sk.builtin.method);Sk.abstr.setUpInheritance("instancemethod",Sk.builtin.method,Sk.builtin.object);Sk.builtin.method.prototype.tp$name="method";Sk.builtin.method.prototype.ob$eq=function(a){if(this.im_self==Sk.builtin.none.none$&&a.im_self!=Sk.builtin.none.none$||a.im_self==Sk.builtin.none.none$&&this.im_self!=Sk.builtin.none.none$)return!1;try{return Sk.misceval.richCompareBool(this.im_self,a.im_self,"Eq",!1)&&this.im_func==a.im_func}catch(b){return!1}};
Sk.builtin.method.prototype.ob$ne=function(a){return!this.ob$eq(a)};Sk.builtin.method.prototype.tp$hash=function(){var a=this.im_self==Sk.builtin.none.none$?0:Sk.builtin.asnum$(Sk.builtin.hash(this.im_self));var b=Sk.builtin.asnum$(Sk.builtin.hash(this.im_func));return new Sk.builtin.int_(a+b)};Sk.builtin.method.prototype.tp$call=function(a,b){this.im_self!==Sk.builtin.none.none$&&a.unshift(this.im_self);if(this.im_self===Sk.builtin.none.none$){var c=function(a){return"unbound method "+this.tp$name+
"() must be called with "+Sk.abstr.typeName(this.im_class)+" instance as first argument (got "+a+" instead)"}.bind(this);if(0<a.length){if(this.im_class!=Sk.builtin.none.none$&&!Sk.builtin.issubclass(a[0].ob$type,this.im_class)&&!this.im_builtin)throw new Sk.builtin.TypeError(c(Sk.abstr.typeName(a[0].ob$type)+" instance"));}else throw new Sk.builtin.TypeError(c("nothing"));}return this.im_func.tp$call(a,b)};Sk.builtin.method.prototype.tp$descr_get=function(a,b){Sk.asserts.assert(void 0!==a&&void 0!==
b);return new Sk.builtin.method(this,a,b,this.im_builtin)};Sk.builtin.method.pythonFunctions=["__get__"];Sk.builtin.method.prototype.__get__=function(a,b,c){Sk.builtin.pyCheckArgsLen("__get__",arguments.length,1,2,!1,!0);if(b===Sk.builtin.none.none$&&c===Sk.builtin.none.none$)throw new Sk.builtin.TypeError("__get__(None, None) is invalid");return c&&c!==Sk.builtin.none.none$?Sk.builtin.issubclass(c,a.im_class)?a.tp$descr_get(b,c):a:a.tp$descr_get(b,Sk.builtin.none.none$)};Sk.builtin.method.prototype.$r=
function(){return this.im_builtin?new Sk.builtin.str("<built-in method "+this.tp$name+" of type object>"):this.im_self===Sk.builtin.none.none$?new Sk.builtin.str("<unbound method "+this.im_class.prototype.tp$name+"."+this.tp$name+">"):new Sk.builtin.str("<bound method "+(this.im_class!==Sk.builtin.none.none$?this.im_class.prototype.tp$name:"?")+"."+this.tp$name+" of "+Sk.ffi.remapToJs(Sk.misceval.objectRepr(this.im_self))+">")}},function(m,p){Sk.misceval={};Sk.misceval.Suspension=function(a,b,c){this.$isSuspension=
!0;void 0!==a&&void 0!==b&&(this.resume=function(){return a(b.resume())});this.child=b;this.optional=void 0!==b&&b.optional;this.data=void 0===c&&void 0!==b?b.data:c};Sk.exportSymbol("Sk.misceval.Suspension",Sk.misceval.Suspension);Sk.misceval.retryOptionalSuspensionOrThrow=function(a,b){for(;a instanceof Sk.misceval.Suspension;){if(!a.optional)throw new Sk.builtin.SuspensionError(b||"Cannot call a function that blocks or suspends here");a=a.resume()}return a};Sk.exportSymbol("Sk.misceval.retryOptionalSuspensionOrThrow",
Sk.misceval.retryOptionalSuspensionOrThrow);Sk.misceval.isIndex=function(a){return Sk.builtin.checkInt(a)||Sk.abstr.lookupSpecial(a,Sk.builtin.str.$index)?!0:!1};Sk.exportSymbol("Sk.misceval.isIndex",Sk.misceval.isIndex);Sk.misceval.asIndex=function(a){var b;if(Sk.misceval.isIndex(a)&&null!==a){if(!0===a)return 1;if(!1===a)return 0;if("number"===typeof a)return a;if(a.constructor===Sk.builtin.int_)return a.v;if(a.constructor===Sk.builtin.lng)return a.cantBeInt()?a.str$(10,!0):a.toInt$();if(a.constructor===
Sk.builtin.bool)return Sk.builtin.asnum$(a);if(b=Sk.abstr.lookupSpecial(a,Sk.builtin.str.$index)){a=Sk.misceval.callsimArray(b,[a]);if(!Sk.builtin.checkInt(a))throw new Sk.builtin.TypeError("__index__ returned non-(int,long) (type "+Sk.abstr.typeName(a)+")");return Sk.builtin.asnum$(a)}Sk.asserts.fail("todo asIndex;")}};Sk.misceval.applySlice=function(a,b,c,d){return a.sq$slice&&Sk.misceval.isIndex(b)&&Sk.misceval.isIndex(c)?(b=Sk.misceval.asIndex(b),void 0===b&&(b=0),c=Sk.misceval.asIndex(c),void 0===
c&&(c=1E100),Sk.abstr.sequenceGetSlice(a,b,c)):Sk.abstr.objectGetItem(a,new Sk.builtin.slice(b,c,null),d)};Sk.exportSymbol("Sk.misceval.applySlice",Sk.misceval.applySlice);Sk.misceval.assignSlice=function(a,b,c,d,e){if(a.sq$ass_slice&&Sk.misceval.isIndex(b)&&Sk.misceval.isIndex(c))e=Sk.misceval.asIndex(b)||0,c=Sk.misceval.asIndex(c)||1E100,null===d?Sk.abstr.sequenceDelSlice(a,e,c):Sk.abstr.sequenceSetSlice(a,e,c,d);else return c=new Sk.builtin.slice(b,c),null===d?Sk.abstr.objectDelItem(a,c):Sk.abstr.objectSetItem(a,
c,d,e)};Sk.exportSymbol("Sk.misceval.assignSlice",Sk.misceval.assignSlice);Sk.misceval.arrayFromArguments=function(a){var b;if(1!=a.length)return a;var c=a[0];c instanceof Sk.builtin.set?c=c.tp$iter().$obj:c instanceof Sk.builtin.dict&&(c=Sk.builtin.dict.prototype.keys.func_code(c));if(c instanceof Sk.builtin.list||c instanceof Sk.builtin.tuple)return c.v;if(Sk.builtin.checkIterable(c)){a=[];c=Sk.abstr.iter(c);for(b=c.tp$iternext();void 0!==b;b=c.tp$iternext())a.push(b);return a}throw new Sk.builtin.TypeError("'"+
Sk.abstr.typeName(c)+"' object is not iterable");};Sk.exportSymbol("Sk.misceval.arrayFromArguments",Sk.misceval.arrayFromArguments);Sk.misceval.swappedOp_={Eq:"Eq",NotEq:"NotEq",Lt:"Gt",LtE:"GtE",Gt:"Lt",GtE:"LtE"};Sk.misceval.opSymbols={Eq:"==",NotEq:"!=",Lt:"<",LtE:"<=",Gt:">",GtE:">=",Is:"is",IsNot:"is not",In_:"in",NotIn:"not in"};Sk.misceval.richCompareBool=function(a,b,c,d){var e;Sk.asserts.assert(null!==a&&void 0!==a,"passed null or undefined parameter to Sk.misceval.richCompareBool");Sk.asserts.assert(null!==
b&&void 0!==b,"passed null or undefined parameter to Sk.misceval.richCompareBool");var h=a.ob$type;var g=b.ob$type;if(!Sk.__future__.python3&&h!==g&&("GtE"===c||"Gt"===c||"LtE"===c||"Lt"===c)){var f=[Sk.builtin.float_,Sk.builtin.int_,Sk.builtin.lng,Sk.builtin.bool],k=[Sk.builtin.dict,Sk.builtin.enumerate,Sk.builtin.filter_,Sk.builtin.list,Sk.builtin.map_,Sk.builtin.str,Sk.builtin.tuple,Sk.builtin.zip_];const d=f.indexOf(h),e=k.indexOf(h);f=f.indexOf(g);k=k.indexOf(g);if(a===Sk.builtin.none.none$)switch(c){case "Lt":return!0;
case "LtE":return!0;case "Gt":return!1;case "GtE":return!1}if(b===Sk.builtin.none.none$)switch(c){case "Lt":return!1;case "LtE":return!1;case "Gt":return!0;case "GtE":return!0}if(-1!==d&&-1!==k)switch(c){case "Lt":return!0;case "LtE":return!0;case "Gt":return!1;case "GtE":return!1}if(-1!==e&&-1!==f)switch(c){case "Lt":return!1;case "LtE":return!1;case "Gt":return!0;case "GtE":return!0}if(-1!==e&&-1!==k)switch(c){case "Lt":return e<k;case "LtE":return e<=k;case "Gt":return e>k;case "GtE":return e>=
k}}if("Is"===c){if(h===g){if(a===b)return!0;if(h===Sk.builtin.float_||h===Sk.builtin.int_)return a.v===b.v;if(h===Sk.builtin.lng)return 0===a.longCompare(b)}return!1}if("IsNot"===c)return h!==g?!0:h===Sk.builtin.float_||h===Sk.builtin.int_?a.v!==b.v:h===Sk.builtin.lng?0!==a.longCompare(b):a!==b;if("In"===c)return Sk.misceval.chain(Sk.abstr.sequenceContains(b,a,d),Sk.misceval.isTrue);if("NotIn"===c)return Sk.misceval.chain(Sk.abstr.sequenceContains(b,a,d),function(a){return!Sk.misceval.isTrue(a)});
g={Eq:"ob$eq",NotEq:"ob$ne",Gt:"ob$gt",GtE:"ob$ge",Lt:"ob$lt",LtE:"ob$le"};h=g[c];if((d=a.constructor.prototype.hasOwnProperty(h))&&(e=a[h](b))!==Sk.builtin.NotImplemented.NotImplemented$)return Sk.misceval.isTrue(e);g=g[Sk.misceval.swappedOp_[c]];if((h=b.constructor.prototype.hasOwnProperty(g))&&(e=b[g](a))!==Sk.builtin.NotImplemented.NotImplemented$)return Sk.misceval.isTrue(e);if(a.tp$richcompare&&void 0!==(e=a.tp$richcompare(b,c))&&e!==Sk.builtin.NotImplemented.NotImplemented$||b.tp$richcompare&&
void 0!==(e=b.tp$richcompare(a,Sk.misceval.swappedOp_[c]))&&e!==Sk.builtin.NotImplemented.NotImplemented$)return Sk.misceval.isTrue(e);if((g=Sk.abstr.lookupSpecial(a,Sk.misceval.op2method_[c]))&&!d&&(e=Sk.misceval.callsimArray(g,[a,b]),e!=Sk.builtin.NotImplemented.NotImplemented$)||(d=Sk.abstr.lookupSpecial(b,Sk.misceval.op2method_[Sk.misceval.swappedOp_[c]]))&&!h&&(e=Sk.misceval.callsimArray(d,[b,a]),e!=Sk.builtin.NotImplemented.NotImplemented$))return Sk.misceval.isTrue(e);if(!Sk.__future__.python3){if(d=
Sk.abstr.lookupSpecial(a,Sk.builtin.str.$cmp))try{e=Sk.misceval.callsimArray(d,[a,b]);if(Sk.builtin.checkNumber(e)){e=Sk.builtin.asnum$(e);if("Eq"===c)return 0===e;if("NotEq"===c)return 0!==e;if("Lt"===c)return 0>e;if("Gt"===c)return 0<e;if("LtE"===c)return 0>=e;if("GtE"===c)return 0<=e}if(e!==Sk.builtin.NotImplemented.NotImplemented$)throw new Sk.builtin.TypeError("comparison did not return an int");}catch(n){throw new Sk.builtin.TypeError("comparison did not return an int");}if(d=Sk.abstr.lookupSpecial(b,
Sk.builtin.str.$cmp))try{e=Sk.misceval.callsimArray(d,[b,a]);if(Sk.builtin.checkNumber(e)){e=Sk.builtin.asnum$(e);if("Eq"===c)return 0===e;if("NotEq"===c)return 0!==e;if("Lt"===c)return 0<e;if("Gt"===c)return 0>e;if("LtE"===c)return 0<=e;if("GtE"===c)return 0>=e}if(e!==Sk.builtin.NotImplemented.NotImplemented$)throw new Sk.builtin.TypeError("comparison did not return an int");}catch(n){throw new Sk.builtin.TypeError("comparison did not return an int");}if(a===Sk.builtin.none.none$&&b===Sk.builtin.none.none$){if("Eq"===
c)return a.v===b.v;if("NotEq"===c)return a.v!==b.v;if("Gt"===c)return a.v>b.v;if("GtE"===c)return a.v>=b.v;if("Lt"===c)return a.v<b.v;if("LtE"===c)return a.v<=b.v}}if("Eq"===c)return a===b;if("NotEq"===c)return a!==b;a=Sk.abstr.typeName(a);b=Sk.abstr.typeName(b);throw new Sk.builtin.TypeError("'"+Sk.misceval.opSymbols[c]+"' not supported between instances of '"+a+"' and '"+b+"'");};Sk.exportSymbol("Sk.misceval.richCompareBool",Sk.misceval.richCompareBool);Sk.misceval.objectRepr=function(a){Sk.asserts.assert(void 0!==
a,"trying to repr undefined");return null===a||a===Sk.builtin.none.none$?new Sk.builtin.str("None"):!0===a?new Sk.builtin.str("True"):!1===a?new Sk.builtin.str("False"):"number"===typeof a?new Sk.builtin.str(""+a):"string"===typeof a?new Sk.builtin.str(a):a.$r?a.constructor===Sk.builtin.float_?Infinity===a.v?new Sk.builtin.str("inf"):-Infinity===a.v?new Sk.builtin.str("-inf"):a.$r():a.$r():a.tp$name?new Sk.builtin.str("<"+a.tp$name+" object>"):new Sk.builtin.str("<unknown>")};Sk.exportSymbol("Sk.misceval.objectRepr",
Sk.misceval.objectRepr);Sk.misceval.opAllowsEquality=function(a){switch(a){case "LtE":case "Eq":case "GtE":return!0}return!1};Sk.exportSymbol("Sk.misceval.opAllowsEquality",Sk.misceval.opAllowsEquality);Sk.misceval.isTrue=function(a){if(!0===a)return!0;if(!1===a||null===a||a.constructor===Sk.builtin.none||a.constructor===Sk.builtin.NotImplemented)return!1;if(a.constructor===Sk.builtin.bool)return a.v;if("number"===typeof a)return 0!==a;if(a instanceof Sk.builtin.lng)return a.nb$nonzero();if(a.constructor===
Sk.builtin.int_||a.constructor===Sk.builtin.float_)return 0!==a.v;if(Sk.__future__.python3){if(a.nb$bool){a=a.nb$bool();if(!(a instanceof Sk.builtin.bool))throw new Sk.builtin.TypeError("__bool__ should return bool, returned "+Sk.abstr.typeName(a));return a.v}}else if(a.nb$nonzero){a=a.nb$nonzero();if(!Sk.builtin.checkInt(a))throw new Sk.builtin.TypeError("__nonzero__ should return an int");return 0!==Sk.builtin.asnum$(a)}if(a.sq$length){a=a.sq$length();if(!Sk.builtin.checkInt(a))throw new Sk.builtin.TypeError("__len__ should return an int");
return 0!==Sk.builtin.asnum$(a)}return a.mp$length?0!==Sk.builtin.asnum$(a.mp$length()):a.sq$length?0!==Sk.builtin.asnum$(a.sq$length()):!0};Sk.exportSymbol("Sk.misceval.isTrue",Sk.misceval.isTrue);Sk.misceval.softspace_=!1;Sk.misceval.print_=function(a){Sk.misceval.softspace_&&("\n"!==a&&Sk.output(" "),Sk.misceval.softspace_=!1);var b=new Sk.builtin.str(a);return Sk.misceval.chain(Sk.importModule("sys",!1,!0),function(a){return Sk.misceval.apply(a.$d.stdout.write,void 0,void 0,void 0,[a.$d.stdout,
b])},function(){var a;(a=0===b.v.length)||(a=b.v[b.v.length-1],a=!("\n"===a||"\t"===a||"\r"===a));if(a||" "===b.v[b.v.length-1])Sk.misceval.softspace_=!0})};Sk.exportSymbol("Sk.misceval.print_",Sk.misceval.print_);Sk.misceval.loadname=function(a,b){b=b[a];if(void 0!==b)return"function"===typeof b&&void 0===b.$d&&void 0===b.tp$name?b():b;b=Sk.builtins[a];if(void 0!==b)return b;throw new Sk.builtin.NameError("name '"+Sk.unfixReserved(a)+"' is not defined");};Sk.exportSymbol("Sk.misceval.loadname",Sk.misceval.loadname);
Sk.misceval.call=function(a,b,c,d,e){e=Array.prototype.slice.call(arguments,4);return Sk.misceval.apply(a,b,c,d,e)};Sk.exportSymbol("Sk.misceval.call",Sk.misceval.call);Sk.misceval.callAsync=function(a,b,c,d,e,h){h=Array.prototype.slice.call(arguments,5);return Sk.misceval.applyAsync(a,b,c,d,e,h)};Sk.exportSymbol("Sk.misceval.callAsync",Sk.misceval.callAsync);Sk.misceval.callOrSuspend=function(a,b,c,d,e){e=Array.prototype.slice.call(arguments,4);return Sk.misceval.applyOrSuspend(a,b,c,d,e)};Sk.exportSymbol("Sk.misceval.callOrSuspend",
Sk.misceval.callOrSuspend);Sk.misceval.callsim=function(a,b){b=Array.prototype.slice.call(arguments,1);return Sk.misceval.apply(a,void 0,void 0,void 0,b)};Sk.exportSymbol("Sk.misceval.callsim",Sk.misceval.callsim);Sk.misceval.callsimArray=function(a,b,c){return Sk.misceval.apply(a,void 0,void 0,c,b?b:[])};Sk.exportSymbol("Sk.misceval.callsimArray",Sk.misceval.callsimArray);Sk.misceval.callsimAsync=function(a,b,c){c=Array.prototype.slice.call(arguments,2);return Sk.misceval.applyAsync(a,b,void 0,void 0,
void 0,c)};Sk.exportSymbol("Sk.misceval.callsimAsync",Sk.misceval.callsimAsync);Sk.misceval.callsimOrSuspend=function(a,b){b=Array.prototype.slice.call(arguments,1);return Sk.misceval.applyOrSuspend(a,void 0,void 0,void 0,b)};Sk.exportSymbol("Sk.misceval.callsimOrSuspend",Sk.misceval.callsimOrSuspend);Sk.misceval.callsimOrSuspendArray=function(a,b,c){b||(b=[]);return a.tp$call?a.tp$call(b,c):Sk.misceval.applyOrSuspend(a,void 0,void 0,c,b)};Sk.exportSymbol("Sk.misceval.callsimOrSuspendArray",Sk.misceval.callsimOrSuspendArray);
Sk.misceval.apply=function(a,b,c,d,e){a=Sk.misceval.applyOrSuspend(a,b,c,d,e);return a instanceof Sk.misceval.Suspension?Sk.misceval.retryOptionalSuspensionOrThrow(a):a};Sk.exportSymbol("Sk.misceval.apply",Sk.misceval.apply);Sk.misceval.asyncToPromise=function(a,b){return new Promise(function(c,d){try{(function g(a){try{for(var f=function(){try{g(a.resume())}catch(u){d(u)}},h=function(c){try{a.data.result=c,f()}catch(z){d(z)}},n=function(c){try{a.data.error=c,f()}catch(z){d(z)}};a instanceof Sk.misceval.Suspension;){var l=
b&&(b[a.data.type]||b["*"]);if(l){var q=l(a);if(q){q.then(g,d);return}}if("Sk.promise"==a.data.type){a.data.promise.then(h,n);return}if("Sk.yield"==a.data.type){Sk.global.setImmediate(f);return}if("Sk.delay"==a.data.type){Sk.global.setImmediate(f);return}if(a.optional)a=a.resume();else throw new Sk.builtin.SuspensionError("Unhandled non-optional suspension of type '"+a.data.type+"'");}c(a)}catch(u){d(u)}})(a())}catch(e){d(e)}})};Sk.exportSymbol("Sk.misceval.asyncToPromise",Sk.misceval.asyncToPromise);
Sk.misceval.applyAsync=function(a,b,c,d,e,h){return Sk.misceval.asyncToPromise(function(){return Sk.misceval.applyOrSuspend(b,c,d,e,h)},a)};Sk.exportSymbol("Sk.misceval.applyAsync",Sk.misceval.applyAsync);Sk.misceval.chain=function(a,b){for(var c=1,d=a,e,h;;){if(c==arguments.length)return d;if(d&&d.$isSuspension)break;d=arguments[c](d);c++}h=Array(arguments.length-c);for(e=0;e<arguments.length-c;e++)h[e]=arguments[c+e];e=0;return function k(a){for(;e<h.length;){if(a instanceof Sk.misceval.Suspension)return new Sk.misceval.Suspension(k,
a);a=h[e](a);e++}return a}(d)};Sk.exportSymbol("Sk.misceval.chain",Sk.misceval.chain);Sk.misceval.tryCatch=function(a,b){try{var c=a()}catch(d){return b(d)}return c instanceof Sk.misceval.Suspension?(a=new Sk.misceval.Suspension(void 0,c),a.resume=function(){return Sk.misceval.tryCatch(c.resume,b)},a):c};Sk.exportSymbol("Sk.misceval.tryCatch",Sk.misceval.tryCatch);Sk.misceval.iterFor=function(a,b,c){var d=c,e=function(c){d=c;return c instanceof Sk.misceval.Break?c:a.tp$iternext(!0)};return function f(a){for(;void 0!==
a;){if(a instanceof Sk.misceval.Suspension)return new Sk.misceval.Suspension(f,a);if(a===Sk.misceval.Break||a instanceof Sk.misceval.Break)return a.brValue;a=Sk.misceval.chain(b(a,d),e)}return d}(a.tp$iternext(!0))};Sk.exportSymbol("Sk.misceval.iterFor",Sk.misceval.iterFor);Sk.misceval.arrayFromIterable=function(a,b){if(void 0===a)return[];if(void 0===(a.hp$type||void 0)&&void 0!==a.sk$asarray)return a.sk$asarray();const c=[];a=Sk.misceval.chain(Sk.misceval.iterFor(Sk.abstr.iter(a),a=>{c.push(a)}),
()=>c);return b?a:Sk.misceval.retryOptionalSuspensionOrThrow(a)};Sk.misceval.Break=function(a){if(!(this instanceof Sk.misceval.Break))return new Sk.misceval.Break(a);this.brValue=a};Sk.exportSymbol("Sk.misceval.Break",Sk.misceval.Break);Sk.misceval.applyOrSuspend=function(a,b,c,d,e){var h;if(null===a||a===Sk.builtin.none.none$)throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(a)+"' object is not callable");"function"===typeof a&&void 0===a.tp$call&&(a=new Sk.builtin.func(a));var g=a.tp$call;if(void 0!==
g){if(c)for(c=c.tp$iter(),h=c.tp$iternext();void 0!==h;h=c.tp$iternext())e.push(h);if(b)for(c=Sk.abstr.iter(b),h=c.tp$iternext();void 0!==h;h=c.tp$iternext()){if(!Sk.builtin.checkString(h))throw new Sk.builtin.TypeError("Function keywords must be strings");d.push(h.v);d.push(Sk.abstr.objectGetItem(b,h,!1))}return g.call(a,e,d,b)}g=a.__call__;if(void 0!==g)return e.unshift(a),Sk.misceval.apply(g,b,c,d,e);throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(a)+"' object is not callable");};Sk.exportSymbol("Sk.misceval.applyOrSuspend",
Sk.misceval.applyOrSuspend);Sk.misceval.promiseToSuspension=function(a){var b=new Sk.misceval.Suspension;b.resume=function(){if(b.data.error)throw b.data.error;return b.data.result};b.data={type:"Sk.promise",promise:a};return b};Sk.exportSymbol("Sk.misceval.promiseToSuspension",Sk.misceval.promiseToSuspension);Sk.misceval.buildClass=function(a,b,c,d,e){var h=Sk.builtin.type,g={};b(a,g,void 0===e?{}:e);a.__name__&&(g.__module__=a.__name__);a=new Sk.builtin.str(c);d=new Sk.builtin.tuple(d);b=[];for(var f in g)g.hasOwnProperty(f)&&
(b.push(new Sk.builtin.str(f)),b.push(g[f]));b=new Sk.builtin.dict(b);return Sk.misceval.callsimArray(h,[a,d,b])};Sk.exportSymbol("Sk.misceval.buildClass",Sk.misceval.buildClass)},function(m,p){Sk.builtin.seqtype=function(){throw new Sk.builtin.ExternalError("Cannot instantiate abstract Sk.builtin.seqtype class");};Sk.abstr.setUpInheritance("SequenceType",Sk.builtin.seqtype,Sk.builtin.object);Sk.builtin.seqtype.sk$abstract=!0;Sk.builtin.seqtype.prototype.__len__=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("__len__",
arguments.length,0,0,!1,!0);return new Sk.builtin.int_(a.sq$length())});Sk.builtin.seqtype.prototype.__iter__=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("__iter__",arguments.length,0,0,!1,!0);return a.tp$iter()});Sk.builtin.seqtype.prototype.__contains__=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("__contains__",arguments.length,1,1,!1,!0);return a.sq$contains(b)?Sk.builtin.bool.true$:Sk.builtin.bool.false$});Sk.builtin.seqtype.prototype.__getitem__=new Sk.builtin.func(function(a,
b){Sk.builtin.pyCheckArgsLen("__getitem__",arguments.length,1,1,!1,!0);return a.mp$subscript(b)});Sk.builtin.seqtype.prototype.__add__=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("__add__",arguments.length,1,1,!1,!0);return a.sq$concat(b)});Sk.builtin.seqtype.prototype.__mul__=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("__mul__",arguments.length,1,1,!1,!0);if(!Sk.misceval.isIndex(b))throw new Sk.builtin.TypeError("can't multiply sequence by non-int of type '"+Sk.abstr.typeName(b)+
"'");return a.sq$repeat(b)});Sk.builtin.seqtype.prototype.__rmul__=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("__rmul__",arguments.length,1,1,!1,!0);return a.sq$repeat(b)})},function(m,p){Sk.builtin.list=function(a,b){if(!(this instanceof Sk.builtin.list))return Sk.builtin.pyCheckArgsLen("list",arguments.length,0,1),new Sk.builtin.list(a,!0);if(void 0===a)this.v=[];else if(Array.isArray(a))this.v=a;else return Sk.misceval.chain(Sk.misceval.arrayFromIterable(a,b),a=>{this.v=a;return this})};
Sk.abstr.setUpInheritance("list",Sk.builtin.list,Sk.builtin.seqtype);Sk.abstr.markUnhashable(Sk.builtin.list);Sk.builtin.list.prototype.__class__=Sk.builtin.list;Sk.builtin.list.prototype.sk$asarray=function(){return this.v.slice(0)};Sk.builtin.list.prototype.list_concat_=function(a){if(!a.__class__||a.__class__!=Sk.builtin.list)throw new Sk.builtin.TypeError("can only concatenate list to list");return new Sk.builtin.list(this.v.concat(a.v),!1)};Sk.builtin.list.prototype.list_extend_=function(a){var b;
if(a.sk$asarray)return this.v.push.apply(this.v,a.sk$asarray()),this;if(!Sk.builtin.checkIterable(a))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(a)+"' object is not iterable");a=Sk.abstr.iter(a);for(b=a.tp$iternext();void 0!==b;b=a.tp$iternext())this.v.push(b);return this};Sk.builtin.list.prototype.list_del_item_=function(a){a=Sk.builtin.asnum$(a);if(0>a||a>=this.v.length)throw new Sk.builtin.IndexError("list assignment index out of range");this.list_del_slice_(a,a+1)};Sk.builtin.list.prototype.list_del_slice_=
function(a,b){a=Sk.builtin.asnum$(a);b=Sk.builtin.asnum$(b);var c=[];c.unshift(b-a);c.unshift(a);this.v.splice.apply(this.v,c)};Sk.builtin.list.prototype.list_ass_item_=function(a,b){a=Sk.builtin.asnum$(a);if(0>a||a>=this.v.length)throw new Sk.builtin.IndexError("list assignment index out of range");this.v[a]=b};Sk.builtin.list.prototype.list_ass_slice_=function(a,b,c){a=Sk.builtin.asnum$(a);b=Sk.builtin.asnum$(b);if(Sk.builtin.checkIterable(c))c=(new Sk.builtin.list(c,!1)).v.slice(0);else throw new Sk.builtin.TypeError("can only assign an iterable");
c.unshift(b-a);c.unshift(a);this.v.splice.apply(this.v,c)};Sk.builtin.list.prototype.$r=function(){var a,b=[];var c=Sk.abstr.iter(this);for(a=c.tp$iternext();void 0!==a;a=c.tp$iternext())a===this?b.push("[...]"):b.push(Sk.misceval.objectRepr(a).v);return new Sk.builtin.str("["+b.join(", ")+"]")};Sk.builtin.list.prototype.tp$richcompare=function(a,b){var c;if(this===a&&Sk.misceval.opAllowsEquality(b))return!0;if(!a.__class__||a.__class__!=Sk.builtin.list)return"Eq"===b?!1:"NotEq"===b?!0:Sk.__future__.python3?
Sk.builtin.NotImplemented.NotImplemented$:!1;var d=this.v;a=a.v;var e=d.length;var h=a.length;for(c=0;c<e&&c<h;++c){var g=Sk.misceval.richCompareBool(d[c],a[c],"Eq");if(!g)break}if(c>=e||c>=h)switch(b){case "Lt":return e<h;case "LtE":return e<=h;case "Eq":return e===h;case "NotEq":return e!==h;case "Gt":return e>h;case "GtE":return e>=h;default:Sk.asserts.fail()}return"Eq"===b?!1:"NotEq"===b?!0:Sk.misceval.richCompareBool(d[c],a[c],b)};Sk.builtin.list.prototype.__iter__=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("__iter__",
arguments.length,0,0,!0,!1);return new Sk.builtin.list_iter_(a)});Sk.builtin.list.prototype.tp$iter=function(){return new Sk.builtin.list_iter_(this)};Sk.builtin.list.prototype.sq$length=function(){return this.v.length};Sk.builtin.list.prototype.sq$concat=Sk.builtin.list.prototype.list_concat_;Sk.builtin.list.prototype.nb$add=Sk.builtin.list.prototype.list_concat_;Sk.builtin.list.prototype.nb$inplace_add=Sk.builtin.list.prototype.list_extend_;Sk.builtin.list.prototype.sq$repeat=function(a){if(!Sk.misceval.isIndex(a))throw new Sk.builtin.TypeError("can't multiply sequence by non-int of type '"+
Sk.abstr.typeName(a)+"'");var b=Sk.misceval.asIndex(a);if("number"!==typeof b)throw new Sk.builtin.OverflowError("cannot fit '"+Sk.abstr.typeName(a)+"' into an index-sized integer");var c=[];for(a=0;a<b;++a)c.push.apply(c,this.v);return new Sk.builtin.list(c,!1)};Sk.builtin.list.prototype.nb$multiply=Sk.builtin.list.prototype.sq$repeat;Sk.builtin.list.prototype.nb$inplace_multiply=function(a){if(!Sk.misceval.isIndex(a))throw new Sk.builtin.TypeError("can't multiply sequence by non-int of type '"+
Sk.abstr.typeName(a)+"'");var b=Sk.misceval.asIndex(a);if("number"!==typeof b)throw new Sk.builtin.OverflowError("cannot fit '"+Sk.abstr.typeName(a)+"' into an index-sized integer");for(a=1;a<b;++a)this.v.push.apply(this.v,this.v);return this};Sk.builtin.list.prototype.sq$ass_item=Sk.builtin.list.prototype.list_ass_item_;Sk.builtin.list.prototype.sq$del_item=Sk.builtin.list.prototype.list_del_item_;Sk.builtin.list.prototype.sq$ass_slice=Sk.builtin.list.prototype.list_ass_slice_;Sk.builtin.list.prototype.sq$del_slice=
Sk.builtin.list.prototype.list_del_slice_;Sk.builtin.list.prototype.sq$contains=function(a){var b,c=this.v;for(b=0;b<c.length;b++)if(Sk.misceval.richCompareBool(c[b],a,"Eq"))return!0;return!1};Sk.builtin.list.prototype.__contains__=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("__contains__",arguments.length,2,2);return new Sk.builtin.bool(a.sq$contains(b))});Sk.builtin.list.prototype.list_subscript_=function(a){if(Sk.misceval.isIndex(a)){let b=Sk.misceval.asIndex(a);if("number"!==typeof b)throw new Sk.builtin.IndexError("cannot fit '"+
Sk.abstr.typeName(a)+"' into an index-sized integer");if(void 0!==b){0>b&&(b=this.v.length+b);if(0>b||b>=this.v.length)throw new Sk.builtin.IndexError("list index out of range");return this.v[b]}}else if(a instanceof Sk.builtin.slice){const b=[];a.sssiter$(this.v.length,a=>{b.push(this.v[a])});return new Sk.builtin.list(b,!1)}throw new Sk.builtin.TypeError("list indices must be integers, not "+Sk.abstr.typeName(a));};Sk.builtin.list.prototype.list_ass_subscript_=function(a,b){if(Sk.misceval.isIndex(a)){var c=
Sk.misceval.asIndex(a);if("number"!==typeof c)throw new Sk.builtin.IndexError("cannot fit '"+Sk.abstr.typeName(a)+"' into an index-sized integer");if(void 0!==c){0>c&&(c=this.v.length+c);this.list_ass_item_(c,b);return}}else if(a instanceof Sk.builtin.slice){c=a.slice_indices_(this.v.length);if(1===c[2])this.list_ass_slice_(c[0],c[1],b);else{const d=[];a.sssiter$(this.v.length,a=>{d.push(a)});a=0;if(d.length!==b.v.length)throw new Sk.builtin.ValueError("attempt to assign sequence of size "+b.v.length+
" to extended slice of size "+d.length);for(c=0;c<d.length;++c)this.v.splice(d[c],1,b.v[a]),a+=1}return}throw new Sk.builtin.TypeError("list indices must be integers, not "+Sk.abstr.typeName(a));};Sk.builtin.list.prototype.list_del_subscript_=function(a){if(Sk.misceval.isIndex(a)){var b=Sk.misceval.asIndex(a);if(void 0!==b){0>b&&(b=this.v.length+b);this.list_del_item_(b);return}}else if(a instanceof Sk.builtin.slice){b=a.slice_indices_(this.v.length);if(1===b[2])this.list_del_slice_(b[0],b[1]);else{const c=
this.v;let d=0;const e=0<b[2]?1:0;a.sssiter$(c.length,a=>{c.splice(a-d,1);d+=e})}return}throw new Sk.builtin.TypeError("list indices must be integers, not "+typeof a);};Sk.builtin.list.prototype.mp$subscript=Sk.builtin.list.prototype.list_subscript_;Sk.builtin.list.prototype.mp$ass_subscript=Sk.builtin.list.prototype.list_ass_subscript_;Sk.builtin.list.prototype.mp$del_subscript=Sk.builtin.list.prototype.list_del_subscript_;Sk.builtin.list.prototype.__getitem__=new Sk.builtin.func(function(a,b){return Sk.builtin.list.prototype.list_subscript_.call(a,
b)});Sk.builtin.list.prototype.__setitem__=new Sk.builtin.func(function(a,b,c){return Sk.builtin.list.prototype.list_ass_subscript_.call(a,b,c)});Sk.builtin.list.prototype.__delitem__=new Sk.builtin.func(function(a,b){return Sk.builtin.list.prototype.list_del_subscript_.call(a,b)});Sk.builtin.list.prototype.list_sort_=function(a,b,c,d){var e,h=void 0!==c&&null!==c&&c!==Sk.builtin.none.none$;var g=void 0!==b&&null!==b&&b!==Sk.builtin.none.none$;if(void 0===d)var f=!1;else{if(d===Sk.builtin.none.none$)throw new Sk.builtin.TypeError("an integer is required");
f=Sk.misceval.isTrue(d)}d=new Sk.builtin.timSort(a);a.v=[];var k=new Sk.builtin.int_(0);if(h)for(d.lt=g?function(a,c){a=Sk.misceval.callsimArray(b,[a[0],c[0]]);return Sk.misceval.richCompareBool(a,k,"Lt")}:function(a,c){return Sk.misceval.richCompareBool(a[0],c[0],"Lt")},e=0;e<d.listlength;e++){g=d.list.v[e];var n=Sk.misceval.callsimArray(c,[g]);d.list.v[e]=[n,g]}else g&&(d.lt=function(a,c){a=Sk.misceval.callsimArray(b,[a,c]);return Sk.misceval.richCompareBool(a,k,"Lt")});f&&d.list.list_reverse_(d.list);
d.sort();f&&d.list.list_reverse_(d.list);if(h)for(c=0;c<d.listlength;c++)g=d.list.v[c][1],d.list.v[c]=g;c=0<a.sq$length();a.v=d.list.v;if(c)throw new Sk.builtin.OperationError("list modified during sort");return Sk.builtin.none.none$};Sk.builtin.list.prototype.list_sort_.co_varnames=["__self__","cmp","key","reverse"];Sk.builtin.list.prototype.list_sort_.$defaults=[Sk.builtin.none.none$,Sk.builtin.none.none$,!1];Sk.builtin.list.prototype.list_reverse_=function(a){Sk.builtin.pyCheckArgsLen("reverse",
arguments.length,1,1);var b=a.v.length;var c=a.v;var d=[];for(--b;-1<b;--b)d.push(c[b]);a.v=d;return Sk.builtin.none.none$};Sk.builtin.list.prototype.append=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("append",arguments.length,2,2);a.v.push(b);return Sk.builtin.none.none$});Sk.builtin.list.prototype.insert=new Sk.builtin.func(function(a,b,c){Sk.builtin.pyCheckArgsLen("insert",arguments.length,3,3);if(!Sk.builtin.checkNumber(b))throw new Sk.builtin.TypeError("an integer is required");
b=Sk.builtin.asnum$(b);0>b&&(b+=a.v.length);0>b?b=0:b>a.v.length&&(b=a.v.length);a.v.splice(b,0,c);return Sk.builtin.none.none$});Sk.builtin.list.prototype.extend=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("extend",arguments.length,2,2);a.list_extend_(b);return Sk.builtin.none.none$});Sk.builtin.list.prototype.pop=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("pop",arguments.length,1,2);void 0===b&&(b=a.v.length-1);if(!Sk.builtin.checkNumber(b))throw new Sk.builtin.TypeError("an integer is required");
b=Sk.builtin.asnum$(b);0>b&&(b+=a.v.length);if(0>b||b>=a.v.length)throw new Sk.builtin.IndexError("pop index out of range");var c=a.v[b];a.v.splice(b,1);return c});Sk.builtin.list.prototype.remove=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("remove",arguments.length,2,2);var c=Sk.builtin.list.prototype.index.func_code(a,b);a.v.splice(Sk.builtin.asnum$(c),1);return Sk.builtin.none.none$});Sk.builtin.list.prototype.clear$=function(a){Sk.builtin.pyCheckArgsLen("clear",arguments.length,
1,1);a.v=[];return Sk.builtin.none.none$};Sk.builtin.list.prototype.copy$=function(a){Sk.builtin.pyCheckArgsLen("copy",arguments.length,1,1);return new Sk.builtin.list(a)};Sk.builtin.list.prototype.index=new Sk.builtin.func(function(a,b,c,d){Sk.builtin.pyCheckArgsLen("index",arguments.length,2,4);if(void 0!==c&&!Sk.builtin.checkInt(c))throw new Sk.builtin.TypeError("slice indices must be integers");if(void 0!==d&&!Sk.builtin.checkInt(d))throw new Sk.builtin.TypeError("slice indices must be integers");
var e=a.v.length;var h=a.v;c=void 0===c?0:c.v;0>c&&(c=0<=c+e?c+e:0);d=void 0===d?e:d.v;0>d&&(d=0<=d+e?d+e:0);for(e=c;e<d;++e)if(Sk.misceval.richCompareBool(h[e],b,"Eq"))return new Sk.builtin.int_(e);throw new Sk.builtin.ValueError("list.index(x): x not in list");});Sk.builtin.list.prototype.count=new Sk.builtin.func(function(a,b){var c,d;Sk.builtin.pyCheckArgsLen("count",arguments.length,2,2);var e=a.v.length;var h=a.v;for(c=d=0;c<e;++c)Sk.misceval.richCompareBool(h[c],b,"Eq")&&(d+=1);return new Sk.builtin.int_(d)});
Sk.builtin.list.prototype.reverse=new Sk.builtin.func(Sk.builtin.list.prototype.list_reverse_);Sk.builtin.list.prototype.sort=new Sk.builtin.func(Sk.builtin.list.prototype.list_sort_);Sk.exportSymbol("Sk.builtin.list",Sk.builtin.list);Sk.builtin.list_iter_=function(a){if(!(this instanceof Sk.builtin.list_iter_))return new Sk.builtin.list_iter_(a);this.$index=0;this.lst=a.v;this.$done=!1;this.tp$iter=()=>this;this.tp$iternext=function(){if(this.$done||this.$index>=this.lst.length)this.$done=!0;else return this.lst[this.$index++]};
this.$r=function(){return new Sk.builtin.str("<listiterator>")};return this};Sk.abstr.setUpInheritance("listiterator",Sk.builtin.list_iter_,Sk.builtin.object);Sk.builtin.list_iter_.prototype.__class__=Sk.builtin.list_iter_;Sk.builtin.list_iter_.prototype.__iter__=new Sk.builtin.func(function(a){return a});Sk.builtin.list_iter_.prototype.next$=function(a){a=a.tp$iternext();if(void 0===a)throw new Sk.builtin.StopIteration;return a}},function(m,p){function a(a,c){return new Sk.builtin.func(function(b,
d,e){Sk.builtin.pyCheckArgsLen(c?"center":a?"rjust":"ljust",arguments.length,2,3);if(!Sk.builtin.checkInt(d))throw new Sk.builtin.TypeError("integer argument expected, got "+Sk.abstr.typeName(d));if(void 0!==e&&(!Sk.builtin.checkString(e)||1!==e.v.length&&1!==e.sq$length()))throw new Sk.builtin.TypeError("must be char, not "+Sk.abstr.typeName(e));e=void 0===e?" ":e.v;d=Sk.builtin.asnum$(d);let f=b.sq$length();if(f>=d)return b;if(c){var g=e.repeat(Math.floor((d-f)/2));g=g+b.v+g;(d-f)%2&&(g+=e);return new Sk.builtin.str(g)}g=
e.repeat(d-f);return new Sk.builtin.str(a?g+b.v:b.v+g)})}function b(a,c,b){const d=a.sq$length();if(void 0===c||Sk.builtin.checkNone(c))c=0;else if(Sk.misceval.isIndex(c))c=Sk.misceval.asIndex(c),c=0<=c?c:d+c,0>c&&(c=0);else throw new Sk.builtin.TypeError("slice indices must be integers or None or have an __index__ method");if(void 0===b||Sk.builtin.checkNone(b))b=d;else if(Sk.misceval.isIndex(b))b=Sk.misceval.asIndex(b),b=0<=b?b:d+b,0>b?b=0:b>d&&(b=d);else throw new Sk.builtin.TypeError("slice indices must be integers or None or have an __index__ method");
a.$hasAstralCodePoints()&&(c=a.codepoints[c],b=a.codepoints[b],c=void 0===c?a.v.length:c,b=void 0===b?a.v.length:b);return{start:c,end:b}}function c(a){return new Sk.builtin.func(function(c,d,f,e){Sk.builtin.pyCheckArgsLen("find",arguments.length,2,4);if(!Sk.builtin.checkString(d))throw new Sk.builtin.TypeError("expected a character buffer object");({start:f,end:e}=b(c,f,e));const g=c.sq$length();if(e<f)return new Sk.builtin.int_(-1);e-=d.v.length;let h=a?c.v.lastIndexOf(d.v,e):c.v.indexOf(d.v,f);
h=h>=f&&h<=e?h:-1;if(c.$hasAstralCodePoints()){var k=-1;for(let a=0;a<g;a++)h==c.codepoints[a]&&(k=a)}else k=h;return new Sk.builtin.int_(k)})}Sk.builtin.interned=Object.create(null);Sk.builtin.str=function(a,c,b){void 0===a&&(a="");if(c){Sk.builtin.pyCheckArgsLen("str",arguments.length,0,Sk.__future__.python3?3:1);if(!Sk.builtin.checkBytes(a))throw new TypeError("decoding "+Sk.abstr.typeName(a)+" is not supported");return Sk.builtin.bytes.$decode(a,c,b)}if(a instanceof Sk.builtin.str)return a;if(!(this instanceof
Sk.builtin.str))return new Sk.builtin.str(a);if(!0===a)var f="True";else if(!1===a)f="False";else if(null===a||a===Sk.builtin.none.none$)f="None";else if(a instanceof Sk.builtin.bool)f=a.v?"True":"False";else if("number"===typeof a)f=a.toString(),"Infinity"===f?f="inf":"-Infinity"===f&&(f="-inf");else if("string"===typeof a)f=a;else{if(void 0!==a.tp$str){f=a.tp$str();if(!(f instanceof Sk.builtin.str))throw new Sk.builtin.ValueError("__str__ didn't return a str");return f}return Sk.misceval.objectRepr(a)}const e=
Sk.builtin.interned[f];if(void 0!==e)return e;this.__class__=Sk.builtin.str;this.v=f;Sk.builtin.interned[f]=this;f=void 0===d[f]?f:f+"_$rw$";this.$mangled=f;this.$savedKeyHash_=void 0;return this};Sk.exportSymbol("Sk.builtin.str",Sk.builtin.str);Sk.abstr.setUpInheritance("str",Sk.builtin.str,Sk.builtin.seqtype);Sk.builtin.str.prototype.sk$builtinBase=Sk.builtin.str;Sk.builtin.str.prototype.$hasAstralCodePoints=function(){if(null===this.codepoints)return!1;if(void 0!==this.codepoints)return!0;for(var a=
0;a<this.v.length;a++){let c=this.v.charCodeAt(a);if(55296<=c&&57344>c){this.codepoints=[];for(a=0;a<this.v.length;a++)this.codepoints.push(a),c=this.v.charCodeAt(a),55296<=c&&56320>c&&a++;return!0}}this.codepoints=null;return!1};Sk.builtin.str.prototype.$jsstr=function(){return this.v};Sk.builtin.str.prototype.mp$subscript=function(a){let c;if(Sk.misceval.isIndex(a)){a=Sk.misceval.asIndex(a);c=this.sq$length();0>a&&(a=c+a);if(0>a||a>=c)throw new Sk.builtin.IndexError("string index out of range");
return this.codepoints?new Sk.builtin.str(this.v.substring(this.codepoints[a],this.codepoints[a+1])):new Sk.builtin.str(this.v.charAt(a))}if(a instanceof Sk.builtin.slice){let b="";c=this.sq$length();this.codepoints?a.sssiter$(c,a=>{b+=this.v.substring(this.codepoints[a],this.codepoints[a+1])}):a.sssiter$(c,a=>{b+=this.v.charAt(a)});return new Sk.builtin.str(b)}throw new Sk.builtin.TypeError("string indices must be integers, not "+Sk.abstr.typeName(a));};Sk.builtin.str.prototype.sq$length=function(){return this.$hasAstralCodePoints()?
this.codepoints.length:this.v.length};Sk.builtin.str.prototype.sq$concat=function(a){if(!a||!Sk.builtin.checkString(a))throw a=Sk.abstr.typeName(a),new Sk.builtin.TypeError("cannot concatenate 'str' and '"+a+"' objects");return new Sk.builtin.str(this.v+a.v)};Sk.builtin.str.prototype.nb$add=Sk.builtin.str.prototype.sq$concat;Sk.builtin.str.prototype.nb$inplace_add=Sk.builtin.str.prototype.sq$concat;Sk.builtin.str.prototype.sq$repeat=function(a){var c;if(!Sk.misceval.isIndex(a))throw new Sk.builtin.TypeError("can't multiply sequence by non-int of type '"+
Sk.abstr.typeName(a)+"'");a=Sk.misceval.asIndex(a);var b="";for(c=0;c<a;++c)b+=this.v;return new Sk.builtin.str(b)};Sk.builtin.str.prototype.nb$multiply=Sk.builtin.str.prototype.sq$repeat;Sk.builtin.str.prototype.nb$inplace_multiply=Sk.builtin.str.prototype.sq$repeat;Sk.builtin.str.prototype.sq$item=function(){Sk.asserts.fail()};Sk.builtin.str.prototype.sq$slice=function(a,c){a=Sk.builtin.asnum$(a);c=Sk.builtin.asnum$(c);0>a&&(a=0);return this.$hasAstralCodePoints()?a>=this.codepoints.length?Sk.builtin.str.$emptystr:
new Sk.builtin.str(this.v.substring(this.codepoints[a],this.codepoints[c])):new Sk.builtin.str(this.v.substring(a,c))};Sk.builtin.str.prototype.sq$contains=function(a){if(!(a instanceof Sk.builtin.str))throw new Sk.builtin.TypeError("TypeError: 'In <string> requires string as left operand");return-1!=this.v.indexOf(a.v)};Sk.builtin.str.prototype.__contains__=new Sk.builtin.func(function(a,c){Sk.builtin.pyCheckArgsLen("__contains__",arguments.length-1,1,1);return new Sk.builtin.bool(-1!=a.v.indexOf(c.v))});
Sk.builtin.str.prototype.__iter__=new Sk.builtin.func(function(a){return new Sk.builtin.str_iter_(a)});Sk.builtin.str.prototype.tp$iter=function(){return new Sk.builtin.str_iter_(this)};Sk.builtin.str.prototype.tp$richcompare=function(a,c){if(!(a instanceof Sk.builtin.str))return Sk.builtin.NotImplemented.NotImplemented$;switch(c){case "Lt":return this.v<a.v;case "LtE":return this.v<=a.v;case "Eq":return this.v===a.v;case "NotEq":return this.v!==a.v;case "Gt":return this.v>a.v;case "GtE":return this.v>=
a.v;default:Sk.asserts.fail()}};Sk.builtin.str.prototype.$r=function(){var a,c="'";-1!==this.v.indexOf("'")&&-1===this.v.indexOf('"')&&(c='"');var b=this.v.length;var d=c;for(a=0;a<b;++a){var k=this.v.charAt(a);var n=this.v.charCodeAt(a);k===c||"\\"===k?d+="\\"+k:"\t"===k?d+="\\t":"\n"===k?d+="\\n":"\r"===k?d+="\\r":(255<n&&55296>n||57344<=n)&&!Sk.__future__.python3?d+="\\u"+("000"+n.toString(16)).slice(-4):55296<=n&&!Sk.__future__.python3?(k=this.v.codePointAt(a),a++,k=k.toString(16),n="0000000"+
k.toString(16),d=4<k.length?d+("\\U"+n.slice(-8)):d+("\\u"+n.slice(-4))):255<n&&!Sk.__future__.python3?d+="\\ufffd":" ">k||127<=n&&!Sk.__future__.python3?(k=k.charCodeAt(0).toString(16),2>k.length&&(k="0"+k),d+="\\x"+k):d+=k}return new Sk.builtin.str(d+c)};Sk.builtin.str.re_escape_=function(a){var c,b=[],d=/^[A-Za-z0-9]+$/;for(c=0;c<a.length;++c){var e=a.charAt(c);d.test(e)?b.push(e):"\\000"===e?b.push("\\000"):b.push("\\"+e)}return b.join("")};Sk.builtin.str.prototype.lower=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("lower",
arguments.length,1,1);return new Sk.builtin.str(a.v.toLowerCase())});Sk.builtin.str.prototype.upper=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("upper",arguments.length,1,1);return new Sk.builtin.str(a.v.toUpperCase())});Sk.builtin.str.prototype.capitalize=new Sk.builtin.func(function(a){var c;Sk.builtin.pyCheckArgsLen("capitalize",arguments.length,1,1);var b=a.v;if(0===b.length)return new Sk.builtin.str("");var d=b.charAt(0).toUpperCase();for(c=1;c<b.length;c++)d+=b.charAt(c).toLowerCase();
return new Sk.builtin.str(d)});Sk.builtin.str.prototype.join=new Sk.builtin.func(function(a,c){var b;Sk.builtin.pyCheckArgsLen("join",arguments.length,2,2);Sk.builtin.pyCheckType("seq","iterable",Sk.builtin.checkIterable(c));var d=[];var e=c.tp$iter();for(b=e.tp$iternext();void 0!==b;b=e.tp$iternext()){if(b.constructor!==Sk.builtin.str)throw new Sk.builtin.TypeError("TypeError: sequence item "+d.length+": expected string, "+Sk.abstr.typeName(b)+" found");d.push(b.v)}return new Sk.builtin.str(d.join(a.v))});
Sk.builtin.str.prototype.split=new Sk.builtin.func(function(a,c,b){var d,e;Sk.builtin.pyCheckArgsLen("split",arguments.length,1,3);if(void 0===c||c===Sk.builtin.none.none$)c=null;if(null!==c&&!Sk.builtin.checkString(c))throw new Sk.builtin.TypeError("expected a string");if(null!==c&&""===c.v)throw new Sk.builtin.ValueError("empty separator");if(void 0!==b&&!Sk.builtin.checkInt(b))throw new Sk.builtin.TypeError("an integer is required");b=Sk.builtin.asnum$(b);var g=/[\s\xa0]+/g;var h=a.v;if(null===
c)h=h.replace(/^[\s\xa0]+/,"");else{var q=c.v.replace(/([.*+?=|\\\/()\[\]\{\}^$])/g,"\\$1");g=new RegExp(q,"g")}var u=[];for(q=d=0;null!=(e=g.exec(h))&&e.index!==g.lastIndex&&!(u.push(new Sk.builtin.str(h.substring(d,e.index))),d=g.lastIndex,q+=1,b&&q>=b););h=h.substring(d);(null!==c||0<h.length)&&u.push(new Sk.builtin.str(h));return new Sk.builtin.list(u)});Sk.builtin.str.prototype.strip=new Sk.builtin.func(function(a,c){Sk.builtin.pyCheckArgsLen("strip",arguments.length,1,2);if(void 0!==c&&!Sk.builtin.checkString(c))throw new Sk.builtin.TypeError("strip arg must be None or str");
if(void 0===c)var b=/^\s+|\s+$/g;else b=Sk.builtin.str.re_escape_(c.v),b=new RegExp("^["+b+"]+|["+b+"]+$","g");return new Sk.builtin.str(a.v.replace(b,""))});Sk.builtin.str.prototype.lstrip=new Sk.builtin.func(function(a,c){Sk.builtin.pyCheckArgsLen("lstrip",arguments.length,1,2);if(void 0!==c&&!Sk.builtin.checkString(c))throw new Sk.builtin.TypeError("lstrip arg must be None or str");if(void 0===c)var b=/^\s+/g;else b=Sk.builtin.str.re_escape_(c.v),b=new RegExp("^["+b+"]+","g");return new Sk.builtin.str(a.v.replace(b,
""))});Sk.builtin.str.prototype.rstrip=new Sk.builtin.func(function(a,c){Sk.builtin.pyCheckArgsLen("rstrip",arguments.length,1,2);if(void 0!==c&&!Sk.builtin.checkString(c))throw new Sk.builtin.TypeError("rstrip arg must be None or str");if(void 0===c)var b=/\s+$/g;else b=Sk.builtin.str.re_escape_(c.v),b=new RegExp("["+b+"]+$","g");return new Sk.builtin.str(a.v.replace(b,""))});Sk.builtin.str.prototype.__format__=new Sk.builtin.func(function(a,c){Sk.builtin.pyCheckArgsLen("__format__",arguments.length,
2,2);if(Sk.builtin.checkString(c)){var b=Sk.ffi.remapToJs(c);if(""!==b&&"s"!==b)throw new Sk.builtin.NotImplementedError("format spec is not yet implemented");}else{if(Sk.__future__.exceptions)throw new Sk.builtin.TypeError("format() argument 2 must be str, not "+Sk.abstr.typeName(c));throw new Sk.builtin.TypeError("format expects arg 2 to be string or unicode, not "+Sk.abstr.typeName(c));}return new Sk.builtin.str(a)});Sk.builtin.str.prototype.partition=new Sk.builtin.func(function(a,c){Sk.builtin.pyCheckArgsLen("partition",
arguments.length,2,2);Sk.builtin.pyCheckType("sep","string",Sk.builtin.checkString(c));var b=new Sk.builtin.str(c);var d=a.v.indexOf(b.v);return 0>d?new Sk.builtin.tuple([a,Sk.builtin.str.$emptystr,Sk.builtin.str.$emptystr]):new Sk.builtin.tuple([new Sk.builtin.str(a.v.substring(0,d)),b,new Sk.builtin.str(a.v.substring(d+b.v.length))])});Sk.builtin.str.prototype.rpartition=new Sk.builtin.func(function(a,c){Sk.builtin.pyCheckArgsLen("rpartition",arguments.length,2,2);Sk.builtin.pyCheckType("sep","string",
Sk.builtin.checkString(c));var b=new Sk.builtin.str(c);var d=a.v.lastIndexOf(b.v);return 0>d?new Sk.builtin.tuple([Sk.builtin.str.$emptystr,Sk.builtin.str.$emptystr,a]):new Sk.builtin.tuple([new Sk.builtin.str(a.v.substring(0,d)),b,new Sk.builtin.str(a.v.substring(d+b.v.length))])});Sk.builtin.str.prototype.count=new Sk.builtin.func(function(a,c,b,d){Sk.builtin.pyCheckArgsLen("count",arguments.length,2,4);if(!Sk.builtin.checkString(c))throw new Sk.builtin.TypeError("expected a character buffer object");
if(void 0!==b&&!Sk.builtin.checkInt(b)&&!Sk.builtin.checkNone(b))throw new Sk.builtin.TypeError("slice indices must be integers or None or have an __index__ method");if(void 0!==d&&!Sk.builtin.checkInt(d)&&!Sk.builtin.checkNone(d))throw new Sk.builtin.TypeError("slice indices must be integers or None or have an __index__ method");var f=a.sq$length();if(void 0===b||b===Sk.builtin.none.none$)b=0;else if(b=Sk.builtin.asnum$(b),b=0<=b?b:f+b,b>f)return new Sk.builtin.int_(0);void 0===d||d===Sk.builtin.none.none$?
d=f:(d=Sk.builtin.asnum$(d),d=0<=d?d:f+d);f=c.v.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&");f=new RegExp(f,"g");return(f=a.v.slice(a.codepoints?a.codepoints[b]:b,a.codepoints?a.codepoints[d]:d).match(f))?new Sk.builtin.int_(f.length):new Sk.builtin.int_(0)});Sk.builtin.str.prototype.ljust=a(!1);Sk.builtin.str.prototype.rjust=a(!0);Sk.builtin.str.prototype.center=a(!1,!0);Sk.builtin.str.prototype.find=c(!1);Sk.builtin.str.prototype.index=new Sk.builtin.func(function(a,c,b,d){Sk.builtin.pyCheckArgsLen("index",
arguments.length,2,4);var f=Sk.misceval.callsimArray(a.find,[a,c,b,d]);if(-1===Sk.builtin.asnum$(f))throw new Sk.builtin.ValueError("substring not found");return f});Sk.builtin.str.prototype.rfind=c(!0);Sk.builtin.str.prototype.rindex=new Sk.builtin.func(function(a,c,b,d){Sk.builtin.pyCheckArgsLen("rindex",arguments.length,2,4);var f=Sk.misceval.callsimArray(a.rfind,[a,c,b,d]);if(-1===Sk.builtin.asnum$(f))throw new Sk.builtin.ValueError("substring not found");return f});Sk.builtin.str.prototype.startswith=
new Sk.builtin.func(function(a,c,d,f){Sk.builtin.pyCheckArgsLen("startswith",arguments.length-1,1,3);if(!(c instanceof Sk.builtin.str||c instanceof Sk.builtin.tuple))throw new Sk.builtin.TypeError("startswith first arg must be str or a tuple of str, not "+Sk.abstr.typeName(c));({start:d,end:f}=b(a,d,f));if(d>f)return Sk.builtin.bool.false$;let e=a.v.slice(d,f);if(c instanceof Sk.builtin.tuple){for(let a=Sk.abstr.iter(c),b=a.tp$iternext();void 0!==b;b=a.tp$iternext()){if(!(b instanceof Sk.builtin.str))throw new Sk.builtin.TypeError("tuple for startswith must only contain str, not "+
Sk.abstr.typeName(b));if(0===e.indexOf(b.v))return Sk.builtin.bool.true$}return Sk.builtin.bool.false$}return new Sk.builtin.bool(0===e.indexOf(c.v))});Sk.builtin.str.prototype.endswith=new Sk.builtin.func(function(a,c,d,f){Sk.builtin.pyCheckArgsLen("endswith",arguments.length-1,1,3);if(!(c instanceof Sk.builtin.str||c instanceof Sk.builtin.tuple))throw new Sk.builtin.TypeError("endswith first arg must be str or a tuple of str, not "+Sk.abstr.typeName(c));({start:d,end:f}=b(a,d,f));if(d>f)return Sk.builtin.bool.false$;
let e=a.v.slice(d,f);if(c instanceof Sk.builtin.tuple){for(let a=Sk.abstr.iter(c),b=a.tp$iternext();void 0!==b;b=a.tp$iternext()){if(!(b instanceof Sk.builtin.str))throw new Sk.builtin.TypeError("tuple for endswith must only contain str, not "+Sk.abstr.typeName(b));if(-1!==e.indexOf(b.v,e.length-b.v.length))return Sk.builtin.bool.true$}return Sk.builtin.bool.false$}return new Sk.builtin.bool(-1!==e.indexOf(c.v,e.length-c.v.length))});Sk.builtin.str.prototype.replace=new Sk.builtin.func(function(a,
c,b,d){Sk.builtin.pyCheckArgsLen("replace",arguments.length,3,4);Sk.builtin.pyCheckType("oldS","string",Sk.builtin.checkString(c));Sk.builtin.pyCheckType("newS","string",Sk.builtin.checkString(b));if(void 0!==d&&!Sk.builtin.checkInt(d))throw new Sk.builtin.TypeError("integer argument expected, got "+Sk.abstr.typeName(d));d=Sk.builtin.asnum$(d);var f=new RegExp(Sk.builtin.str.re_escape_(c.v),"g");if(void 0===d||0>d)return new Sk.builtin.str(a.v.replace(f,b.v));var e=0;return new Sk.builtin.str(a.v.replace(f,
function(a){e++;return e<=d?b.v:a}))});Sk.builtin.str.prototype.zfill=new Sk.builtin.func(function(a,c){var b=a.v,d="";Sk.builtin.pyCheckArgsLen("zfill",arguments.length,2,2);if(!Sk.builtin.checkInt(c))throw new Sk.builtin.TypeError("integer argument expected, got "+Sk.abstr.typeName(c));var e=c.v-b.length;var h="+"===b[0]||"-"===b[0]?1:0;for(var l=0;l<e;l++)d+="0";b=b.substr(0,h)+d+b.substr(h);return new Sk.builtin.str(b)});Sk.builtin.str.prototype.isdigit=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("isdigit",
arguments.length,1,1);return new Sk.builtin.bool(/^\d+$/.test(a.v))});Sk.builtin.str.prototype.isspace=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("isspace",arguments.length,1,1);return new Sk.builtin.bool(/^\s+$/.test(a.v))});Sk.builtin.str.prototype.expandtabs=new Sk.builtin.func(function(a,c){Sk.builtin.pyCheckArgsLen("expandtabs",arguments.length,1,2);if(void 0!==c&&!Sk.builtin.checkInt(c))throw new Sk.builtin.TypeError("integer argument expected, got "+Sk.abstr.typeName(c));c=void 0===
c?8:Sk.builtin.asnum$(c);var b=Array(c+1).join(" ");var d=a.v.replace(/([^\r\n\t]*)\t/g,function(a,d){return d+b.slice(d.length%c)});return new Sk.builtin.str(d)});Sk.builtin.str.prototype.swapcase=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("swapcase",arguments.length,1,1);var c=a.v.replace(/[a-z]/gi,function(a){var c=a.toLowerCase();return c===a?a.toUpperCase():c});return new Sk.builtin.str(c)});Sk.builtin.str.prototype.splitlines=new Sk.builtin.func(function(a,c){var b=a.v,d,e=a.v.length,
h=[],l=0;Sk.builtin.pyCheckArgsLen("splitlines",arguments.length,1,2);if(void 0!==c&&!Sk.builtin.checkBool(c))throw new Sk.builtin.TypeError("boolean argument expected, got "+Sk.abstr.typeName(c));c=void 0===c?!1:c.v;for(d=0;d<e;d++){var q=b.charAt(d);if("\n"===b.charAt(d+1)&&"\r"===q)q=d+2,l=b.slice(l,q),c||(l=l.replace(/(\r|\n)/g,"")),h.push(new Sk.builtin.str(l)),l=q;else if("\n"===q&&"\r"!==b.charAt(d-1)||"\r"===q)q=d+1,l=b.slice(l,q),c||(l=l.replace(/(\r|\n)/g,"")),h.push(new Sk.builtin.str(l)),
l=q}l<e&&(l=b.slice(l,e),c||(l=l.replace(/(\r|\n)/g,"")),h.push(new Sk.builtin.str(l)));return new Sk.builtin.list(h)});Sk.builtin.str.prototype.title=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("title",arguments.length,1,1);var c=a.v.replace(/[a-z][a-z]*/gi,function(a){return a[0].toUpperCase()+a.substr(1).toLowerCase()});return new Sk.builtin.str(c)});Sk.builtin.str.prototype.isalpha=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("isalpha",arguments.length,1,1);return new Sk.builtin.bool(a.v.length&&
!/[^a-zA-Z]/.test(a.v))});Sk.builtin.str.prototype.isalnum=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("isalnum",arguments.length,1,1);return new Sk.builtin.bool(a.v.length&&!/[^a-zA-Z0-9]/.test(a.v))});Sk.builtin.str.prototype.isnumeric=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("isnumeric",arguments.length,1,1);return new Sk.builtin.bool(a.v.length&&!/[^0-9]/.test(a.v))});Sk.builtin.str.prototype.islower=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("islower",
arguments.length,1,1);return new Sk.builtin.bool(a.v.length&&/[a-z]/.test(a.v)&&!/[A-Z]/.test(a.v))});Sk.builtin.str.prototype.isupper=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("isupper",arguments.length,1,1);return new Sk.builtin.bool(a.v.length&&!/[a-z]/.test(a.v)&&/[A-Z]/.test(a.v))});Sk.builtin.str.prototype.istitle=new Sk.builtin.func(function(a){var c=a.v,b=!1,d=!1,e;Sk.builtin.pyCheckArgsLen("istitle",arguments.length,1,1);for(e=0;e<c.length;e++){var n=c.charAt(e);if(!/[a-z]/.test(n)&&
/[A-Z]/.test(n)){if(d)return new Sk.builtin.bool(!1);b=d=!0}else if(/[a-z]/.test(n)&&!/[A-Z]/.test(n)){if(!d)return new Sk.builtin.bool(!1);b=!0}else d=!1}return new Sk.builtin.bool(b)});Sk.builtin.str.prototype.encode=new Sk.builtin.func(function(a,c,b){Sk.builtin.pyCheckArgsLen("encode",arguments.length,1,3);c=c||Sk.builtin.str.$utf8;Sk.builtin.pyCheckType("encoding","string",Sk.builtin.checkString(c));c=c.v;void 0!==b?(Sk.builtin.pyCheckType("errors","string",Sk.builtin.checkString(b)),b=b.v):
b="strict";const d=Sk.builtin.bytes.$strEncode(a,c,b);return Sk.__future__.python3?d:new Sk.builtin.str(d.$jsstr())});Sk.builtin.str.$py2decode=new Sk.builtin.func(function(a,c,b){Sk.builtin.pyCheckArgsLen("decode",arguments.length,1,3);const d=new Sk.builtin.bytes(a.$jsstr());return Sk.builtin.bytes.$decode(d,c,b)});Sk.builtin.str.prototype.nb$remainder=function(a){var c;const b=this.sk$builtinBase;a.constructor===Sk.builtin.tuple||void 0!==a.mp$subscript&&a.constructor!==b||(a=new Sk.builtin.tuple([a]));
var d=0;var e=this.$jsstr().replace(/%(\([a-zA-Z0-9]+\))?([#0 +\-]+)?(\*|[0-9]+)?(\.(\*|[0-9]+))?[hlL]?([diouxXeEfFgGcrsb%])/g,function(f,e,g,h,k,y,t){var u,l,z,n,q;h=Sk.builtin.asnum$(h);k=Sk.builtin.asnum$(k);void 0!==e&&""!==e||"%"==t||(u=d++);""===k&&(k=void 0);var m=l=z=n=q=!1;g&&(-1!==g.indexOf("-")?n=!0:-1!==g.indexOf("0")&&(q=!0),-1!==g.indexOf("+")?l=!0:-1!==g.indexOf(" ")&&(z=!0),m=-1!==g.indexOf("#"));k&&(k=parseInt(k.substr(1),10));g=function(a,c){var b;c=Sk.builtin.asnum$(c);var d=!1;
if("number"===typeof a){0>a&&(a=-a,d=!0);var f=a.toString(c)}else a instanceof Sk.builtin.float_?(f=a.str$(c,!1),2<f.length&&".0"===f.substr(-2)&&(f=f.substr(0,f.length-2)),d=a.nb$isnegative()):a instanceof Sk.builtin.int_?(f=a.str$(c,!1),d=a.nb$isnegative()):a instanceof Sk.builtin.lng&&(f=a.str$(c,!1),d=a.nb$isnegative());Sk.asserts.assert(void 0!==f,"unhandled number format");a=!1;if(k)for(b=f.length;b<k;++b)f="0"+f,a=!0;b="";d?b="-":l?b="+"+b:z&&(b=" "+b);m&&(16===c?b+="0x":8!==c||a||"0"===f||
(b+="0"));return[b,f]};f=function(a){var c=a[0];a=a[1];if(h){h=parseInt(h,10);var b=a.length+c.length;if(q)for(;b<h;++b)a="0"+a;else if(n){for(;b<h;++b)a+=" ";Sk.__future__.python3&&(a+=c,c="")}else for(;b<h;++b)c=" "+c}return c+a};if(a.constructor===Sk.builtin.tuple)e=a.v[u];else if(void 0!==a.mp$subscript&&void 0!==e)e=e.substring(1,e.length-1),e=a.mp$subscript(new b(e));else if(a.constructor===Sk.builtin.dict||a.constructor===Sk.builtin.list)e=a;else throw new Sk.builtin.AttributeError(a.tp$name+
" instance has no attribute 'mp$subscript'");if("d"===t||"i"===t){var p=g(e,10);if(void 0===p[1])throw new Sk.builtin.TypeError("%"+t+" format: a number is required, not "+Sk.abstr.typeName(e));t=p[1];p[1]=-1!==t.indexOf(".")?parseInt(t,10).toString():t;return f(p)}if("o"===t)return f(g(e,8));if("x"===t)return f(g(e,16));if("X"===t)return f(g(e,16)).toUpperCase();if("f"===t||"F"===t||"e"===t||"E"===t||"g"===t||"G"===t){p=Sk.builtin.asnum$(e);"string"===typeof p&&(p=Number(p));if(Infinity===p)return"inf";
if(-Infinity===p)return"-inf";if(isNaN(p))return"nan";u=["toExponential","toFixed","toPrecision"]["efg".indexOf(t.toLowerCase())];if(void 0===k||""===k)if("e"===t||"E"===t)k=6;else if("f"===t||"F"===t)k=Sk.__future__.python3?6:7;u=p[u](k);Sk.builtin.checkFloat(e)&&0===p&&-Infinity===1/p&&(u="-"+u);Sk.__future__.python3&&(7<=u.length&&"0.0000"==u.slice(0,6)&&(c=parseFloat(u),u=c.toExponential()),"-"==u.charAt(u.length-2)&&(u=u.slice(0,u.length-1)+"0"+u.charAt(u.length-1)));-1!=="EFG".indexOf(t)&&(u=
u.toUpperCase());return f(["",u])}if("c"===t){if("number"===typeof e)return String.fromCharCode(e);if(e instanceof Sk.builtin.int_)return String.fromCharCode(e.v);if(e instanceof Sk.builtin.float_)return String.fromCharCode(e.v);if(e instanceof Sk.builtin.lng)return String.fromCharCode(e.str$(10,!1)[0]);if(e.constructor===Sk.builtin.str)return e.v.substr(0,1);throw new Sk.builtin.TypeError("an integer is required");}if("r"===t)return t=Sk.builtin.repr(e),k?t.v.substr(0,k):t.v;if("s"===t&&b===Sk.builtin.str){t=
new Sk.builtin.str(e);t=t.$jsstr();if(k)return t.substr(0,k);h&&(t=f([" ",t]));return t}if("b"===t||"s"===t){if(b===Sk.builtin.str)throw new Sk.builtin.ValueError("unsupported format character 'b'");if(!(e instanceof Sk.builtin.bytes)&&void 0===(p=Sk.abstr.lookupSpecial(e,Sk.builtin.str.$bytes)))throw new Sk.builtin.TypeError("%b requires a bytes-like object, or an object that implements __bytes__, not '"+Sk.abstr.typeName(e)+"'");void 0!==p&&(e=new Sk.builtin.bytes(e));t=e.$jsstr();if(k)return t.substr(0,
k);h&&(t=f([" ",t]));return t}if("%"===t)return"%"});return new b(e)};Sk.builtin.str_iter_=function(a){if(!(this instanceof Sk.builtin.str_iter_))return new Sk.builtin.str_iter_(a);this.$index=0;this.$obj=a.v.slice();this.tp$iter=()=>this;a.$hasAstralCodePoints()?(this.sq$length=a.codepoints.length,this.$codepoints=a.codepoints.slice(),this.tp$iternext=function(){if(!(this.$index>=this.sq$length)){var a=new Sk.builtin.str(this.$obj.substring(this.$codepoints[this.$index],this.$codepoints[this.$index+
1]));this.$index++;return a}}):(this.sq$length=this.$obj.length,this.tp$iternext=function(){if(!(this.$index>=this.sq$length))return new Sk.builtin.str(this.$obj.substr(this.$index++,1))});this.$r=function(){return new Sk.builtin.str("iterator")};return this};Sk.abstr.setUpInheritance("iterator",Sk.builtin.str_iter_,Sk.builtin.object);Sk.builtin.str_iter_.prototype.__class__=Sk.builtin.str_iter_;Sk.builtin.str_iter_.prototype.__iter__=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("__iter__",
arguments.length,0,0,!0,!1);return a});Sk.builtin.str_iter_.prototype.next$=function(a){a=a.tp$iternext();if(void 0===a)throw new Sk.builtin.StopIteration;return a};var d={"abstract":!0,as:!0,"boolean":!0,"break":!0,"byte":!0,"case":!0,"catch":!0,"char":!0,"class":!0,"continue":!0,"const":!0,"debugger":!0,"default":!0,"delete":!0,"do":!0,"double":!0,"else":!0,"enum":!0,"export":!0,"extends":!0,"false":!0,"final":!0,"finally":!0,"float":!0,"for":!0,"function":!0,"goto":!0,"if":!0,"implements":!0,"import":!0,
"in":!0,"instanceof":!0,"int":!0,"interface":!0,is:!0,"long":!0,namespace:!0,"native":!0,"new":!0,"null":!0,"package":!0,"private":!0,"protected":!0,"public":!0,"return":!0,"short":!0,"static":!0,"switch":!0,"synchronized":!0,"this":!0,"throw":!0,"throws":!0,"transient":!0,"true":!0,"try":!0,"typeof":!0,use:!0,"var":!0,"void":!0,"volatile":!0,"while":!0,"with":!0,__defineGetter__:!0,__defineSetter__:!0,apply:!0,arguments:!0,call:!0,caller:!0,eval:!0,hasOwnProperty:!0,isPrototypeOf:!0,__lookupGetter__:!0,
__lookupSetter__:!0,__noSuchMethod__:!0,propertyIsEnumerable:!0,prototype:!0,toSource:!0,toLocaleString:!0,toString:!0,unwatch:!0,valueOf:!0,watch:!0,length:!0,name:!0};Sk.builtin.str.reservedWords_=d},function(m,p,a){function b(a){var c=a.replace(/\s+/g,"").toLowerCase();c=n[c];return void 0===c?a:c}function c(a){if(Uint8Array.from)return Uint8Array.from(a);const c=new Uint8Array(a.length);for(let b=0;b<a.length;b++)c[b]=a[b];return c}function d(a,d,f){a=a.$jsstr();d=b(d);if("strict"!==f&&"ignore"!==
f&&"replace"!==f)throw new Sk.builtin.NotImplementedError("'"+f+"' error handling not implemented in Skulpt");if("ascii"===d){d=[];for(e in a){const c=a.charCodeAt(e);if(0>c||127<c){if("strict"===f)throw f=h(c),new Sk.builtin.UnicodeEncodeError("'ascii' codec can't encode character '"+f+"' in position "+e+": ordinal not in range(128)");"replace"===f&&d.push(63)}else d.push(c)}var e=c(d)}else if("utf-8"===d)e=l.encode(a);else throw new Sk.builtin.LookupError("unknown encoding: "+d);return new Sk.builtin.bytes(e)}
function e(a,c,b){Sk.builtin.pyCheckArgsLen("bytes",arguments.length,0,3);let f;var e;if(1<arguments.length){if(!Sk.builtin.checkString(c))throw new Sk.builtin.TypeError("bytes() argument 2 must be str not "+Sk.abstr.typeName(c));if(void 0!==b&&!Sk.builtin.checkString(b))throw new Sk.builtin.TypeError("bytes() argument 3 must be str not "+Sk.abstr.typeName(c));if(!Sk.builtin.checkString(a))throw new Sk.builtin.TypeError((void 0!==c?"encoding":"errors")+" without a string argument");}if(void 0===a)return new Sk.builtin.bytes;
if(Sk.builtin.checkString(a)){if(void 0===c)throw new Sk.builtin.TypeError("string argument without an encoding");b=void 0===b?"strict":b.$jsstr();c=c.$jsstr();return d(a,c,b)}if(Sk.builtin.checkInt(a)){f=Sk.builtin.asnum$(a);if(0>f)throw new Sk.builtin.ValueError("negative count");if(f>Number.MAX_SAFE_INTEGER)throw new Sk.builtin.OverflowError("cannot fit 'int' into an index-sized integer");return new Sk.builtin.bytes(f)}if(Sk.builtin.checkBytes(a))return new Sk.builtin.bytes(a.v);if(null!=(e=Sk.abstr.lookupSpecial(a,
Sk.builtin.str.$bytes)))return e=Sk.misceval.callsimOrSuspendArray(e,[a]),Sk.misceval.chain(e,a=>{if(!Sk.builtin.checkBytes(a))throw new Sk.builtin.TypeError("__bytes__ returned non-bytes (type "+Sk.abstr.typeName(a)+")");return a});if(Sk.builtin.checkIterable(a))return f=[],e=Sk.misceval.iterFor(Sk.abstr.iter(a),a=>{if(!Sk.misceval.isIndex(a))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(a)+"' object cannot be interpreted as an integer");a=Sk.misceval.asIndex(a);if(0>a||255<a)throw new Sk.builtin.ValueError("bytes must be in range(0, 256)");
f.push(a)}),Sk.misceval.chain(e,()=>new Sk.builtin.bytes(f));e="";void 0===a.sk$object&&(e+=", if calling constructor with a javascript object use 'new'");throw new Sk.builtin.TypeError("cannot convert '"+Sk.abstr.typeName(a)+"' object into bytes"+e);}function h(a){var c=265>=a?"\\x":"\\u";a=a.toString(16);3===a.length&&(a=a.slice(1,3));return a=1===a.length?c+"0"+a:c+a}function g(a){return function(c){return c instanceof Sk.builtin.bytes?a(this.$jsstr(),c.$jsstr()):Sk.builtin.NotImplemented.NotImplemented$}}
function f(a,c,b){a=a.v.byteLength;if(void 0===c||c===Sk.builtin.none.none$)c=0;else if(Sk.misceval.isIndex(c))c=Sk.misceval.asIndex(c),c=0<=c?c:a+c,0>c&&(c=0);else throw new Sk.builtin.TypeError("slice indices must be integers or None or have an __index__ method");if(void 0===b||Sk.builtin.checkNone(b))b=a;else if(Sk.misceval.isIndex(b))b=Sk.misceval.asIndex(b),b=0<=b?b:a+b,0>b?b=0:b>a&&(b=a);else throw new Sk.builtin.TypeError("slice indices must be integers or None or have an __index__ method");
return{start:c,end:b}}function k(a){return 9<=a&&13>=a||32===a}a(22);const n={utf:"utf-8",utf8:"utf-8","utf-8":"utf-8",ascii:"ascii"},l=new TextEncoder,q=new TextDecoder;Sk.builtin.bytes=function(a,b,d){if(!(this instanceof Sk.builtin.bytes))return e(...arguments);if(void 0===a)this.v=new Uint8Array;else if(a instanceof Uint8Array)this.v=a;else if(Array.isArray(a))Sk.asserts.assert(a.every(a=>0<=a&&256>a),"bad internal call to bytes with array"),this.v=c(a);else if("string"===typeof a){const b=[];
for(let c in a){var f=a.charCodeAt(c);if(255<f)throw new Sk.builtin.UnicodeDecodeError("invalid string (possibly contains a unicode character)");b.push(f)}this.v=c(b)}else if("number"===typeof a)this.v=new Uint8Array(a);else return f=Sk.misceval.chain(e(...arguments),a=>{this.v=a.v;return this}),Sk.misceval.retryOptionalSuspensionOrThrow(f)};Sk.abstr.setUpInheritance("bytes",Sk.builtin.bytes,Sk.builtin.seqtype);Sk.builtin.bytes.prototype.__class__=Sk.builtin.bytes;Sk.builtin.bytes.prototype.sk$builtinBase=
Sk.builtin.bytes;Sk.builtin.bytes.$strEncode=d;Sk.builtin.bytes.prototype.$jsstr=function(){let a="";for(let c=0;c<this.v.byteLength;c++)a+=String.fromCharCode(this.v[c]);return a};Sk.builtin.bytes.prototype.tp$hash=function(){return Sk.builtin.hash(new Sk.builtin.str(this.$jsstr()))};Sk.builtin.bytes.prototype.$r=function(){let a,c="'";const b=-1!==this.v.indexOf(34);let d="";for(let f=0;f<this.v.byteLength;f++)if(a=this.v[f],9>a||10<a&&13>a||13<a&&32>a||126<a)d+=h(a);else if(9===a||10===a||13===
a||39===a||92===a)switch(a){case 9:d+="\\t";break;case 10:d+="\\n";break;case 13:d+="\\r";break;case 39:b?d+="\\'":(d+="'",c='"');break;case 92:d+="\\\\"}else d+=String.fromCharCode(a);return new Sk.builtin.str("b"+c+d+c)};Sk.builtin.bytes.prototype.mp$subscript=function(a){if(Sk.misceval.isIndex(a)){var c=Sk.misceval.asIndex(a);if(void 0!==c){0>c&&(c=this.v.byteLength+c);if(0>c||c>=this.v.byteLength)throw new Sk.builtin.IndexError("index out of range");return new Sk.builtin.int_(this.v[c])}}else if(a instanceof
Sk.builtin.slice){var b=[];a.sssiter$(this.v.byteLength,a=>{b.push(this.v[a])});return new Sk.builtin.bytes(b)}throw new Sk.builtin.TypeError("byte indices must be integers, not "+Sk.abstr.typeName(a));};Sk.builtin.bytes.prototype.ob$eq=function(a){if(this===a)return Sk.builtin.bool.true$;if(!(a instanceof Sk.builtin.bytes))return Sk.builtin.NotImplemented.NotImplemented$;if(this.v.byteLength!=a.v.byteLength)return Sk.builtin.bool.false$;for(let c=0;c<this.v.byteLength;c++)if(this.v[c]!=a.v[c])return Sk.builtin.bool.false$;
return Sk.builtin.bool.true$};Sk.builtin.bytes.prototype.ob$ne=function(a){a=this.ob$eq(a);return a===Sk.builtin.NotImplemented.NotImplemented$?a:Sk.misceval.isTrue(a)?Sk.builtin.bool.false$:Sk.builtin.bool.true$};Sk.builtin.bytes.prototype.ob$lt=g((a,c)=>new Sk.builtin.bool(a<c));Sk.builtin.bytes.prototype.ob$le=g((a,c)=>new Sk.builtin.bool(a<=c));Sk.builtin.bytes.prototype.ob$gt=g((a,c)=>new Sk.builtin.bool(a>c));Sk.builtin.bytes.prototype.ob$ge=g((a,c)=>new Sk.builtin.bool(a>=c));Sk.builtin.bytes.prototype.sq$length=
function(){return this.v.byteLength};Sk.builtin.bytes.prototype.sq$concat=function(a){var c;if(!(a instanceof Sk.builtin.bytes))throw new Sk.builtin.TypeError("can't concat "+Sk.abstr.typeName(a)+" to bytes");var b=[];for(c=0;c<this.v.byteLength;c++)b.push(this.v[c]);for(c=0;c<a.v.byteLength;c++)b.push(a.v[c]);return new Sk.builtin.bytes(b)};Sk.builtin.bytes.prototype.nb$add=Sk.builtin.bytes.prototype.sq$concat;Sk.builtin.bytes.prototype.nb$inplace_add=Sk.builtin.bytes.prototype.sq$concat;Sk.builtin.bytes.prototype.sq$repeat=
function(a){var c,b;if(!(a instanceof Sk.builtin.int_))throw new Sk.builtin.TypeError("can't multiply sequence by non-int of type '"+Sk.abstr.typeName(a)+"'");var d=[];for(b=0;b<a.v;b++)for(c=0;c<this.v.byteLength;c++)d.push(this.v[c]);return new Sk.builtin.bytes(d)};Sk.builtin.bytes.prototype.nb$multiply=Sk.builtin.bytes.prototype.sq$repeat;Sk.builtin.bytes.prototype.nb$inplace_multiply=Sk.builtin.bytes.prototype.sq$repeat;Sk.builtin.bytes.prototype.sq$contains=function(a){if(Sk.builtin.checkInt(a)){a=
Sk.builtin.asnum$(a);if(0>a||255<a)throw new Sk.builtin.ValueError("byte must be in range(0, 256)");return-1!==this.v.indexOf(a)}if(!(a instanceof Sk.builtin.bytes))throw new Sk.builtin.TypeError("a bytes-like object is required, not "+Sk.abstr.typeName(a));if(0===a.v.byteLength)return!0;if(1===a.v.byteLength)return-1!==this.v.indexOf(a.v[0]);for(var c=0;c<this.v.byteLength;){c=this.v.indexOf(a.v[0],c);if(-1===c)break;let b=!0;for(let d=0;d<a.v.byteLength;d++)if(this.v[c+d]!==a.v[d]){b=!1;break}if(b)return!0;
c+=1}return!1};Sk.builtin.bytes.prototype.nb$remainder=Sk.builtin.str.prototype.nb$remainder;Sk.builtin.bytes.$decode=function(a,c,d){var f;Sk.builtin.pyCheckArgsLen("decode",arguments.length-1,0,2);if(void 0===c)c="utf-8";else if(c instanceof Sk.builtin.str)c=c.v;else throw new Sk.builtin.TypeError("decode() argument 1 must be str, not "+Sk.abstr.typeName(c));c=b(c);if(void 0===d)d="strict";else if(d instanceof Sk.builtin.str)d=d.v;else throw new Sk.builtin.TypeError("decode() argument 2 must be str, not "+
Sk.abstr.typeName(d));if("strict"!==d&&"ignore"!==d&&"replace"!==d)throw new Sk.builtin.NotImplementedError("'"+d+"' error handling not implemented in Skulpt");if("ascii"!==c&&"utf-8"!==c)throw new Sk.builtin.LookupError("unknown encoding: "+c.v);if("ascii"===c){var e="";for(f=0;f<a.v.byteLength;f++){var g=a.v[f];if(127<g){if("strict"===d)throw g=g.toString(16),new Sk.builtin.UnicodeDecodeError("'ascii' codec can't decode byte 0x"+g+" in position "+f.toString()+": ordinal not in range(128)");"replace"===
d&&(e+=String.fromCharCode(65533))}else e+=String.fromCharCode(g)}}else{g=q.decode(a.v);if("replace"===d)return new Sk.builtin.str(g);e="";for(f in g)if(65533===g[f].charCodeAt(0)){if("strict"===d)throw g=a.v[f],g=g.toString(16),new Sk.builtin.UnicodeDecodeError("'utf-8' codec can't decode byte 0x"+g+" in position "+f.toString()+": invalid start byte");}else e+=g[f]}return new Sk.builtin.str(e)};Sk.builtin.bytes.prototype.decode=new Sk.builtin.func(Sk.builtin.bytes.$decode);Sk.builtin.bytes.prototype.fromhex=
new Sk.builtin.func(function(a){var c;Sk.builtin.pyCheckArgsLen("fromhex",arguments.length,1,1);if(!(a instanceof Sk.builtin.str))throw new Sk.builtin.TypeError("fromhex() argument must be str, not "+Sk.abstr.typeName(a));var b=[];var d=function(a){return"0123456789abcdefABCDEF".includes(a)?!0:!1};var f=function(a){a=a.charCodeAt(0);return 9===a||10===a||11===a||12===a||13===a||32===a||133===a?!0:!1};for(c=0;c<a.v.length;){var e=a.v.charAt(c);if(d(e))if(c+1<a.v.length)if(d(a.v.charAt(c+1)))e=a.v.slice(c,
c+2),e=parseInt(e,16),b.push(e),c+=2;else throw new Sk.builtin.ValueError("non-hexadecimal number found in fromhex() arg at position "+(c+1).toString());else throw new Sk.builtin.ValueError("non-hexadecimal number found in fromhex() arg at position "+c.toString());else if(f(e))c++;else throw new Sk.builtin.ValueError("non-hexadecimal number found in fromhex() arg at position "+c.toString());}return new Sk.builtin.bytes(b)});Sk.builtin.bytes.prototype.hex=new Sk.builtin.func(function(a){var c;Sk.builtin.pyCheckArgsLen("hex",
arguments.length-1,0,0);var b="";for(c=0;c<a.v.byteLength;c++){var d=a.v[c];d=d.toString(16);1===d.length&&(d="0"+d);b+=d}return new Sk.builtin.str(b)});Sk.builtin.bytes.prototype.count=new Sk.builtin.func(function(a,c,b,d){var e;Sk.builtin.pyCheckArgsLen("count",arguments.length-1,1,3);({start:b,end:d}=f(a,b,d));var g=0;if(c instanceof Sk.builtin.int_)for(e=b;e<d;e++)a.v[e]===c.v&&g++;else if(c instanceof Sk.builtin.bytes)for(e=c.v.byteLength;b<d;){const f=a.find$left(c,b,d);if(-1===f)break;g++;
b=f+e}else throw new Sk.builtin.TypeError("argument should be integer or bytes-like object, not '"+Sk.abstr.typeName(c)+"'");return new Sk.builtin.int_(g)});Sk.builtin.bytes.prototype.endswith=new Sk.builtin.func(function(a,c,b,d){function e(c){const f=c.v.byteLength;if(d-b>=f){for(let b=d-f,e=0;b<d;b++,e++)if(a.v[b]!==c.v[e])return!1;return!0}return!1}Sk.builtin.pyCheckArgsLen("endswith",arguments.length-1,1,3);if(!(c instanceof Sk.builtin.bytes||c instanceof Sk.builtin.tuple))throw new Sk.builtin.TypeError("endswith first arg must be bytes or a tuple of bytes, not "+
Sk.abstr.typeName(c));({start:b,end:d}=f(a,b,d));if(d<b)return Sk.builtin.bool.false$;if(c instanceof Sk.builtin.tuple){for(let a=Sk.abstr.iter(c),b=a.tp$iternext();void 0!==b;b=a.tp$iternext()){if(!(b instanceof Sk.builtin.bytes))throw new Sk.builtin.TypeError("a bytes-like object is required, not '"+Sk.abstr.typeName(b)+"'");if(e(b))return Sk.builtin.bool.true$}return Sk.builtin.bool.false$}return e(c)?Sk.builtin.bool.true$:Sk.builtin.bool.false$});Sk.builtin.bytes.prototype.find$left=function(a,
c,b){let d=-1;({start:c,end:b}=f(this,c,b));if(a instanceof Sk.builtin.int_)for(var e=c;e<b;e++){if(this.v[e]===a.v){d=e;break}}else if(a instanceof Sk.builtin.bytes)for(e=a.v.byteLength;c<b-e+1;c++){let b=!0;for(let d=c,f=0;f<e;d++,f++)if(this.v[d]!==a.v[f]){b=!1;break}if(b){d=c;break}}else throw new Sk.builtin.TypeError("argument should be integer or bytes-like object, not '"+Sk.abstr.typeName(a)+"'");return d};Sk.builtin.bytes.prototype.find$right=function(a,c,b){let d=-1;({start:c,end:b}=f(this,
c,b));if(a instanceof Sk.builtin.int_)for(var e=b-1;e>=c;e--){if(this.v[e]===a.v){d=e;break}}else if(a instanceof Sk.builtin.bytes)for(e=a.v.byteLength,b-=e;b>=c;b--){let c=!0;for(let d=b,f=0;f<e;d++,f++)if(this.v[d]!==a.v[f]){c=!1;break}if(c){d=b;break}}else throw new Sk.builtin.TypeError("argument should be integer or bytes-like object, not '"+Sk.abstr.typeName(a)+"'");return d};Sk.builtin.bytes.prototype.find=new Sk.builtin.func(function(a,c,b,d){Sk.builtin.pyCheckArgsLen("find",arguments.length-
1,1,3);return Sk.builtin.int_(a.find$left(c,b,d))});Sk.builtin.bytes.prototype.index=new Sk.builtin.func(function(a,c,b,d){Sk.builtin.pyCheckArgsLen("index",arguments.length-1,1,3);var f=a.find$left(c,b,d);if(-1===f)throw new Sk.builtin.ValueError("subsection not found");return Sk.builtin.int_(f)});Sk.builtin.bytes.prototype.join=new Sk.builtin.func(function(a,c){var b,d;Sk.builtin.pyCheckArgsLen("join",arguments.length-1,1,1);if(!Sk.builtin.checkIterable(c))throw Sk.builtin.TypeError("can only join an iterable");
var f=[];var e=[];for(b=0;b<a.v.byteLength;b++)e.push(a.v[b]);b=0;var g=Sk.abstr.iter(c);for(d=g.tp$iternext();void 0!==d;d=g.tp$iternext()){if(!(d instanceof Sk.builtin.bytes))throw new Sk.builtin.TypeError("sequence item "+b.toString()+": expected a bytes-like object, "+Sk.abstr.typeName(d)+" found");0<f.length&&(f=f.concat(e));for(b=0;b<d.v.byteLength;b++)f.push(d.v[b]);b++}return new Sk.builtin.bytes(f)});Sk.builtin.bytes.prototype.maketrans=new Sk.builtin.func(function(){throw new Sk.builtin.NotImplementedError("maketrans() bytes method not implemented in Skulpt");
});Sk.builtin.bytes.prototype.partition=new Sk.builtin.func(function(a,c){Sk.builtin.pyCheckArgsLen("partition",arguments.length-1,1,1);if(!(c instanceof Sk.builtin.bytes))throw new Sk.builtin.TypeError("a bytes-like object is required, not '"+Sk.abstr.typeName(c)+"'");var b=a.find$left(c);if(-1===b){var d=a;var f=new Sk.builtin.bytes(0);b=new Sk.builtin.bytes(0)}else d=new Sk.builtin.bytes(a.v.subarray(0,b)),f=new Sk.builtin.bytes(a.v.subarray(b,b+c.v.byteLength)),b=new Sk.builtin.bytes(a.v.subarray(b+
c.v.byteLength,a.v.byteLength));return new Sk.builtin.tuple([d,f,b])});Sk.builtin.bytes.prototype.replace=new Sk.builtin.func(function(a,c,b,d){var f,e;Sk.builtin.pyCheckArgsLen("replace",arguments.length-1,2,3);if(!(c instanceof Sk.builtin.bytes))throw new Sk.builtin.TypeError("a bytes-like object is required, not '"+Sk.abstr.typeName(c)+"'");if(!(b instanceof Sk.builtin.bytes))throw new Sk.builtin.TypeError("a bytes-like object is required, not '"+Sk.abstr.typeName(b)+"'");if(void 0===d)d=-1;else if(d instanceof
Sk.builtin.int_)d=d.v;else throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(d)+"' object cannot be interpreted as an integer");var g=[];var h=[];for(f=0;f<b.v.byteLength;f++)h.push(b.v[f]);var k=c.v.byteLength;for(e=f=0;f<a.v.byteLength&&(-1===d||e<d);){const b=a.find$left(c,f,a.v.byteLength);if(-1===b)break;for(;f<b;f++)g.push(a.v[f]);g=g.concat(h);f=b+k;e++}for(k=f;k<a.v.byteLength;k++)g.push(a.v[k]);return new Sk.builtin.bytes(g)});Sk.builtin.bytes.prototype.rfind=new Sk.builtin.func(function(a,
c,b,d){Sk.builtin.pyCheckArgsLen("rfind",arguments.length-1,1,3);return Sk.builtin.int_(a.find$right(c,b,d))});Sk.builtin.bytes.prototype.rindex=new Sk.builtin.func(function(a,c,b,d){Sk.builtin.pyCheckArgsLen("rindex",arguments.length-1,1,3);var f=a.find$right(c,b,d);if(-1===f)throw new Sk.builtin.ValueError("subsection not found");return Sk.builtin.int_(f)});Sk.builtin.bytes.prototype.rpartition=new Sk.builtin.func(function(a,c){Sk.builtin.pyCheckArgsLen("rpartition",arguments.length-1,1,1);if(!(c instanceof
Sk.builtin.bytes))throw new Sk.builtin.TypeError("a bytes-like object is required, not '"+Sk.abstr.typeName(c)+"'");var b=a.find$right(c);if(-1===b){var d=new Sk.builtin.bytes(0);var f=new Sk.builtin.bytes(0);return new Sk.builtin.tuple([d,f,a])}d=new Sk.builtin.bytes(a.v.subarray(0,b));f=new Sk.builtin.bytes(a.v.subarray(b,b+c.v.byteLength));b=new Sk.builtin.bytes(a.v.subarray(b+c.v.byteLength,a.v.byteLength));return new Sk.builtin.tuple([d,f,b])});Sk.builtin.bytes.prototype.startswith=new Sk.builtin.func(function(a,
c,b,d){function e(c){const f=c.v.byteLength;if(b+f<=d){for(let d=b,e=0;e<f;d++,e++)if(a.v[d]!==c.v[e])return!1;return!0}return!1}Sk.builtin.pyCheckArgsLen("startswith",arguments.length-1,1,3);if(!(c instanceof Sk.builtin.bytes||c instanceof Sk.builtin.tuple))throw new Sk.builtin.TypeError("startswith first arg must be bytes or a tuple of bytes, not "+Sk.abstr.typeName(c));({start:b,end:d}=f(a,b,d));if(d<b)return Sk.builtin.bool.false$;if(c instanceof Sk.builtin.tuple){for(let a=Sk.abstr.iter(c),b=
a.tp$iternext();void 0!==b;b=a.tp$iternext()){if(!(b instanceof Sk.builtin.bytes))throw new Sk.builtin.TypeError("a bytes-like object is required, not '"+Sk.abstr.typeName(b)+"'");if(e(b))return Sk.builtin.bool.true$}return Sk.builtin.bool.false$}return e(c)?Sk.builtin.bool.true$:Sk.builtin.bool.false$});Sk.builtin.bytes.prototype.translate=new Sk.builtin.func(function(){throw new Sk.builtin.NotImplementedError("translate() bytes method not implemented in Skulpt");});Sk.builtin.bytes.prototype.center=
new Sk.builtin.func(function(a,c,b){Sk.builtin.pyCheckArgsLen("center",arguments.length-1,1,2);if(void 0===b)b=32;else if(b instanceof Sk.builtin.bytes&&1==b.v.byteLength)b=b.v[0];else throw new Sk.builtin.TypeError("center() argument 2 must be a byte string of length 1, not "+Sk.abstr.typeName(b));if(c instanceof Sk.builtin.int_)c=c.v;else throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(c)+"' object cannot be interpreted as an integer");if(c<=a.v.byteLength)return a;var d=[];var f=c-a.v.byteLength;
if(f%2){var e=f/2-.5;var g=f/2+.5}else g=e=f/2;for(f=0;f<e;f++)d.push(b);for(f=0;f<a.v.byteLength;f++)d.push(a.v[f]);for(f=0;f<g;f++)d.push(b);return new Sk.builtin.bytes(d)});Sk.builtin.bytes.prototype.ljust=new Sk.builtin.func(function(a,c,b){var d;Sk.builtin.pyCheckArgsLen("ljust",arguments.length-1,1,2);if(void 0===b)b=32;else if(b instanceof Sk.builtin.bytes&&1==b.v.byteLength)b=b.v[0];else throw new Sk.builtin.TypeError("ljust() argument 2 must be a byte string of length 1, not "+Sk.abstr.typeName(b));
if(c instanceof Sk.builtin.int_)c=c.v;else throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(c)+"' object cannot be interpreted as an integer");if(c<=a.v.byteLength)return a;var f=[];for(d=0;d<a.v.byteLength;d++)f.push(a.v[d]);for(d=0;d<c-a.v.byteLength;d++)f.push(b);return new Sk.builtin.bytes(f)});Sk.builtin.bytes.prototype.left$strip=function(a){var c;if(void 0===a||a===Sk.builtin.none.none$)var b=[9,10,11,12,13,32,133];else if(a instanceof Sk.builtin.bytes)for(b=[],c=0;c<a.v.byteLength;c++)b.push(a.v[c]);
else throw new Sk.builtin.TypeError("a bytes-like object is required, not '"+Sk.abstr.typeName(a)+"'");a=[];for(c=0;c<this.v.byteLength;)if(b.includes(this.v[c]))c++;else break;for(b=c;b<this.v.byteLength;b++)a.push(this.v[b]);return new Sk.builtin.bytes(a)};Sk.builtin.bytes.prototype.lstrip=new Sk.builtin.func(function(a,c){Sk.builtin.pyCheckArgsLen("lstrip",arguments.length-1,0,1);return a.left$strip(c)});Sk.builtin.bytes.prototype.rjust=new Sk.builtin.func(function(a,c,b){var d;Sk.builtin.pyCheckArgsLen("rjust",
arguments.length-1,1,2);if(void 0===b)b=32;else if(b instanceof Sk.builtin.bytes&&1==b.v.byteLength)b=b.v[0];else throw new Sk.builtin.TypeError("rjust() argument 2 must be a byte string of length 1, not "+Sk.abstr.typeName(b));if(c instanceof Sk.builtin.int_)c=c.v;else throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(c)+"' object cannot be interpreted as an integer");if(c<=a.v.byteLength)return a;var f=[];for(d=0;d<c-a.v.byteLength;d++)f.push(b);for(d=0;d<a.v.byteLength;d++)f.push(a.v[d]);return new Sk.builtin.bytes(f)});
Sk.builtin.bytes.prototype.rsplit=new Sk.builtin.func(function(a,c,b){Sk.builtin.pyCheckArgsLen("rsplit",arguments.length,1,3);if(void 0===c||c===Sk.builtin.none.none$)c=null;if(null!==c&&!(c instanceof Sk.builtin.bytes))throw new Sk.builtin.TypeError("expected bytes");if(null!==c&&0==c.v.byteLength)throw new Sk.builtin.ValueError("empty separator");if(void 0!==b&&!Sk.builtin.checkInt(b))throw new Sk.builtin.TypeError("an integer is required");b=void 0===b?-1:Sk.builtin.asnum$(b);let d=[],f=0;if(c){for(var e=
a.v.byteLength;0<=e;){var g=a.find$right(c,0,e);if(-1===g)break;d.push(new Sk.builtin.bytes(a.v.subarray(g+c.v.byteLength,e)));e=g;f++;if(-1<b&&f>=b)break}d.push(new Sk.builtin.bytes(a.v.subarray(0,e)))}else{for(e=a.v.byteLength-1;-1===b||f<b;){for(;0<=e&&k(a.v[e]);)e--;if(0>e)break;g=e+1;for(e--;0<=e&&!k(a.v[e]);)e--;d.push(new Sk.builtin.bytes(a.v.subarray(e+1,g)));f++}if(0<=e){for(;0<=e&&k(a.v[e]);)e--;0<=e&&d.push(new Sk.builtin.bytes(a.v.subarray(0,e+1)))}}return new Sk.builtin.list(d.reverse())});
Sk.builtin.bytes.prototype.right$strip=function(a){var c;if(void 0===a||a===Sk.builtin.none.none$)var b=[9,10,11,12,13,32,133];else if(a instanceof Sk.builtin.bytes)for(b=[],c=0;c<a.v.byteLength;c++)b.push(a.v[c]);else throw new Sk.builtin.TypeError("a bytes-like object is required, not '"+Sk.abstr.typeName(a)+"'");a=[];for(c=this.v.byteLength-1;-1<c;)if(b.includes(this.v[c]))c--;else break;for(b=0;b<=c;b++)a.push(this.v[b]);return new Sk.builtin.bytes(a)};Sk.builtin.bytes.prototype.rstrip=new Sk.builtin.func(function(a,
c){Sk.builtin.pyCheckArgsLen("rstrip",arguments.length-1,0,1);return a.right$strip(c)});Sk.builtin.bytes.prototype.split=new Sk.builtin.func(function(a,c,b){Sk.builtin.pyCheckArgsLen("split",arguments.length,1,3);if(void 0===c||c===Sk.builtin.none.none$)c=null;if(null!==c&&!(c instanceof Sk.builtin.bytes))throw new Sk.builtin.TypeError("expected bytes");if(null!==c&&0==c.v.byteLength)throw new Sk.builtin.ValueError("empty separator");if(void 0!==b&&!Sk.builtin.checkInt(b))throw new Sk.builtin.TypeError("an integer is required");
b=void 0===b?-1:Sk.builtin.asnum$(b);let d=[],f=0,e=0;if(c){for(;e<a.v.byteLength;){var g=a.find$left(c,e);if(-1===g)break;d.push(new Sk.builtin.bytes(a.v.subarray(e,g)));e=g+c.v.byteLength;f++;if(-1<b&&f>=b)break}d.push(new Sk.builtin.bytes(a.v.subarray(e,a.v.byteLength)))}else{g=0;let c=a.v.byteLength;for(;-1===b||f<b;){for(;g<c&&k(a.v[g]);)g++;if(g==c)break;e=g;for(g++;g<c&&!k(a.v[g]);)g++;d.push(new Sk.builtin.bytes(a.v.subarray(e,g)));f++}if(g<c){for(;g<c&&k(a.v[g]);)g++;g<c&&d.push(new Sk.builtin.bytes(a.v.subarray(g,
c)))}}return new Sk.builtin.list(d)});Sk.builtin.bytes.prototype.strip=new Sk.builtin.func(function(a,c){Sk.builtin.pyCheckArgsLen("strip",arguments.length-1,0,1);return a.left$strip(c).right$strip(c)});Sk.builtin.bytes.prototype.capitalize=new Sk.builtin.func(function(a){var c;Sk.builtin.pyCheckArgsLen("capitalize",arguments.length-1,0,0);if(0===a.v.byteLength)return new Sk.builtin.bytes(0);var b=[];var d=97<=a.v[0]&&122>=a.v[0]?a.v[0]-32:a.v[0];b.push(d);for(c=1;c<a.v.byteLength;c++)d=a.v[c],65<=
d&&90>=d&&(d+=32),b.push(d);return new Sk.builtin.bytes(b)});Sk.builtin.bytes.prototype.expandtabs=new Sk.builtin.func(function(a,c){Sk.builtin.pyCheckArgsLen("expandtabs",arguments.length,1,2);if(void 0!==c&&!Sk.builtin.checkInt(c))throw new Sk.builtin.TypeError("integer argument exepected, got "+Sk.abstr.typeName(c));c=void 0===c?8:Sk.builtin.asnum$(c);let b=[],d=0;for(let f=0;f<a.v.byteLength;f++)if(9===a.v[f]){let a=c-d%c;b=b.concat(Array(a).fill(32));d+=a}else 10===a.v[f]||13===a.v[f]?(b.push(a.v[f]),
d=0):(b.push(a.v[f]),d++);return new Sk.builtin.bytes(b)});Sk.builtin.bytes.prototype.isalnum=new Sk.builtin.func(function(a){var c;Sk.builtin.pyCheckArgsLen("isalnum",arguments.length-1,0,0);if(0===a.v.byteLength)return Sk.builtin.bool.false$;for(c=0;c<a.v.byteLength;c++){var b=a.v[c];if(!(48<=b&&57>=b||65<=b&&90>=b||97<=b&&122>=b))return Sk.builtin.bool.false$}return Sk.builtin.bool.true$});Sk.builtin.bytes.prototype.isalpha=new Sk.builtin.func(function(a){var c;Sk.builtin.pyCheckArgsLen("isalpha",
arguments.length-1,0,0);if(0===a.v.byteLength)return Sk.builtin.bool.false$;for(c=0;c<a.v.byteLength;c++){var b=a.v[c];if(!(65<=b&&90>=b||97<=b&&122>=b))return Sk.builtin.bool.false$}return Sk.builtin.bool.true$});Sk.builtin.bytes.prototype.isascii=new Sk.builtin.func(function(a){var c;Sk.builtin.pyCheckArgsLen("isascii",arguments.length-1,0,0);for(c=0;c<a.v.byteLength;c++){var b=a.v[c];if(!(0<=b&&128>b))return Sk.builtin.bool.false$}return Sk.builtin.bool.true$});Sk.builtin.bytes.prototype.isdigit=
new Sk.builtin.func(function(a){var c;Sk.builtin.pyCheckArgsLen("isdigit",arguments.length-1,0,0);if(0===a.v.byteLength)return Sk.builtin.bool.false$;for(c=0;c<a.v.byteLength;c++){var b=a.v[c];if(!(48<=b&&58>b))return Sk.builtin.bool.false$}return Sk.builtin.bool.true$});Sk.builtin.bytes.prototype.islower=new Sk.builtin.func(function(a){var c,b;Sk.builtin.pyCheckArgsLen("islower",arguments.length-1,0,0);for(c=0;c<a.v.byteLength;c++){var d=a.v[c];if(65<=d&&90>=d)return Sk.builtin.bool.false$;!b&&97<=
d&&122>=d&&(b=!0)}return b?Sk.builtin.bool.true$:Sk.builtin.bool.false$});Sk.builtin.bytes.prototype.isspace=new Sk.builtin.func(function(a){var c;Sk.builtin.pyCheckArgsLen("isspace",arguments.length-1,0,0);if(0===a.v.byteLength)return Sk.builtin.bool.false$;for(c=0;c<a.v.byteLength;c++){var b=a.v[c];if(32!==b&&9!==b&&10!==b&&13!==b&&11!==b&&12!==b)return Sk.builtin.bool.false$}return Sk.builtin.bool.true$});Sk.builtin.bytes.prototype.istitle=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("istitle",
arguments.length-1,0,0);if(0===a.v.byteLength)return Sk.builtin.bool.false$;let c=!1,b=!1;for(let d=0;d<a.v.byteLength;d++){const f=a.v[d];if(65<=f&&90>=f){if(c)return Sk.builtin.bool.false$;b=c=!0}else if(97<=f&&122>=f){if(!c)return Sk.builtin.bool.false$;b=!0}else c=!1}return b?Sk.builtin.bool.true$:Sk.builtin.bool.false$});Sk.builtin.bytes.prototype.isupper=new Sk.builtin.func(function(a){var c,b;Sk.builtin.pyCheckArgsLen("isupper",arguments.length-1,0,0);for(c=0;c<a.v.byteLength;c++){var d=a.v[c];
!b&&65<=d&&90>=d&&(b=!0);if(97<=d&&122>=d)return Sk.builtin.bool.false$}return b?Sk.builtin.bool.true$:Sk.builtin.bool.false$});Sk.builtin.bytes.prototype.lower=new Sk.builtin.func(function(a){var c;Sk.builtin.pyCheckArgsLen("lower",arguments.length-1,0,0);var b=[];for(c=0;c<a.v.byteLength;c++){var d=a.v[c];65<=d&&90>=d&&(d+=32);b.push(d)}return new Sk.builtin.bytes(b)});Sk.builtin.bytes.prototype.splitlines=new Sk.builtin.func(function(a,c){Sk.builtin.pyCheckArgsLen("splitlines",arguments.length,
1,2);if(void 0!==c&&!Sk.builtin.checkBool(c))throw new Sk.builtin.TypeError("boolean argument expected, got "+Sk.abstr.typeName(c));c=void 0===c?!1:c.v;let b=[],d=0;let f=0;for(;f<a.v.byteLength;){var e=a.v[f];if(13===e){let g=!1;f<a.v.byteLength-1&&10===a.v[f+1]&&(g=!0);e=c?g?f+2:f+1:f;b.push(new Sk.builtin.bytes(a.v.subarray(d,e)));f=d=g?f+2:f+1}else 10===e?(e=c?f+1:f,b.push(new Sk.builtin.bytes(a.v.subarray(d,e))),f=d=f+1):f++}d<a.v.byteLength&&b.push(new Sk.builtin.bytes(a.v.subarray(d,a.v.byteLength)));
return new Sk.builtin.list(b)});Sk.builtin.bytes.prototype.swapcase=new Sk.builtin.func(function(a){var c;Sk.builtin.pyCheckArgsLen("swapcase",arguments.length-1,0,0);var b=[];for(c=0;c<a.v.byteLength;c++){var d=a.v[c];65<=d&&90>=d?d+=32:97<=d&&122>=d&&(d-=32);b.push(d)}return new Sk.builtin.bytes(b)});Sk.builtin.bytes.prototype.title=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("title",arguments.length-1,0,0);if(0===a.v.byteLength)return new Sk.builtin.bytes(0);let c=[],b=!1;for(let d=
0;d<a.v.byteLength;d++){const f=a.v[d];65<=f&&90>=f?b?c.push(f+32):(b=!0,c.push(f)):97<=f&&122>=f?b?c.push(f):(b=!0,c.push(f-32)):(b=!1,c.push(f))}return new Sk.builtin.bytes(c)});Sk.builtin.bytes.prototype.upper=new Sk.builtin.func(function(a){var c;Sk.builtin.pyCheckArgsLen("upper",arguments.length-1,0,0);var b=[];for(c=0;c<a.v.byteLength;c++){var d=a.v[c];97<=d&&122>=d&&(d-=32);b.push(d)}return new Sk.builtin.bytes(b)});Sk.builtin.bytes.prototype.zfill=new Sk.builtin.func(function(a,c){var b;Sk.builtin.pyCheckArgsLen("zfill",
arguments.length-1,1,1);if(!(c instanceof Sk.builtin.int_))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(c)+"' object cannot be interpreted as an integer");if(c.v<=a.v.byteLength)return a;var d=[];var f=c.v-a.v.byteLength;if(43===a.v[0]||45===a.v[0]){var e=a.v[0];d.push(e);for(b=0;b<f;b++)d.push(48);b=1}else{for(b=0;b<f;b++)d.push(48);b=0}for(;b<a.v.byteLength;b++)e=a.v[b],d.push(e);return new Sk.builtin.bytes(d)});Sk.builtin.bytes.prototype.__iter__=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("__iter__",
arguments.length,0,0,!0,!1);return new Sk.builtin.bytes_iter_(a)});Sk.builtin.bytes.prototype.tp$iter=function(){return new Sk.builtin.bytes_iter_(this)};Sk.builtin.bytes_iter_=function(a){if(!(this instanceof Sk.builtin.bytes_iter_))return new Sk.builtin.bytes_iter_(a);this.$index=0;this.sq$length=a.v.byteLength;this.tp$iter=()=>this;this.tp$iternext=function(){if(!(this.$index>=this.sq$length))return new Sk.builtin.int_(a.v[this.$index++])};this.$r=function(){return new Sk.builtin.str("bytesiterator")};
return this};Sk.abstr.setUpInheritance("bytesiterator",Sk.builtin.bytes_iter_,Sk.builtin.object);Sk.builtin.bytes_iter_.prototype.__class__=Sk.builtin.bytes_iter_;Sk.builtin.bytes_iter_.prototype.__iter__=new Sk.builtin.func(function(a){return a});Sk.builtin.bytes_iter_.prototype.next$=function(a){a=a.tp$iternext();if(void 0===a)throw new Sk.builtin.StopIteration;return a};Sk.exportSymbol("Sk.builtin.bytes",Sk.builtin.bytes)},function(m,p,a){(function(a){(function(a){function c(){}function b(){}var h=
String.fromCharCode,g={}.toString,f=g.call(a.SharedArrayBuffer),k=g(),n=a.Uint8Array,l=n||Array,q=n?ArrayBuffer:l,m=q.isView||function(a){return a&&"length"in a},z=g.call(q.prototype);q=b.prototype;var p=a.TextEncoder,t=new (n?Uint16Array:l)(32);c.prototype.decode=function(a){if(!m(a)){var c=g.call(a);if(c!==z&&c!==f&&c!==k)throw TypeError("Failed to execute 'decode' on 'TextDecoder': The provided value is not of type '(ArrayBuffer or ArrayBufferView)'");a=n?new l(a):a||[]}for(var b=c="",d=0,e=a.length|
0,q=e-32|0,u,p,y=0,H=0,S,R=0,V=-1;d<e;){for(u=d<=q?32:e-d|0;R<u;d=d+1|0,R=R+1|0){p=a[d]&255;switch(p>>4){case 15:S=a[d=d+1|0]&255;if(2!==S>>6||247<p){d=d-1|0;break}y=(p&7)<<6|S&63;H=5;p=256;case 14:S=a[d=d+1|0]&255,y<<=6,y|=(p&15)<<6|S&63,H=2===S>>6?H+4|0:24,p=p+256&768;case 13:case 12:S=a[d=d+1|0]&255,y<<=6,y|=(p&31)<<6|S&63,H=H+7|0,d<e&&2===S>>6&&y>>H&&1114112>y?(p=y,y=y-65536|0,0<=y&&(V=(y>>10)+55296|0,p=(y&1023)+56320|0,31>R?(t[R]=V,R=R+1|0,V=-1):(S=V,V=p,p=S))):(p>>=8,d=d-p-1|0,p=65533),y=H=
0,u=d<=q?32:e-d|0;default:t[R]=p;continue;case 11:case 10:case 9:case 8:}t[R]=65533}b+=h(t[0],t[1],t[2],t[3],t[4],t[5],t[6],t[7],t[8],t[9],t[10],t[11],t[12],t[13],t[14],t[15],t[16],t[17],t[18],t[19],t[20],t[21],t[22],t[23],t[24],t[25],t[26],t[27],t[28],t[29],t[30],t[31]);32>R&&(b=b.slice(0,R-32|0));if(d<e){if(t[0]=V,R=~V>>>31,V=-1,b.length<c.length)continue}else-1!==V&&(b+=h(V));c+=b;b=""}return c};q.encode=function(a){a=void 0===a?"":""+a;var c=a.length|0,b=new l((c<<1)+8|0),d,f=0,e=!n;for(d=0;d<
c;d=d+1|0,f=f+1|0){var g=a.charCodeAt(d)|0;if(127>=g)b[f]=g;else{if(2047>=g)b[f]=192|g>>6;else{a:{if(55296<=g)if(56319>=g){var h=a.charCodeAt(d=d+1|0)|0;if(56320<=h&&57343>=h){g=(g<<10)+h-56613888|0;if(65535<g){b[f]=240|g>>18;b[f=f+1|0]=128|g>>12&63;b[f=f+1|0]=128|g>>6&63;b[f=f+1|0]=128|g&63;continue}break a}g=65533}else 57343>=g&&(g=65533);!e&&d<<1<f&&d<<1<(f-7|0)&&(e=!0,h=new l(3*c),h.set(b),b=h)}b[f]=224|g>>12;b[f=f+1|0]=128|g>>6&63}b[f=f+1|0]=128|g&63}}return n?b.subarray(0,f):b.slice(0,f)};p||
(a.TextDecoder=c,a.TextEncoder=b)})("undefined"==typeof a?"undefined"==typeof self?this:self:a)}).call(this,a(0))},function(m,p){const a=/^(?:(.)?([<>=\^]))?([\+\-\s])?(#)?(0)?(\d+)?(,)?(?:\.(\d+))?([bcdeEfFgGnosxX%])?$/;Sk.formatting={};let b=function(a,c,b,d){Sk.asserts.assert("string"===typeof c);if(a[6]){var f=parseInt(a[6],10);d=a[2]||(a[5]?"=":d?">":"<");let e=f-(c.length+(b?b.length:0));if(0>=e)return c;f=(a[1]||(a[5]?"0":" ")).repeat(e);switch(d){case "=":if("s"===a[9])throw new Sk.builtin.ValueError("'=' alignment not allowed in string format specifier");
return b+f+c;case ">":return f+b+c;case "<":return b+c+f;case "^":return a=Math.floor(e/2),f.substring(0,a)+b+c+f.substring(a)}}return b+c},c=function(a,c){return c?"-":"+"===a[3]?"+":" "===a[3]?" ":""},d=function(a,d,f){Sk.asserts.assert(d instanceof Sk.builtin.int_||d instanceof Sk.builtin.lng);if(a[8])throw new Sk.builtin.ValueError("Precision not allowed in integer format");let e=d.str$(f,!1);d=d.nb$isnegative();d=c(a,d);a[4]&&(16===f?d+="0x":8===f?d+="0o":2===f&&(d+="0b"));"X"===a[9]&&(e=e.toUpperCase());
"n"===a[9]?e=(+e).toLocaleString():a[7]&&(f=e.toString().split("."),f[0]=f[0].replace(/\B(?=(\d{3})+(?!\d))/g,","),e=f.join("."));return b(a,e,d,!0)},e=function(e,g,f){if(!g)return e.str$(10,!0);g=g.match(a);if(!g)throw new Sk.builtin.ValueError("Invalid format specifier");var k=g[9];k||(k=f?"g":"d");if(-1==(f?"fFeEgG%":"bcdoxXnfFeEgG%").indexOf(k))throw new Sk.builtin.ValueError("Unknown format code '"+g[9]+"' for object of type '"+Sk.abstr.typeName(e)+"'");switch(k){case "d":case "n":return d(g,
e,10);case "x":case "X":return d(g,e,16);case "o":return d(g,e,8);case "b":return d(g,e,2);case "c":if(g[3])throw new Sk.builtin.ValueError("Sign not allowed with integer format specifier 'c'");if(g[4])throw new Sk.builtin.ValueError("Alternate form not allowed with integer format specifier 'c'");if(g[7])throw new Sk.builtin.ValueError("Cannot specify ',' with 'c'");if(g[8])throw new Sk.builtin.ValueError("Cannot specify ',' with 'c'");return b(g,String.fromCodePoint(Sk.builtin.asnum$(e)),"",!0);
case "f":case "F":case "e":case "E":case "g":case "G":{if(g[4])throw new Sk.builtin.ValueError("Alternate form (#) not allowed in float format specifier");f=Sk.builtin.asnum$(e);"string"===typeof f&&(f=Number(f));if(Infinity===f)return b(g,"inf","",!0);if(-Infinity===f)return b(g,"inf","-",!0);if(isNaN(f))return b(g,"nan","",!0);e=!1;0>f&&(f=-f,e=!0);let a=["toExponential","toFixed","toPrecision"]["efg".indexOf(k.toLowerCase())],d=g[8]?parseInt(g[8],10):6;f=f[a](d);-1!=="EFG".indexOf(k)&&(f=f.toUpperCase());
if("g"===k.toLowerCase()||!g[9]){if(k=f.match(/\.(\d*[1-9])?(0+)$/)){let [,a,c]=k;f=f.slice(0,a?-c.length:-(c.length+1))}-1!=f.indexOf(".")||g[9]||(f+=".0")}g[7]&&(k=f.toString().split("."),k[0]=k[0].replace(/\B(?=(\d{3})+(?!\d))/g,","),f=k.join("."));return b(g,f,c(g,e),!0)}case "%":if(g[4])throw new Sk.builtin.ValueError("Alternate form (#) not allowed with format specifier '%'");e=Sk.builtin.asnum$(e);"string"===typeof e&&(e=Number(e));if(Infinity===e)return b(g,"inf%","",!0);if(-Infinity===e)return b(g,
"inf%","-",!0);if(isNaN(e))return b(g,"nan%","",!0);k=!1;0>e&&(e=-e,k=!0);f=g[8]?parseInt(g[8],10):6;e=(100*e).toFixed(f)+"%";return b(g,e,c(g,k),!0);default:throw new Sk.builtin.ValueError("Unknown format code '"+g[9]+"'");}};Sk.formatting.mkNumber__format__=a=>new Sk.builtin.func(function(c,b){Sk.builtin.pyCheckArgsLen("__format__",arguments.length,2,2);if(!Sk.builtin.checkString(b))throw new Sk.builtin.TypeError("format() argument 2 must be str, not "+Sk.abstr.typeName(b));return new Sk.builtin.str(e(c,
b.$jsstr(),a))});m=function(a){var c={};Sk.builtin.pyCheckArgsLen("format",arguments.length,0,Infinity,!0,!0);var b=new Sk.builtins.tuple(Array.prototype.slice.call(arguments,1));var d=new Sk.builtins.dict(a);if(void 0===arguments[1])return b.v;var e=0;if(0!==d.size){let a,b,f;a=d.tp$iter();for(b=a.tp$iternext();void 0!==b;b=a.tp$iternext())f=d.mp$lookup(b),c[b.v]=f}for(var h in b.v)"0"!==h&&(c[h-1]=b.v[h]);b=b.v[0].v.replace(/{(((?:\d+)|(?:\w+))?((?:\.(\w+))|(?:\[((?:\d+)|(?:\w+))\])?))?(?:!([rs]))?(?::([^}]*))?}/g,
function(a,b,d,f,g,h,k,l,n,m){let q;if(void 0!==h&&""!==h)a=c[d],q=a.constructor===Array?a[h]:/^\d+$/.test(h)?Sk.abstr.objectGetItem(a,new Sk.builtin.int_(parseInt(h,10)),!1):Sk.abstr.objectGetItem(a,new Sk.builtin.str(h),!1),e++;else if(void 0!==g&&""!==g)q=Sk.abstr.gattr(c[d||e++],new Sk.builtin.str(g));else if(void 0!==d&&""!==d)q=c[d];else if(void 0===b||""===b)q=c[e],e++;else if(b instanceof Sk.builtin.int_||b instanceof Sk.builtin.float_||b instanceof Sk.builtin.lng||/^\d+$/.test(b))q=c[b],
e++;if("s"===k)q=new Sk.builtin.str(q);else if("r"===k)q=Sk.builtin.repr(q);else if(""!==k&&void 0!==k)throw new Sk.builtin.ValueError("Unknown conversion specifier "+k);return Sk.abstr.objectFormat(q,new Sk.builtin.str(l)).$jsstr()});return new Sk.builtin.str(b)};m.co_kwargs=!0;Sk.builtin.str.prototype.format=new Sk.builtin.func(m);Sk.builtin.str.prototype.__format__=new Sk.builtin.func(function(c,d){Sk.builtin.pyCheckArgsLen("__format__",arguments.length,2,2);if(!Sk.builtin.checkString(d))throw new Sk.builtin.TypeError("format() argument 2 must be str, not "+
Sk.abstr.typeName(d));let f=d.$jsstr().match(a);if(f[9]&&"s"!==f[9])throw new Sk.builtin.ValueError("Unknown format code '"+f[9]+"' for object of type 'str'");if(f[3])throw new Sk.builtin.ValueError("Sign not allowed in string format specifier");if(f[4])throw new Sk.builtin.ValueError("Alternate form (#) not allowed with string format specifier");if(f[7])throw new Sk.builtin.ValueError("Cannot specify ',' with 's'");let e=c.v;f[8]&&(e=e.substring(0,f[8]));return new Sk.builtin.str(b(f,e,"",!1))})},
function(m,p){Sk.builtin.tuple=function(a,b){if(!(this instanceof Sk.builtin.tuple))return Sk.builtin.pyCheckArgsLen("tuple",arguments.length,0,1),new Sk.builtin.tuple(a,!0);if(void 0===a)this.v=[];else if(Array.isArray(a))this.v=a;else return Sk.misceval.chain(Sk.misceval.arrayFromIterable(a,b),a=>{this.v=a;return this})};Sk.abstr.setUpInheritance("tuple",Sk.builtin.tuple,Sk.builtin.seqtype);Sk.builtin.tuple.prototype.__class__=Sk.builtin.tuple;Sk.builtin.tuple.prototype.sk$asarray=function(){return this.v.slice(0)};
Sk.builtin.tuple.prototype.$r=function(){var a;if(0===this.v.length)return new Sk.builtin.str("()");var b=[];for(a=0;a<this.v.length;++a)b[a]=Sk.misceval.objectRepr(this.v[a]).v;a=b.join(", ");1===this.v.length&&(a+=",");return new Sk.builtin.str("("+a+")")};Sk.builtin.tuple.prototype.mp$subscript=function(a){let b;if(Sk.misceval.isIndex(a)){b=Sk.misceval.asIndex(a);if("number"!==typeof b)throw new Sk.builtin.IndexError("cannot fit '"+Sk.abstr.typeName(a)+"' into an index-sized integer");if(void 0!==
b){0>b&&(b=this.v.length+b);if(0>b||b>=this.v.length)throw new Sk.builtin.IndexError("tuple index out of range");return this.v[b]}}else if(a instanceof Sk.builtin.slice){const c=[];a.sssiter$(this.v.length,a=>{c.push(this.v[a])});return new Sk.builtin.tuple(c)}throw new Sk.builtin.TypeError("tuple indices must be integers, not "+Sk.abstr.typeName(a));};Sk.builtin.tuple.prototype.tp$hash=function(){var a,b=1000003,c=3430008,d=this.v.length;for(a=0;a<d;++a){var e=Sk.builtin.hash(this.v[a]).v;if(-1===
e)return new Sk.builtin.int_(-1);c=(c^e)*b;b+=82520+d+d}c+=97531;-1===c&&(c=-2);return new Sk.builtin.int_(c|0)};Sk.builtin.tuple.prototype.sq$repeat=function(a){if(!Sk.misceval.isIndex(a))throw new Sk.builtin.TypeError("can't multiply sequence by non-int of type '"+Sk.abstr.typeName(a)+"'");var b=Sk.misceval.asIndex(a);if("number"!==typeof b)throw new Sk.builtin.OverflowError("cannot fit '"+Sk.abstr.typeName(a)+"' into an index-sized integer");var c=[];for(a=0;a<b;++a)c.push.apply(c,this.v);return new Sk.builtin.tuple(c)};
Sk.builtin.tuple.prototype.nb$multiply=Sk.builtin.tuple.prototype.sq$repeat;Sk.builtin.tuple.prototype.nb$inplace_multiply=Sk.builtin.tuple.prototype.sq$repeat;Sk.builtin.tuple.prototype.__iter__=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("__iter__",arguments.length,1,1);return new Sk.builtin.tuple_iter_(a)});Sk.builtin.tuple.prototype.tp$iter=function(){return new Sk.builtin.tuple_iter_(this)};Sk.builtin.tuple.prototype.tp$richcompare=function(a,b){var c;if(!a.__class__||!Sk.misceval.isTrue(Sk.builtin.isinstance(a,
Sk.builtin.tuple)))return"Eq"===b?!1:"NotEq"===b?!0:Sk.__future__.python3?Sk.builtin.NotImplemented.NotImplemented$:!1;var d=this.v;a=a.v;var e=d.length;var h=a.length;for(c=0;c<e&&c<h;++c){var g=Sk.misceval.richCompareBool(d[c],a[c],"Eq");if(!g)break}if(c>=e||c>=h)switch(b){case "Lt":return e<h;case "LtE":return e<=h;case "Eq":return e===h;case "NotEq":return e!==h;case "Gt":return e>h;case "GtE":return e>=h;default:Sk.asserts.fail()}return"Eq"===b?!1:"NotEq"===b?!0:Sk.misceval.richCompareBool(d[c],
a[c],b)};Sk.builtin.tuple.prototype.sq$concat=function(a){if(a.__class__!=Sk.builtin.tuple)throw a='can only concatenate tuple (not "'+(Sk.abstr.typeName(a)+'") to tuple'),new Sk.builtin.TypeError(a);return new Sk.builtin.tuple(this.v.concat(a.v))};Sk.builtin.tuple.prototype.sq$contains=function(a){var b,c=this.v;for(b=0;b<c.length;b++)if(Sk.misceval.richCompareBool(c[b],a,"Eq"))return!0;return!1};Sk.builtin.tuple.prototype.nb$add=Sk.builtin.tuple.prototype.sq$concat;Sk.builtin.tuple.prototype.nb$inplace_add=
Sk.builtin.tuple.prototype.sq$concat;Sk.builtin.tuple.prototype.sq$length=function(){return this.v.length};Sk.builtin.tuple.prototype.index=new Sk.builtin.func(function(a,b){var c=a.v.length,d=a.v;for(a=0;a<c;++a)if(Sk.misceval.richCompareBool(d[a],b,"Eq"))return new Sk.builtin.int_(a);throw new Sk.builtin.ValueError("tuple.index(x): x not in tuple");});Sk.builtin.tuple.prototype.count=new Sk.builtin.func(function(a,b){var c=a.v.length,d=a.v,e=0;for(a=0;a<c;++a)Sk.misceval.richCompareBool(d[a],b,
"Eq")&&(e+=1);return new Sk.builtin.int_(e)});Sk.exportSymbol("Sk.builtin.tuple",Sk.builtin.tuple);Sk.builtin.tuple_iter_=function(a){if(!(this instanceof Sk.builtin.tuple_iter_))return new Sk.builtin.tuple_iter_(a);this.$index=0;this.$obj=a.v.slice();this.sq$length=this.$obj.length;this.tp$iter=()=>this;this.tp$iternext=function(){if(!(this.$index>=this.sq$length))return this.$obj[this.$index++]};this.$r=function(){return new Sk.builtin.str("tupleiterator")};return this};Sk.abstr.setUpInheritance("tupleiterator",
Sk.builtin.tuple_iter_,Sk.builtin.object);Sk.builtin.tuple_iter_.prototype.__class__=Sk.builtin.tuple_iter_;Sk.builtin.tuple_iter_.prototype.__iter__=new Sk.builtin.func(function(a){return a});Sk.builtin.tuple_iter_.prototype.next$=function(a){a=a.tp$iternext();if(void 0===a)throw new Sk.builtin.StopIteration;return a}},function(m,p){function a(a){let c=a.$savedKeyHash_;return void 0!==c?c:a.ob$type===Sk.builtin.str?(c=a.$jsstr().replace(b,"!$&"),a.$savedKeyHash_=c):"string"===typeof a?a.replace(b,
"!$&"):Sk.builtin.hash(a).v}Sk.builtin.dict=function(a){var c,b;if(!(this instanceof Sk.builtin.dict))return new Sk.builtin.dict(a);void 0===a&&(a=[]);this.size=0;this.entries=Object.create(null);this.buckets={};if(Array.isArray(a))for(c=0;c<a.length;c+=2)this.mp$ass_subscript(a[c],a[c+1]);else if(a instanceof Sk.builtin.dict){var h=Sk.abstr.iter(a);for(b=h.tp$iternext();void 0!==b;b=h.tp$iternext())c=a.mp$subscript(b),void 0===c&&(c=null),this.mp$ass_subscript(b,c)}else if(Sk.builtin.checkIterable(a))for(h=
Sk.abstr.iter(a),c=h.tp$iternext();void 0!==c;c=h.tp$iternext())if(c.mp$subscript)this.mp$ass_subscript(c.mp$subscript(0),c.mp$subscript(1));else throw new Sk.builtin.TypeError("element "+this.size+" is not a sequence");else throw new Sk.builtin.TypeError("object is not iterable");this.__class__=Sk.builtin.dict;this.tp$call=void 0;return this};Sk.builtin.dict.tp$call=function(a,b){var c;Sk.builtin.pyCheckArgsLen("dict",a,0,1);a=new Sk.builtin.dict(a[0]);if(b)for(c=0;c<b.length;c+=2)a.mp$ass_subscript(new Sk.builtin.str(b[c]),
b[c+1]);return a};Sk.abstr.setUpInheritance("dict",Sk.builtin.dict,Sk.builtin.object);Sk.abstr.markUnhashable(Sk.builtin.dict);var b=/^[0-9!#_]/;Sk.builtin.dict.prototype.sk$asarray=function(){return Object.values(this.entries).map(a=>a.lhs)};Sk.builtin.dict.prototype.get$bucket_item=function(a,b){b=this.buckets[b];let c,d;if(void 0!==b)for(let e=0;e<b.length;e++)if(d=b[e],void 0!==d&&(c=d.lhs,c===a||Sk.misceval.richCompareBool(a,c,"Eq")))return d};Sk.builtin.dict.prototype.mp$lookup=function(c){const b=
a(c);c="string"===typeof b?this.entries[b]:this.get$bucket_item(c,b);if(void 0!==c)return c.rhs};Sk.builtin.dict.prototype.mp$subscript=function(a){Sk.builtin.pyCheckArgsLen("[]",arguments.length,1,2,!1,!1);var c=this.mp$lookup(a);if(void 0!==c)return c;throw new Sk.builtin.KeyError(a);};Sk.builtin.dict.prototype.sq$contains=function(a){return void 0!==this.mp$lookup(a)};Sk.builtin.dict.prototype.mp$ass_subscript=function(c,b){const d=a(c);let h;"string"===typeof d?(h=this.entries[d],void 0===h?(this.entries[d]=
{lhs:c,rhs:b},this.size++):h.rhs=b):(h=this.get$bucket_item(c,d),void 0===h?(this.set$bucket_item(c,b,d),this.size++):h.rhs=b)};Sk.builtin.dict.prototype.set$bucket_item=function(a,b,e){let c=this.buckets[e];a={lhs:a,rhs:b};void 0===c?(this.buckets[e]=[a],e="#"+e+"_0"):(b=c.indexOf(void 0),-1!==b?(e="#"+e+"_"+b,c[b]=a):(e="#"+e+"_"+c.length,c.push(a)));this.entries[e]=a};Sk.builtin.dict.prototype.pop$bucket_item=function(a,b){const c=this.buckets[b];let d,g;if(void 0!==c)for(let f=0;f<c.length;f++)if(g=
c[f],void 0!==g&&(d=g.lhs,d===a||Sk.misceval.richCompareBool(a,d,"Eq")))return delete this.entries["#"+b+"_"+f],c[f]=void 0,c.every(a=>void 0===a)&&delete this.buckets[b],g};Sk.builtin.dict.prototype.mp$del_subscript=function(c){Sk.builtin.pyCheckArgsLen("del",arguments.length,1,1,!1,!1);const b=a(c);let e;"string"===typeof b?(e=this.entries[b],delete this.entries[b]):e=this.pop$bucket_item(c,b);if(void 0!==e)this.size--;else throw new Sk.builtin.KeyError(c);};Sk.builtin.dict.prototype.$r=function(){var a,
b=[];var e=Sk.abstr.iter(this);for(a=e.tp$iternext();void 0!==a;a=e.tp$iternext()){var h=this.mp$subscript(a);void 0===h&&(h=null);h===this?b.push(Sk.misceval.objectRepr(a).v+": {...}"):b.push(Sk.misceval.objectRepr(a).v+": "+Sk.misceval.objectRepr(h).v)}return new Sk.builtin.str("{"+b.join(", ")+"}")};Sk.builtin.dict.prototype.mp$length=function(){return this.size};Sk.builtin.dict.prototype.get=new Sk.builtin.func(function(a,b,e){Sk.builtin.pyCheckArgsLen("get()",arguments.length,1,2,!1,!0);void 0===
e&&(e=Sk.builtin.none.none$);var c=a.mp$lookup(b);void 0===c&&(c=e);return c});Sk.builtin.dict.prototype.pop=new Sk.builtin.func(function(c,b,e){Sk.builtin.pyCheckArgsLen("pop()",arguments.length,1,2,!1,!0);const d=a(b);let g,f;"string"===typeof d?(g=c.entries[d],void 0!==g&&(f=g.rhs,delete c.entries[d])):(g=c.pop$bucket_item(b,d),void 0!==g&&(f=g.rhs));if(void 0!==f)return c.size--,f;if(void 0!==e)return e;throw new Sk.builtin.KeyError(b);});Sk.builtin.dict.prototype.haskey$=function(a,b){Sk.builtin.pyCheckArgsLen("has_key()",
arguments.length,1,1,!1,!0);return new Sk.builtin.bool(a.sq$contains(b))};Sk.builtin.dictview=function(a,b){this.dict=b;this.type=a;return this};Sk.abstr.setUpInheritance("dictview",Sk.builtin.dictview,Sk.builtin.object);Sk.builtin.dictview.prototype.__class__=Sk.builtin.dictview;Sk.builtin.dictview.prototype.$r=function(){var a="dict_"+this.type+"([",b,e=!0;var h=Sk.abstr.iter(this.dict);for(b=h.tp$iternext();void 0!==b;b=h.tp$iternext())if(e=!1,"keys"===this.type)a+=Sk.misceval.objectRepr(b).v+
", ";else{var g=this.dict.mp$subscript(b);void 0===g&&(g=null);"values"===this.type?a+=Sk.misceval.objectRepr(g).v+", ":"items"===this.type&&(a+="("+Sk.misceval.objectRepr(b).v+", "+Sk.misceval.objectRepr(g).v+"), ")}e||(a=a.slice(0,-2));return new Sk.builtin.str(a+"])")};Sk.builtin.dictview.prototype.sq$length=function(){return this.dict.mp$length()};Sk.builtin.dictview.prototype.sq$contains=function(a){var c;if("keys"===this.type)return this.dict.sq$contains(a);if("values"===this.type){var b=Sk.abstr.iter(this.dict);
for(c=b.tp$iternext();void 0!==c;c=b.tp$iternext()){var h=this.dict.mp$subscript(c);void 0===h&&(h=null);if(Sk.misceval.isTrue(Sk.misceval.richCompareBool(h,a,"Eq")))return!0}return!1}if("items"===this.type)return a.mp$subscript&&a.sq$length&&2===a.sq$length()&&(c=a.mp$subscript(new Sk.builtin.int_(0)),h=this.dict.mp$lookup(c),void 0!==h&&(b=new Sk.builtin.tuple([c,h]),Sk.misceval.isTrue(Sk.misceval.richCompareBool(b,a,"Eq"))))?!0:!1};Sk.builtin.dictview.prototype.tp$iter=function(){var a;"keys"===
this.type?a=Sk.builtin.dict.prototype.py2$keys(this.dict).tp$iter():"values"===this.type?a=this.dict.py2$values(this.dict).tp$iter():"items"===this.type&&(a=this.dict.py2$items(this.dict).tp$iter());a.$r=function(){return new Sk.builtin.str("<dict_"+this.type+"iterator>")};return a};Sk.builtin.dictview.prototype.__iter__=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("__iter__",arguments.length,0,0,!1,!0);return a.tp$iter()});Sk.builtin.dict.prototype.py2$items=function(a){Sk.builtin.pyCheckArgsLen("items",
arguments.length,0,0,!1,!0);var c,b=[];var h=Sk.abstr.iter(a);for(c=h.tp$iternext();void 0!==c;c=h.tp$iternext()){var g=a.mp$subscript(c);void 0===g&&(g=null);b.push(new Sk.builtin.tuple([c,g]))}return new Sk.builtin.list(b)};Sk.builtin.dict.prototype.py3$items=function(a){Sk.builtin.pyCheckArgsLen("items",arguments.length,0,0,!1,!0);return new Sk.builtin.dictview("items",a)};Sk.builtin.dict.prototype.items=new Sk.builtin.func(Sk.builtin.dict.prototype.py2$items);Sk.builtin.dict.prototype.py2$keys=
function(a){Sk.builtin.pyCheckArgsLen("keys",arguments.length,0,0,!1,!0);var c,b=[];var h=Sk.abstr.iter(a);for(c=h.tp$iternext();void 0!==c;c=h.tp$iternext())b.push(c);return new Sk.builtin.list(b)};Sk.builtin.dict.prototype.py3$keys=function(a){Sk.builtin.pyCheckArgsLen("keys",arguments.length,0,0,!1,!0);return new Sk.builtin.dictview("keys",a)};Sk.builtin.dict.prototype.keys=new Sk.builtin.func(Sk.builtin.dict.prototype.py2$keys);Sk.builtin.dict.prototype.py2$values=function(a){Sk.builtin.pyCheckArgsLen("values",
arguments.length,0,0,!1,!0);var c,b=[];var h=Sk.abstr.iter(a);for(c=h.tp$iternext();void 0!==c;c=h.tp$iternext())c=a.mp$subscript(c),void 0===c&&(c=null),b.push(c);return new Sk.builtin.list(b)};Sk.builtin.dict.prototype.py3$values=function(a){Sk.builtin.pyCheckArgsLen("values",arguments.length,0,0,!1,!0);return new Sk.builtin.dictview("values",a)};Sk.builtin.dict.prototype.values=new Sk.builtin.func(Sk.builtin.dict.prototype.py2$values);Sk.builtin.dict.prototype.clear=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("clear()",
arguments.length,0,0,!1,!0);a.entries=Object.create(null);a.buckets={};a.size=0});Sk.builtin.dict.prototype.setdefault=new Sk.builtin.func(function(a,b,e){try{return a.mp$subscript(b)}catch(h){return void 0===e&&(e=Sk.builtin.none.none$),a.mp$ass_subscript(b,e),e}});Sk.builtin.dict.prototype.dict_merge=function(a){var c;if(a instanceof Sk.builtin.dict){var b=a.tp$iter();for(c=b.tp$iternext();void 0!==c;c=b.tp$iternext()){var h=a.mp$subscript(c);if(void 0===h)throw new Sk.builtin.AttributeError("cannot get item for key: "+
c.v);this.mp$ass_subscript(c,h)}}else for(b=Sk.misceval.callsimArray(a.keys,[a]),b=Sk.abstr.iter(b),c=b.tp$iternext();void 0!==c;c=b.tp$iternext()){h=a.tp$getitem(c);if(void 0===h)throw new Sk.builtin.AttributeError("cannot get item for key: "+c.v);this.mp$ass_subscript(c,h)}};m=function(a,b,e){if(void 0!==e&&("dict"===e.tp$name||e.keys))b.dict_merge(e);else if(void 0!==e&&Sk.builtin.checkIterable(e)){var c,d=0;e=Sk.abstr.iter(e);for(c=e.tp$iternext();void 0!==c;c=e.tp$iternext(),d++){if(!Sk.builtin.checkIterable(c))throw new Sk.builtin.TypeError("cannot convert dictionary update sequence element #"+
d+" to a sequence");if(2===c.sq$length()){var f=Sk.abstr.iter(c);c=f.tp$iternext();f=f.tp$iternext();b.mp$ass_subscript(c,f)}else throw new Sk.builtin.ValueError("dictionary update sequence element #"+d+" has length "+c.sq$length()+"; 2 is required");}}else if(void 0!==e)throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(e)+"' object is not iterable");a=new Sk.builtin.dict(a);b.dict_merge(a);return Sk.builtin.none.none$};m.co_kwargs=!0;Sk.builtin.dict.prototype.update=new Sk.builtin.func(m);Sk.builtin.dict.prototype.__contains__=
new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("__contains__",arguments.length,2,2);return new Sk.builtin.bool(a.sq$contains(b))});Sk.builtin.dict.prototype.__cmp__=new Sk.builtin.func(function(a,b,e){return Sk.builtin.NotImplemented.NotImplemented$});Sk.builtin.dict.prototype.__delitem__=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("__delitem__",arguments.length,1,1,!1,!0);return Sk.builtin.dict.prototype.mp$del_subscript.call(a,b)});Sk.builtin.dict.prototype.__getitem__=
new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("__getitem__",arguments.length,1,1,!1,!0);return Sk.builtin.dict.prototype.mp$subscript.call(a,b)});Sk.builtin.dict.prototype.__setitem__=new Sk.builtin.func(function(a,b,e){Sk.builtin.pyCheckArgsLen("__setitem__",arguments.length,2,2,!1,!0);return Sk.builtin.dict.prototype.mp$ass_subscript.call(a,b,e)});Sk.builtin.dict.prototype.__hash__=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("__hash__",arguments.length,0,0,!1,!0);return Sk.builtin.dict.prototype.tp$hash.call(a)});
Sk.builtin.dict.prototype.__len__=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("__len__",arguments.length,0,0,!1,!0);return Sk.builtin.dict.prototype.mp$length.call(a)});Sk.builtin.dict.prototype.__getattribute__=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("__getattribute__",arguments.length,1,1,!1,!0);if(!Sk.builtin.checkString(b))throw new Sk.builtin.TypeError("__getattribute__ requires a string");return Sk.builtin.dict.prototype.tp$getattr.call(a,b)});Sk.builtin.dict.prototype.__iter__=
new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("__iter__",arguments.length,0,0,!1,!0);return new Sk.builtin.dict_iter_(a)});Sk.builtin.dict.prototype.tp$iter=function(){return new Sk.builtin.dict_iter_(this)};Sk.builtin.dict.prototype.__repr__=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("__repr__",arguments.length,0,0,!1,!0);return Sk.builtin.dict.prototype.$r.call(a)});Sk.builtin.dict.prototype.ob$eq=function(a){var c;if(this===a)return Sk.builtin.bool.true$;if(!(a instanceof
Sk.builtin.dict))return Sk.builtin.NotImplemented.NotImplemented$;if(this.size!==a.size)return Sk.builtin.bool.false$;var b=this.tp$iter();for(c=b.tp$iternext();void 0!==c;c=b.tp$iternext()){var h=this.mp$lookup(c);c=a.mp$lookup(c);if(void 0===c||!Sk.misceval.richCompareBool(h,c,"Eq"))return Sk.builtin.bool.false$}return Sk.builtin.bool.true$};Sk.builtin.dict.prototype.ob$ne=function(a){a=this.ob$eq(a);return a===Sk.builtin.NotImplemented.NotImplemented$?a:a.v?Sk.builtin.bool.false$:Sk.builtin.bool.true$};
Sk.builtin.dict.prototype.copy=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("copy",arguments.length,0,0,!1,!0);var c,b=new Sk.builtin.dict([]);var h=Sk.abstr.iter(a);for(c=h.tp$iternext();void 0!==c;c=h.tp$iternext()){var g=a.mp$subscript(c);void 0===g&&(g=null);b.mp$ass_subscript(c,g)}return b});Sk.builtin.dict.$fromkeys=function(a,b,e){if(a instanceof Sk.builtin.dict){Sk.builtin.pyCheckArgsLen("fromkeys",arguments.length,1,2,!1,!0);var c=a;var d=b;var f=void 0===e?Sk.builtin.none.none$:
e}else Sk.builtin.pyCheckArgsLen("fromkeys",arguments.length,1,2,!1,!1),c=new Sk.builtin.dict([]),d=a,f=void 0===b?Sk.builtin.none.none$:b;if(!Sk.builtin.checkIterable(d))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(d)+"' object is not iterable");var k=Sk.abstr.iter(d);for(d=k.tp$iternext();void 0!==d;d=k.tp$iternext())c.mp$ass_subscript(d,f);return c};Sk.builtin.dict.prototype.iteritems=new Sk.builtin.func(function(a){throw new Sk.builtin.NotImplementedError("dict.iteritems is not yet implemented in Skulpt");
});Sk.builtin.dict.prototype.iterkeys=new Sk.builtin.func(function(a){throw new Sk.builtin.NotImplementedError("dict.iterkeys is not yet implemented in Skulpt");});Sk.builtin.dict.prototype.itervalues=new Sk.builtin.func(function(a){throw new Sk.builtin.NotImplementedError("dict.itervalues is not yet implemented in Skulpt");});Sk.builtin.dict.prototype.popitem=new Sk.builtin.func(function(a){throw new Sk.builtin.NotImplementedError("dict.popitem is not yet implemented in Skulpt");});Sk.builtin.dict.prototype.viewitems=
new Sk.builtin.func(function(a){throw new Sk.builtin.NotImplementedError("dict.viewitems is not yet implemented in Skulpt");});Sk.builtin.dict.prototype.viewkeys=new Sk.builtin.func(function(a){throw new Sk.builtin.NotImplementedError("dict.viewkeys is not yet implemented in Skulpt");});Sk.builtin.dict.prototype.viewvalues=new Sk.builtin.func(function(a){throw new Sk.builtin.NotImplementedError("dict.viewvalues is not yet implemented in Skulpt");});Sk.exportSymbol("Sk.builtin.dict",Sk.builtin.dict);
Sk.builtin.create_dict_iter_=function(a){const c={$index:0};c.$obj=a;c.$keys=a.sk$asarray();c.tp$iter=function(){return c};c.tp$iternext=function(){if(!(this.$index>=this.$keys.length))return this.$keys[this.$index++]};return c};Sk.builtin.dict_iter_=function(a){if(!(this instanceof Sk.builtin.dict_iter_))return new Sk.builtin.dict_iter_(a);a=Sk.builtin.create_dict_iter_(a);a.$r=function(){return new Sk.builtin.str("<dictionary-keyiterator>")};return a};Sk.abstr.setUpInheritance("dictionary-keyiterator",
Sk.builtin.dict_iter_,Sk.builtin.object);Sk.builtin.dict_iter_.prototype.__class__=Sk.builtin.dict_iter_;Sk.builtin.dict_iter_.prototype.__iter__=new Sk.builtin.func(function(a){return a});Sk.builtin.dict_iter_.prototype.next$=function(a){a=a.tp$iternext();if(void 0===a)throw new Sk.builtin.StopIteration;return a}},function(m,p){Sk.builtin.numtype=function(){throw new Sk.builtin.ExternalError("Cannot instantiate abstract Sk.builtin.numtype class");};Sk.abstr.setUpInheritance("NumericType",Sk.builtin.numtype,
Sk.builtin.object);Sk.builtin.numtype.sk$abstract=!0;Sk.builtin.numtype.prototype.__abs__=new Sk.builtin.func(function(a){if(void 0===a.nb$abs)throw new Sk.builtin.NotImplementedError("__abs__ is not yet implemented");Sk.builtin.pyCheckArgsLen("__abs__",arguments.length,0,0,!1,!0);return a.nb$abs()});Sk.builtin.numtype.prototype.__neg__=new Sk.builtin.func(function(a){if(void 0===a.nb$negative)throw new Sk.builtin.NotImplementedError("__neg__ is not yet implemented");Sk.builtin.pyCheckArgsLen("__neg__",
arguments.length,0,0,!1,!0);return a.nb$negative()});Sk.builtin.numtype.prototype.__pos__=new Sk.builtin.func(function(a){if(void 0===a.nb$positive)throw new Sk.builtin.NotImplementedError("__pos__ is not yet implemented");Sk.builtin.pyCheckArgsLen("__pos__",arguments.length,0,0,!1,!0);return a.nb$positive()});Sk.builtin.numtype.prototype.__int__=new Sk.builtin.func(function(a){if(void 0===a.nb$int_)throw new Sk.builtin.NotImplementedError("__int__ is not yet implemented");Sk.builtin.pyCheckArgsLen("__int__",
arguments.length,0,0,!1,!0);return a.nb$int_()});Sk.builtin.numtype.prototype.__long__=new Sk.builtin.func(function(a){if(void 0===a.nb$lng)throw new Sk.builtin.NotImplementedError("__long__ is not yet implemented");Sk.builtin.pyCheckArgsLen("__long__",arguments.length,0,0,!1,!0);return a.nb$lng()});Sk.builtin.numtype.prototype.__float__=new Sk.builtin.func(function(a){if(void 0===a.nb$float_)throw new Sk.builtin.NotImplementedError("__float__ is not yet implemented");Sk.builtin.pyCheckArgsLen("__float__",
arguments.length,0,0,!1,!0);return a.nb$float_()});Sk.builtin.numtype.prototype.__add__=new Sk.builtin.func(function(a,b){if(void 0===a.nb$add)throw new Sk.builtin.NotImplementedError("__add__ is not yet implemented");Sk.builtin.pyCheckArgsLen("__add__",arguments.length,1,1,!1,!0);return a.nb$add(b)});Sk.builtin.numtype.prototype.__radd__=new Sk.builtin.func(function(a,b){if(void 0===a.nb$reflected_add)throw new Sk.builtin.NotImplementedError("__radd__ is not yet implemented");Sk.builtin.pyCheckArgsLen("__radd__",
arguments.length,1,1,!1,!0);return a.nb$reflected_add(b)});Sk.builtin.numtype.prototype.__sub__=new Sk.builtin.func(function(a,b){if(void 0===a.nb$subtract)throw new Sk.builtin.NotImplementedError("__sub__ is not yet implemented");Sk.builtin.pyCheckArgsLen("__sub__",arguments.length,1,1,!1,!0);return a.nb$subtract(b)});Sk.builtin.numtype.prototype.__rsub__=new Sk.builtin.func(function(a,b){if(void 0===a.nb$reflected_subtract)throw new Sk.builtin.NotImplementedError("__rsub__ is not yet implemented");
Sk.builtin.pyCheckArgsLen("__rsub__",arguments.length,1,1,!1,!0);return a.nb$reflected_subtract(b)});Sk.builtin.numtype.prototype.__mul__=new Sk.builtin.func(function(a,b){if(void 0===a.nb$multiply)throw new Sk.builtin.NotImplementedError("__mul__ is not yet implemented");Sk.builtin.pyCheckArgsLen("__mul__",arguments.length,1,1,!1,!0);return a.nb$multiply(b)});Sk.builtin.numtype.prototype.__rmul__=new Sk.builtin.func(function(a,b){if(void 0===a.nb$reflected_multiply)throw new Sk.builtin.NotImplementedError("__rmul__ is not yet implemented");
Sk.builtin.pyCheckArgsLen("__rmul__",arguments.length,1,1,!1,!0);return a.nb$reflected_multiply(b)});Sk.builtin.numtype.prototype.__div__=new Sk.builtin.func(function(a,b){if(void 0===a.nb$divide)throw new Sk.builtin.NotImplementedError("__div__ is not yet implemented");Sk.builtin.pyCheckArgsLen("__div__",arguments.length,1,1,!1,!0);return a.nb$divide(b)});Sk.builtin.numtype.prototype.__rdiv__=new Sk.builtin.func(function(a,b){if(void 0===a.nb$reflected_divide)throw new Sk.builtin.NotImplementedError("__rdiv__ is not yet implemented");
Sk.builtin.pyCheckArgsLen("__rdiv__",arguments.length,1,1,!1,!0);return a.nb$reflected_divide(b)});Sk.builtin.numtype.prototype.__floordiv__=new Sk.builtin.func(function(a,b){if(void 0===a.nb$floor_divide)throw new Sk.builtin.NotImplementedError("__floordiv__ is not yet implemented");Sk.builtin.pyCheckArgsLen("__floordiv__",arguments.length,1,1,!1,!0);return a.nb$floor_divide(b)});Sk.builtin.numtype.prototype.__rfloordiv__=new Sk.builtin.func(function(a,b){if(void 0===a.nb$reflected_floor_divide)throw new Sk.builtin.NotImplementedError("__rfloordiv__ is not yet implemented");
Sk.builtin.pyCheckArgsLen("__rfloordiv__",arguments.length,1,1,!1,!0);return a.nb$reflected_floor_divide(b)});Sk.builtin.numtype.prototype.__mod__=new Sk.builtin.func(function(a,b){if(void 0===a.nb$remainder)throw new Sk.builtin.NotImplementedError("__mod__ is not yet implemented");Sk.builtin.pyCheckArgsLen("__mod__",arguments.length,1,1,!1,!0);return a.nb$remainder(b)});Sk.builtin.numtype.prototype.__rmod__=new Sk.builtin.func(function(a,b){if(void 0===a.nb$reflected_remainder)throw new Sk.builtin.NotImplementedError("__rmod__ is not yet implemented");
Sk.builtin.pyCheckArgsLen("__rmod__",arguments.length,1,1,!1,!0);return a.nb$reflected_remainder(b)});Sk.builtin.numtype.prototype.__divmod__=new Sk.builtin.func(function(a,b){if(void 0===a.nb$divmod)throw new Sk.builtin.NotImplementedError("__divmod__ is not yet implemented");Sk.builtin.pyCheckArgsLen("__divmod__",arguments.length,1,1,!1,!0);return a.nb$divmod(b)});Sk.builtin.numtype.prototype.__rdivmod__=new Sk.builtin.func(function(a,b){if(void 0===a.nb$reflected_divmod)throw new Sk.builtin.NotImplementedError("__rdivmod__ is not yet implemented");
Sk.builtin.pyCheckArgsLen("__rdivmod__",arguments.length,1,1,!1,!0);return a.nb$reflected_divmod(b)});Sk.builtin.numtype.prototype.__pow__=new Sk.builtin.func(function(a,b){if(void 0===a.nb$power)throw new Sk.builtin.NotImplementedError("__pow__ is not yet implemented");Sk.builtin.pyCheckArgsLen("__pow__",arguments.length,1,1,!1,!0);return a.nb$power(b)});Sk.builtin.numtype.prototype.__rpow__=new Sk.builtin.func(function(a,b){if(void 0===a.nb$reflected_power)throw new Sk.builtin.NotImplementedError("__rpow__ is not yet implemented");
Sk.builtin.pyCheckArgsLen("__rpow__",arguments.length,1,1,!1,!0);return a.nb$reflected_power(b)});Sk.builtin.numtype.prototype.__coerce__=new Sk.builtin.func(function(a,b){throw new Sk.builtin.NotImplementedError("__coerce__ is not yet implemented");});Sk.builtin.numtype.prototype.nb$add=function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$reflected_add=function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$inplace_add=
function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$subtract=function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$reflected_subtract=function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$inplace_subtract=function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$multiply=function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$reflected_multiply=
function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$inplace_multiply=function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$divide=function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$reflected_divide=function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$inplace_divide=function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$floor_divide=
function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$reflected_floor_divide=function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$inplace_floor_divide=function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$remainder=function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$reflected_remainder=function(a){return Sk.builtin.NotImplemented.NotImplemented$};
Sk.builtin.numtype.prototype.nb$inplace_remainder=function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$divmod=function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$reflected_divmod=function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$power=function(a,b){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$reflected_power=function(a,b){return Sk.builtin.NotImplemented.NotImplemented$};
Sk.builtin.numtype.prototype.nb$inplace_power=function(a){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$abs=function(){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$negative=function(){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$positive=function(){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$nonzero=function(){return Sk.builtin.NotImplemented.NotImplemented$};
Sk.builtin.numtype.prototype.nb$isnegative=function(){return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.numtype.prototype.nb$ispositive=function(){return Sk.builtin.NotImplemented.NotImplemented$}},function(m,p){Sk.builtin.biginteger=function(a,b,c){null!=a&&("number"==typeof a?this.fromNumber(a,b,c):null==b&&"string"!=typeof a?this.fromString(a,256):this.fromString(a,b))};Sk.builtin.biginteger.canary=0xdeadbeefcafe;Sk.builtin.biginteger.j_lm=15715070==(Sk.builtin.biginteger.canary&16777215);
Sk.builtin.biginteger.nbi=function(){return new Sk.builtin.biginteger(null)};Sk.builtin.biginteger.prototype.am1=function(a,b,c,d,e,h){for(var g;0<=--h;)g=b*this[a++]+c[d]+e,e=Math.floor(g/67108864),c[d++]=g&67108863;return e};Sk.builtin.biginteger.prototype.am2=function(a,b,c,d,e,h){for(var g,f,k=b&32767,n=b>>15;0<=--h;)f=this[a]&32767,g=this[a++]>>15,b=n*f+g*k,f=k*f+((b&32767)<<15)+c[d]+(e&1073741823),e=(f>>>30)+(b>>>15)+n*g+(e>>>30),c[d++]=f&1073741823;return e};Sk.builtin.biginteger.prototype.am3=
function(a,b,c,d,e,h){for(var g,f,k=b&16383,n=b>>14;0<=--h;)f=this[a]&16383,g=this[a++]>>14,b=n*f+g*k,f=k*f+((b&16383)<<14)+c[d]+e,e=(f>>28)+(b>>14)+n*g,c[d++]=f&268435455;return e};Sk.builtin.biginteger.prototype.am=Sk.builtin.biginteger.prototype.am3;Sk.builtin.biginteger.dbits=28;Sk.builtin.biginteger.prototype.DB=Sk.builtin.biginteger.dbits;Sk.builtin.biginteger.prototype.DM=(1<<Sk.builtin.biginteger.dbits)-1;Sk.builtin.biginteger.prototype.DV=1<<Sk.builtin.biginteger.dbits;Sk.builtin.biginteger.BI_FP=
52;Sk.builtin.biginteger.prototype.FV=Math.pow(2,Sk.builtin.biginteger.BI_FP);Sk.builtin.biginteger.prototype.F1=Sk.builtin.biginteger.BI_FP-Sk.builtin.biginteger.dbits;Sk.builtin.biginteger.prototype.F2=2*Sk.builtin.biginteger.dbits-Sk.builtin.biginteger.BI_FP;Sk.builtin.biginteger.BI_RM="0123456789abcdefghijklmnopqrstuvwxyz";Sk.builtin.biginteger.BI_RC=[];m=48;for(p=0;9>=p;++p)Sk.builtin.biginteger.BI_RC[m++]=p;m=97;for(p=10;36>p;++p)Sk.builtin.biginteger.BI_RC[m++]=p;m=65;for(p=10;36>p;++p)Sk.builtin.biginteger.BI_RC[m++]=
p;Sk.builtin.biginteger.int2char=function(a){return Sk.builtin.biginteger.BI_RM.charAt(a)};Sk.builtin.biginteger.intAt=function(a,b){a=Sk.builtin.biginteger.BI_RC[a.charCodeAt(b)];return null==a?-1:a};Sk.builtin.biginteger.prototype.bnpCopyTo=function(a){var b;for(b=this.t-1;0<=b;--b)a[b]=this[b];a.t=this.t;a.s=this.s};Sk.builtin.biginteger.prototype.bnpFromInt=function(a){this.t=1;this.s=0>a?-1:0;0<a?this[0]=a:-1>a?this[0]=a+this.DV:this.t=0};Sk.builtin.biginteger.nbv=function(a){var b=new Sk.builtin.biginteger(null);
b.bnpFromInt(a);return b};Sk.builtin.biginteger.prototype.bnpFromString=function(a,b){var c;if(16==b)var d=4;else if(8==b)d=3;else if(256==b)d=8;else if(2==b)d=1;else if(32==b)d=5;else if(4==b)d=2;else{this.fromRadix(a,b);return}this.s=this.t=0;var e=a.length;var h=!1;for(c=0;0<=--e;)b=8==d?a[e]&255:Sk.builtin.biginteger.intAt(a,e),0>b?"-"==a.charAt(e)&&(h=!0):(h=!1,0===c?this[this.t++]=b:c+d>this.DB?(this[this.t-1]|=(b&(1<<this.DB-c)-1)<<c,this[this.t++]=b>>this.DB-c):this[this.t-1]|=b<<c,c+=d,c>=
this.DB&&(c-=this.DB));8==d&&0!==(a[0]&128)&&(this.s=-1,0<c&&(this[this.t-1]|=(1<<this.DB-c)-1<<c));this.clamp();h&&Sk.builtin.biginteger.ZERO.subTo(this,this)};Sk.builtin.biginteger.prototype.bnpClamp=function(){for(var a=this.s&this.DM;0<this.t&&this[this.t-1]==a;)--this.t};Sk.builtin.biginteger.prototype.bnToString=function(a){var b;if(0>this.s)return"-"+this.negate().toString(a);if(16==a)var c=4;else if(8==a)c=3;else if(2==a)c=1;else if(32==a)c=5;else if(4==a)c=2;else return this.toRadix(a);var d=
(1<<c)-1;var e=!1;var h="";var g=this.t;a=this.DB-g*this.DB%c;if(0<g--)for(a<this.DB&&0<(b=this[g]>>a)&&(e=!0,h=Sk.builtin.biginteger.int2char(b));0<=g;)a<c?(b=(this[g]&(1<<a)-1)<<c-a,b|=this[--g]>>(a+=this.DB-c)):(b=this[g]>>(a-=c)&d,0>=a&&(a+=this.DB,--g)),0<b&&(e=!0),e&&(h+=Sk.builtin.biginteger.int2char(b));return e?h:"0"};Sk.builtin.biginteger.prototype.bnNegate=function(){var a=Sk.builtin.biginteger.nbi();Sk.builtin.biginteger.ZERO.subTo(this,a);return a};Sk.builtin.biginteger.prototype.bnAbs=
function(){return 0>this.s?this.negate():this};Sk.builtin.biginteger.prototype.bnCompareTo=function(a){var b=this.s-a.s;if(0!==b)return b;var c=this.t;b=c-a.t;if(0!==b)return 0>this.s?-b:b;for(;0<=--c;)if(0!==(b=this[c]-a[c]))return b;return 0};Sk.builtin.biginteger.nbits=function(a){var b=1,c;0!==(c=a>>>16)&&(a=c,b+=16);0!==(c=a>>8)&&(a=c,b+=8);0!==(c=a>>4)&&(a=c,b+=4);0!==(c=a>>2)&&(a=c,b+=2);0!==a>>1&&(b+=1);return b};Sk.builtin.biginteger.prototype.bnBitLength=function(){return 0>=this.t?0:this.DB*
(this.t-1)+Sk.builtin.biginteger.nbits(this[this.t-1]^this.s&this.DM)};Sk.builtin.biginteger.prototype.bnpDLShiftTo=function(a,b){var c;for(c=this.t-1;0<=c;--c)b[c+a]=this[c];for(c=a-1;0<=c;--c)b[c]=0;b.t=this.t+a;b.s=this.s};Sk.builtin.biginteger.prototype.bnpDRShiftTo=function(a,b){var c;for(c=a;c<this.t;++c)b[c-a]=this[c];b.t=Math.max(this.t-a,0);b.s=this.s};Sk.builtin.biginteger.prototype.bnpLShiftTo=function(a,b){var c=a%this.DB,d=this.DB-c,e=(1<<d)-1;a=Math.floor(a/this.DB);var h=this.s<<c&
this.DM,g;for(g=this.t-1;0<=g;--g)b[g+a+1]=this[g]>>d|h,h=(this[g]&e)<<c;for(g=a-1;0<=g;--g)b[g]=0;b[a]=h;b.t=this.t+a+1;b.s=this.s;b.clamp()};Sk.builtin.biginteger.prototype.bnpRShiftTo=function(a,b){b.s=this.s;var c=Math.floor(a/this.DB);if(c>=this.t)b.t=0;else{var d=a%this.DB;var e=this.DB-d;var h=(1<<d)-1;b[0]=this[c]>>d;for(a=c+1;a<this.t;++a)b[a-c-1]|=(this[a]&h)<<e,b[a-c]=this[a]>>d;0<d&&(b[this.t-c-1]|=(this.s&h)<<e);b.t=this.t-c;b.clamp()}};Sk.builtin.biginteger.prototype.bnpSubTo=function(a,
b){for(var c=0,d=0,e=Math.min(a.t,this.t);c<e;)d+=this[c]-a[c],b[c++]=d&this.DM,d>>=this.DB;if(a.t<this.t){for(d-=a.s;c<this.t;)d+=this[c],b[c++]=d&this.DM,d>>=this.DB;d+=this.s}else{for(d+=this.s;c<a.t;)d-=a[c],b[c++]=d&this.DM,d>>=this.DB;d-=a.s}b.s=0>d?-1:0;-1>d?b[c++]=this.DV+d:0<d&&(b[c++]=d);b.t=c;b.clamp()};Sk.builtin.biginteger.prototype.bnpMultiplyTo=function(a,b){var c=this.abs(),d=a.abs(),e=c.t;for(b.t=e+d.t;0<=--e;)b[e]=0;for(e=0;e<d.t;++e)b[e+c.t]=c.am(0,d[e],b,e,0,c.t);b.s=0;b.clamp();
this.s!=a.s&&Sk.builtin.biginteger.ZERO.subTo(b,b)};Sk.builtin.biginteger.prototype.bnpSquareTo=function(a){for(var b,c=this.abs(),d=a.t=2*c.t;0<=--d;)a[d]=0;for(d=0;d<c.t-1;++d)b=c.am(d,c[d],a,2*d,0,1),(a[d+c.t]+=c.am(d+1,2*c[d],a,2*d+1,b,c.t-d-1))>=c.DV&&(a[d+c.t]-=c.DV,a[d+c.t+1]=1);0<a.t&&(a[a.t-1]+=c.am(d,c[d],a,2*d,0,1));a.s=0;a.clamp()};Sk.builtin.biginteger.prototype.bnpDivRemTo=function(a,b,c){var d=a.abs();if(!(0>=d.t)){var e=this.abs();if(e.t<d.t)null!=b&&b.fromInt(0),null!=c&&this.copyTo(c);
else{null==c&&(c=Sk.builtin.biginteger.nbi());var h=Sk.builtin.biginteger.nbi();var g=this.s;var f=a.s;a=this.DB-Sk.builtin.biginteger.nbits(d[d.t-1]);0<a?(d.lShiftTo(a,h),e.lShiftTo(a,c)):(d.copyTo(h),e.copyTo(c));var k=h.t;d=h[k-1];if(0!==d){var n=d*(1<<this.F1)+(1<k?h[k-2]>>this.F2:0);e=this.FV/n;var l=(1<<this.F1)/n;var q=1<<this.F2;var m=c.t;var p=m-k;var y=null==b?Sk.builtin.biginteger.nbi():b;h.dlShiftTo(p,y);0<=c.compareTo(y)&&(c[c.t++]=1,c.subTo(y,c));Sk.builtin.biginteger.ONE.dlShiftTo(k,
y);for(y.subTo(h,h);h.t<k;)h[h.t++]=0;for(;0<=--p;)if(n=c[--m]==d?this.DM:Math.floor(c[m]*e+(c[m-1]+q)*l),(c[m]+=h.am(0,n,c,p,0,k))<n)for(h.dlShiftTo(p,y),c.subTo(y,c);c[m]<--n;)c.subTo(y,c);null!=b&&(c.drShiftTo(k,b),g!=f&&Sk.builtin.biginteger.ZERO.subTo(b,b));c.t=k;c.clamp();0<a&&c.rShiftTo(a,c);0>g&&Sk.builtin.biginteger.ZERO.subTo(c,c)}}}};Sk.builtin.biginteger.prototype.bnMod=function(a){var b=Sk.builtin.biginteger.nbi();this.abs().divRemTo(a,null,b);0>this.s&&0<b.compareTo(Sk.builtin.biginteger.ZERO)&&
a.subTo(b,b);return b};Sk.builtin.biginteger.Classic=function(a){this.m=a};Sk.builtin.biginteger.prototype.cConvert=function(a){return 0>a.s||0<=a.compareTo(this.m)?a.mod(this.m):a};Sk.builtin.biginteger.prototype.cRevert=function(a){return a};Sk.builtin.biginteger.prototype.cReduce=function(a){a.divRemTo(this.m,null,a)};Sk.builtin.biginteger.prototype.cMulTo=function(a,b,c){a.multiplyTo(b,c);this.reduce(c)};Sk.builtin.biginteger.prototype.cSqrTo=function(a,b){a.squareTo(b);this.reduce(b)};Sk.builtin.biginteger.Classic.prototype.convert=
Sk.builtin.biginteger.prototype.cConvert;Sk.builtin.biginteger.Classic.prototype.revert=Sk.builtin.biginteger.prototype.cRevert;Sk.builtin.biginteger.Classic.prototype.reduce=Sk.builtin.biginteger.prototype.cReduce;Sk.builtin.biginteger.Classic.prototype.mulTo=Sk.builtin.biginteger.prototype.cMulTo;Sk.builtin.biginteger.Classic.prototype.sqrTo=Sk.builtin.biginteger.prototype.cSqrTo;Sk.builtin.biginteger.prototype.bnpInvDigit=function(){if(1>this.t)return 0;var a=this[0];if(0===(a&1))return 0;var b=
a&3;b=b*(2-(a&15)*b)&15;b=b*(2-(a&255)*b)&255;b=b*(2-((a&65535)*b&65535))&65535;b=b*(2-a*b%this.DV)%this.DV;return 0<b?this.DV-b:-b};Sk.builtin.biginteger.Montgomery=function(a){this.m=a;this.mp=a.invDigit();this.mpl=this.mp&32767;this.mph=this.mp>>15;this.um=(1<<a.DB-15)-1;this.mt2=2*a.t};Sk.builtin.biginteger.prototype.montConvert=function(a){var b=Sk.builtin.biginteger.nbi();a.abs().dlShiftTo(this.m.t,b);b.divRemTo(this.m,null,b);0>a.s&&0<b.compareTo(Sk.builtin.biginteger.ZERO)&&this.m.subTo(b,
b);return b};Sk.builtin.biginteger.prototype.montRevert=function(a){var b=Sk.builtin.biginteger.nbi();a.copyTo(b);this.reduce(b);return b};Sk.builtin.biginteger.prototype.montReduce=function(a){for(var b,c,d;a.t<=this.mt2;)a[a.t++]=0;for(d=0;d<this.m.t;++d)for(c=a[d]&32767,b=c*this.mpl+((c*this.mph+(a[d]>>15)*this.mpl&this.um)<<15)&a.DM,c=d+this.m.t,a[c]+=this.m.am(0,b,a,d,0,this.m.t);a[c]>=a.DV;)a[c]-=a.DV,a[++c]++;a.clamp();a.drShiftTo(this.m.t,a);0<=a.compareTo(this.m)&&a.subTo(this.m,a)};Sk.builtin.biginteger.prototype.montSqrTo=
function(a,b){a.squareTo(b);this.reduce(b)};Sk.builtin.biginteger.prototype.montMulTo=function(a,b,c){a.multiplyTo(b,c);this.reduce(c)};Sk.builtin.biginteger.Montgomery.prototype.convert=Sk.builtin.biginteger.prototype.montConvert;Sk.builtin.biginteger.Montgomery.prototype.revert=Sk.builtin.biginteger.prototype.montRevert;Sk.builtin.biginteger.Montgomery.prototype.reduce=Sk.builtin.biginteger.prototype.montReduce;Sk.builtin.biginteger.Montgomery.prototype.mulTo=Sk.builtin.biginteger.prototype.montMulTo;
Sk.builtin.biginteger.Montgomery.prototype.sqrTo=Sk.builtin.biginteger.prototype.montSqrTo;Sk.builtin.biginteger.prototype.bnpIsEven=function(){return 0===(0<this.t?this[0]&1:this.s)};Sk.builtin.biginteger.prototype.bnpExp=function(a,b){if(4294967295<a||1>a)return Sk.builtin.biginteger.ONE;var c=Sk.builtin.biginteger.nbi();var d=Sk.builtin.biginteger.nbi();var e=b.convert(this);var h=Sk.builtin.biginteger.nbits(a)-1;for(e.copyTo(c);0<=--h;)if(b.sqrTo(c,d),0<(a&1<<h))b.mulTo(d,e,c);else{var g=c;c=
d;d=g}return b.revert(c)};Sk.builtin.biginteger.prototype.bnModPowInt=function(a,b){b=256>a||b.isEven()?new Sk.builtin.biginteger.Classic(b):new Sk.builtin.biginteger.Montgomery(b);return this.exp(a,b)};Sk.builtin.biginteger.prototype.copyTo=Sk.builtin.biginteger.prototype.bnpCopyTo;Sk.builtin.biginteger.prototype.fromInt=Sk.builtin.biginteger.prototype.bnpFromInt;Sk.builtin.biginteger.prototype.fromString=Sk.builtin.biginteger.prototype.bnpFromString;Sk.builtin.biginteger.prototype.clamp=Sk.builtin.biginteger.prototype.bnpClamp;
Sk.builtin.biginteger.prototype.dlShiftTo=Sk.builtin.biginteger.prototype.bnpDLShiftTo;Sk.builtin.biginteger.prototype.drShiftTo=Sk.builtin.biginteger.prototype.bnpDRShiftTo;Sk.builtin.biginteger.prototype.lShiftTo=Sk.builtin.biginteger.prototype.bnpLShiftTo;Sk.builtin.biginteger.prototype.rShiftTo=Sk.builtin.biginteger.prototype.bnpRShiftTo;Sk.builtin.biginteger.prototype.subTo=Sk.builtin.biginteger.prototype.bnpSubTo;Sk.builtin.biginteger.prototype.multiplyTo=Sk.builtin.biginteger.prototype.bnpMultiplyTo;
Sk.builtin.biginteger.prototype.squareTo=Sk.builtin.biginteger.prototype.bnpSquareTo;Sk.builtin.biginteger.prototype.divRemTo=Sk.builtin.biginteger.prototype.bnpDivRemTo;Sk.builtin.biginteger.prototype.invDigit=Sk.builtin.biginteger.prototype.bnpInvDigit;Sk.builtin.biginteger.prototype.isEven=Sk.builtin.biginteger.prototype.bnpIsEven;Sk.builtin.biginteger.prototype.exp=Sk.builtin.biginteger.prototype.bnpExp;Sk.builtin.biginteger.prototype.toString=Sk.builtin.biginteger.prototype.bnToString;Sk.builtin.biginteger.prototype.negate=
Sk.builtin.biginteger.prototype.bnNegate;Sk.builtin.biginteger.prototype.abs=Sk.builtin.biginteger.prototype.bnAbs;Sk.builtin.biginteger.prototype.compareTo=Sk.builtin.biginteger.prototype.bnCompareTo;Sk.builtin.biginteger.prototype.bitLength=Sk.builtin.biginteger.prototype.bnBitLength;Sk.builtin.biginteger.prototype.mod=Sk.builtin.biginteger.prototype.bnMod;Sk.builtin.biginteger.prototype.modPowInt=Sk.builtin.biginteger.prototype.bnModPowInt;Sk.builtin.biginteger.ZERO=Sk.builtin.biginteger.nbv(0);
Sk.builtin.biginteger.ONE=Sk.builtin.biginteger.nbv(1);Sk.builtin.biginteger.prototype.bnClone=function(){var a=Sk.builtin.biginteger.nbi();this.copyTo(a);return a};Sk.builtin.biginteger.prototype.bnIntValue=function(){if(0>this.s){if(1==this.t)return this[0]-this.DV;if(0===this.t)return-1}else{if(1==this.t)return this[0];if(0===this.t)return 0}return(this[1]&(1<<32-this.DB)-1)<<this.DB|this[0]};Sk.builtin.biginteger.prototype.bnByteValue=function(){return 0===this.t?this.s:this[0]<<24>>24};Sk.builtin.biginteger.prototype.bnShortValue=
function(){return 0===this.t?this.s:this[0]<<16>>16};Sk.builtin.biginteger.prototype.bnpChunkSize=function(a){return Math.floor(Math.LN2*this.DB/Math.log(a))};Sk.builtin.biginteger.prototype.bnSigNum=function(){return 0>this.s?-1:0>=this.t||1==this.t&&0>=this[0]?0:1};Sk.builtin.biginteger.prototype.bnpToRadix=function(a){null==a&&(a=10);if(0===this.signum()||2>a||36<a)return"0";var b=this.chunkSize(a);var c=Math.pow(a,b);b=Sk.builtin.biginteger.nbv(c);var d=Sk.builtin.biginteger.nbi();var e=Sk.builtin.biginteger.nbi();
var h="";for(this.divRemTo(b,d,e);0<d.signum();)h=(c+e.intValue()).toString(a).substr(1)+h,d.divRemTo(b,d,e);return e.intValue().toString(a)+h};Sk.builtin.biginteger.prototype.bnpFromRadix=function(a,b){var c,d,e;this.fromInt(0);null==b&&(b=10);var h=this.chunkSize(b);var g=Math.pow(b,h);var f=!1;for(c=e=d=0;c<a.length;++c){var k=Sk.builtin.biginteger.intAt(a,c);if(0>k){if("-"==a.charAt(c)&&0===this.signum()&&(f=!0),"."==a.charAt(c))break}else e=b*e+k,++d>=h&&(this.dMultiply(g),this.dAddOffset(e,
0),e=d=0)}0<d&&(this.dMultiply(Math.pow(b,d)),this.dAddOffset(e,0));f&&Sk.builtin.biginteger.ZERO.subTo(this,this)};Sk.builtin.biginteger.prototype.bnpFromNumber=function(a,b,c){if("number"==typeof b)if(2>a)this.fromInt(1);else for(this.fromNumber(a,c),this.testBit(a-1)||this.bitwiseTo(Sk.builtin.biginteger.ONE.shiftLeft(a-1),Sk.builtin.biginteger.op_or,this),this.isEven()&&this.dAddOffset(1,0);!this.isProbablePrime(b);)this.dAddOffset(2,0),this.bitLength()>a&&this.subTo(Sk.builtin.biginteger.ONE.shiftLeft(a-
1),this);this.fromString(a+"")};Sk.builtin.biginteger.prototype.bnToByteArray=function(){var a,b=this.t,c=[];c[0]=this.s;var d=this.DB-b*this.DB%8;var e=0;if(0<b--)for(d<this.DB&&(a=this[b]>>d)!=(this.s&this.DM)>>d&&(c[e++]=a|this.s<<this.DB-d);0<=b;)if(8>d?(a=(this[b]&(1<<d)-1)<<8-d,a|=this[--b]>>(d+=this.DB-8)):(a=this[b]>>(d-=8)&255,0>=d&&(d+=this.DB,--b)),0!==(a&128)&&(a|=-256),0===e&&(this.s&128)!=(a&128)&&++e,0<e||a!=this.s)c[e++]=a;return c};Sk.builtin.biginteger.prototype.bnEquals=function(a){return 0===
this.compareTo(a)};Sk.builtin.biginteger.prototype.bnMin=function(a){return 0>this.compareTo(a)?this:a};Sk.builtin.biginteger.prototype.bnMax=function(a){return 0<this.compareTo(a)?this:a};Sk.builtin.biginteger.prototype.bnpBitwiseTo=function(a,b,c){var d,e=Math.min(a.t,this.t);for(d=0;d<e;++d)c[d]=b(this[d],a[d]);if(a.t<this.t){var h=a.s&this.DM;for(d=e;d<this.t;++d)c[d]=b(this[d],h);c.t=this.t}else{h=this.s&this.DM;for(d=e;d<a.t;++d)c[d]=b(h,a[d]);c.t=a.t}c.s=b(this.s,a.s);c.clamp()};Sk.builtin.biginteger.op_and=
function(a,b){return a&b};Sk.builtin.biginteger.prototype.bnAnd=function(a){var b=Sk.builtin.biginteger.nbi();this.bitwiseTo(a,Sk.builtin.biginteger.op_and,b);return b};Sk.builtin.biginteger.op_or=function(a,b){return a|b};Sk.builtin.biginteger.prototype.bnOr=function(a){var b=Sk.builtin.biginteger.nbi();this.bitwiseTo(a,Sk.builtin.biginteger.op_or,b);return b};Sk.builtin.biginteger.op_xor=function(a,b){return a^b};Sk.builtin.biginteger.prototype.bnXor=function(a){var b=Sk.builtin.biginteger.nbi();
this.bitwiseTo(a,Sk.builtin.biginteger.op_xor,b);return b};Sk.builtin.biginteger.op_andnot=function(a,b){return a&~b};Sk.builtin.biginteger.prototype.bnAndNot=function(a){var b=Sk.builtin.biginteger.nbi();this.bitwiseTo(a,Sk.builtin.biginteger.op_andnot,b);return b};Sk.builtin.biginteger.prototype.bnNot=function(){var a,b=Sk.builtin.biginteger.nbi();for(a=0;a<this.t;++a)b[a]=this.DM&~this[a];b.t=this.t;b.s=~this.s;return b};Sk.builtin.biginteger.prototype.bnShiftLeft=function(a){var b=Sk.builtin.biginteger.nbi();
0>a?this.rShiftTo(-a,b):this.lShiftTo(a,b);return b};Sk.builtin.biginteger.prototype.bnShiftRight=function(a){var b=Sk.builtin.biginteger.nbi();0>a?this.lShiftTo(-a,b):this.rShiftTo(a,b);return b};Sk.builtin.biginteger.lbit=function(a){if(0===a)return-1;var b=0;0===(a&65535)&&(a>>=16,b+=16);0===(a&255)&&(a>>=8,b+=8);0===(a&15)&&(a>>=4,b+=4);0===(a&3)&&(a>>=2,b+=2);0===(a&1)&&++b;return b};Sk.builtin.biginteger.prototype.bnGetLowestSetBit=function(){var a;for(a=0;a<this.t;++a)if(0!==this[a])return a*
this.DB+Sk.builtin.biginteger.lbit(this[a]);return 0>this.s?this.t*this.DB:-1};Sk.builtin.biginteger.cbit=function(a){for(var b=0;0!==a;)a&=a-1,++b;return b};Sk.builtin.biginteger.prototype.bnBitCount=function(){var a,b=0,c=this.s&this.DM;for(a=0;a<this.t;++a)b+=Sk.builtin.biginteger.cbit(this[a]^c);return b};Sk.builtin.biginteger.prototype.bnTestBit=function(a){var b=Math.floor(a/this.DB);return b>=this.t?0!==this.s:0!==(this[b]&1<<a%this.DB)};Sk.builtin.biginteger.prototype.bnpChangeBit=function(a,
b){a=Sk.builtin.biginteger.ONE.shiftLeft(a);this.bitwiseTo(a,b,a);return a};Sk.builtin.biginteger.prototype.bnSetBit=function(a){return this.changeBit(a,Sk.builtin.biginteger.op_or)};Sk.builtin.biginteger.prototype.bnClearBit=function(a){return this.changeBit(a,Sk.builtin.biginteger.op_andnot)};Sk.builtin.biginteger.prototype.bnFlipBit=function(a){return this.changeBit(a,Sk.builtin.biginteger.op_xor)};Sk.builtin.biginteger.prototype.bnpAddTo=function(a,b){for(var c=0,d=0,e=Math.min(a.t,this.t);c<
e;)d+=this[c]+a[c],b[c++]=d&this.DM,d>>=this.DB;if(a.t<this.t){for(d+=a.s;c<this.t;)d+=this[c],b[c++]=d&this.DM,d>>=this.DB;d+=this.s}else{for(d+=this.s;c<a.t;)d+=a[c],b[c++]=d&this.DM,d>>=this.DB;d+=a.s}b.s=0>d?-1:0;0<d?b[c++]=d:-1>d&&(b[c++]=this.DV+d);b.t=c;b.clamp()};Sk.builtin.biginteger.prototype.bnAdd=function(a){var b=Sk.builtin.biginteger.nbi();this.addTo(a,b);return b};Sk.builtin.biginteger.prototype.bnSubtract=function(a){var b=Sk.builtin.biginteger.nbi();this.subTo(a,b);return b};Sk.builtin.biginteger.prototype.bnMultiply=
function(a){var b=Sk.builtin.biginteger.nbi();this.multiplyTo(a,b);return b};Sk.builtin.biginteger.prototype.bnDivide=function(a){var b=Sk.builtin.biginteger.nbi();this.divRemTo(a,b,null);return b};Sk.builtin.biginteger.prototype.bnRemainder=function(a){var b=Sk.builtin.biginteger.nbi();this.divRemTo(a,null,b);return b};Sk.builtin.biginteger.prototype.bnDivideAndRemainder=function(a){var b=Sk.builtin.biginteger.nbi(),c=Sk.builtin.biginteger.nbi();this.divRemTo(a,b,c);return[b,c]};Sk.builtin.biginteger.prototype.bnpDMultiply=
function(a){this[this.t]=this.am(0,a-1,this,0,0,this.t);++this.t;this.clamp()};Sk.builtin.biginteger.prototype.bnpDAddOffset=function(a,b){if(0!==a){for(;this.t<=b;)this[this.t++]=0;for(this[b]+=a;this[b]>=this.DV;)this[b]-=this.DV,++b>=this.t&&(this[this.t++]=0),++this[b]}};Sk.builtin.biginteger.NullExp=function(){};Sk.builtin.biginteger.prototype.nNop=function(a){return a};Sk.builtin.biginteger.prototype.nMulTo=function(a,b,c){a.multiplyTo(b,c)};Sk.builtin.biginteger.prototype.nSqrTo=function(a,
b){a.squareTo(b)};Sk.builtin.biginteger.NullExp.prototype.convert=Sk.builtin.biginteger.prototype.nNop;Sk.builtin.biginteger.NullExp.prototype.revert=Sk.builtin.biginteger.prototype.nNop;Sk.builtin.biginteger.NullExp.prototype.mulTo=Sk.builtin.biginteger.prototype.nMulTo;Sk.builtin.biginteger.NullExp.prototype.sqrTo=Sk.builtin.biginteger.prototype.nSqrTo;Sk.builtin.biginteger.prototype.bnPow=function(a){return this.exp(a,new Sk.builtin.biginteger.NullExp)};Sk.builtin.biginteger.prototype.bnpMultiplyLowerTo=
function(a,b,c){var d,e=Math.min(this.t+a.t,b);c.s=0;for(c.t=e;0<e;)c[--e]=0;for(d=c.t-this.t;e<d;++e)c[e+this.t]=this.am(0,a[e],c,e,0,this.t);for(d=Math.min(a.t,b);e<d;++e)this.am(0,a[e],c,e,0,b-e);c.clamp()};Sk.builtin.biginteger.prototype.bnpMultiplyUpperTo=function(a,b,c){--b;var d=c.t=this.t+a.t-b;for(c.s=0;0<=--d;)c[d]=0;for(d=Math.max(b-this.t,0);d<a.t;++d)c[this.t+d-b]=this.am(b-d,a[d],c,0,0,this.t+d-b);c.clamp();c.drShiftTo(1,c)};Sk.builtin.biginteger.Barrett=function(a){this.r2=Sk.builtin.biginteger.nbi();
this.q3=Sk.builtin.biginteger.nbi();Sk.builtin.biginteger.ONE.dlShiftTo(2*a.t,this.r2);this.mu=this.r2.divide(a);this.m=a};Sk.builtin.biginteger.prototype.barrettConvert=function(a){if(0>a.s||a.t>2*this.m.t)return a.mod(this.m);if(0>a.compareTo(this.m))return a;var b=Sk.builtin.biginteger.nbi();a.copyTo(b);this.reduce(b);return b};Sk.builtin.biginteger.prototype.barrettRevert=function(a){return a};Sk.builtin.biginteger.prototype.barrettReduce=function(a){a.drShiftTo(this.m.t-1,this.r2);a.t>this.m.t+
1&&(a.t=this.m.t+1,a.clamp());this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);for(this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);0>a.compareTo(this.r2);)a.dAddOffset(1,this.m.t+1);for(a.subTo(this.r2,a);0<=a.compareTo(this.m);)a.subTo(this.m,a)};Sk.builtin.biginteger.prototype.barrettSqrTo=function(a,b){a.squareTo(b);this.reduce(b)};Sk.builtin.biginteger.prototype.barrettMulTo=function(a,b,c){a.multiplyTo(b,c);this.reduce(c)};Sk.builtin.biginteger.Barrett.prototype.convert=Sk.builtin.biginteger.prototype.barrettConvert;
Sk.builtin.biginteger.Barrett.prototype.revert=Sk.builtin.biginteger.prototype.barrettRevert;Sk.builtin.biginteger.Barrett.prototype.reduce=Sk.builtin.biginteger.prototype.barrettReduce;Sk.builtin.biginteger.Barrett.prototype.mulTo=Sk.builtin.biginteger.prototype.barrettMulTo;Sk.builtin.biginteger.Barrett.prototype.sqrTo=Sk.builtin.biginteger.prototype.barrettSqrTo;Sk.builtin.biginteger.prototype.bnModPow=function(a,b){var c=a.bitLength();var d=Sk.builtin.biginteger.nbv(1);if(0>=c)return d;var e=
18>c?1:48>c?3:144>c?4:768>c?5:6;var h=8>c?new Sk.builtin.biginteger.Classic(b):b.isEven()?new Sk.builtin.biginteger.Barrett(b):new Sk.builtin.biginteger.Montgomery(b);b=[];var g=3;var f=e-1;var k=(1<<e)-1;b[1]=h.convert(this);if(1<e)for(c=Sk.builtin.biginteger.nbi(),h.sqrTo(b[1],c);g<=k;)b[g]=Sk.builtin.biginteger.nbi(),h.mulTo(c,b[g-2],b[g]),g+=2;var n=a.t-1;var l=!0;var q=Sk.builtin.biginteger.nbi();for(c=Sk.builtin.biginteger.nbits(a[n])-1;0<=n;){if(c>=f)var m=a[n]>>c-f&k;else m=(a[n]&(1<<c+1)-
1)<<f-c,0<n&&(m|=a[n-1]>>this.DB+c-f);for(g=e;0===(m&1);)m>>=1,--g;0>(c-=g)&&(c+=this.DB,--n);if(l)b[m].copyTo(d),l=!1;else{for(;1<g;)h.sqrTo(d,q),h.sqrTo(q,d),g-=2;0<g?h.sqrTo(d,q):(g=d,d=q,q=g);h.mulTo(q,b[m],d)}for(;0<=n&&0===(a[n]&1<<c);)h.sqrTo(d,q),g=d,d=q,q=g,0>--c&&(c=this.DB-1,--n)}return h.revert(d)};Sk.builtin.biginteger.prototype.bnGCD=function(a){var b=0>this.s?this.negate():this.clone();a=0>a.s?a.negate():a.clone();if(0>b.compareTo(a)){var c=b;b=a;a=c}c=b.getLowestSetBit();var d=a.getLowestSetBit();
if(0>d)return b;c<d&&(d=c);0<d&&(b.rShiftTo(d,b),a.rShiftTo(d,a));for(;0<b.signum();)0<(c=b.getLowestSetBit())&&b.rShiftTo(c,b),0<(c=a.getLowestSetBit())&&a.rShiftTo(c,a),0<=b.compareTo(a)?(b.subTo(a,b),b.rShiftTo(1,b)):(a.subTo(b,a),a.rShiftTo(1,a));0<d&&a.lShiftTo(d,a);return a};Sk.builtin.biginteger.prototype.bnpModInt=function(a){var b;if(0>=a)return 0;var c=this.DV%a;var d=0>this.s?a-1:0;if(0<this.t)if(0===c)d=this[0]%a;else for(b=this.t-1;0<=b;--b)d=(c*d+this[b])%a;return d};Sk.builtin.biginteger.prototype.bnModInverse=
function(a){var b,c=a.isEven();if(this.isEven()&&c||0===a.signum())return Sk.builtin.biginteger.ZERO;var d=a.clone();var e=this.clone();var h=Sk.builtin.biginteger.nbv(1);var g=Sk.builtin.biginteger.nbv(0);var f=Sk.builtin.biginteger.nbv(0);for(b=Sk.builtin.biginteger.nbv(1);0!==d.signum();){for(;d.isEven();)d.rShiftTo(1,d),c?(h.isEven()&&g.isEven()||(h.addTo(this,h),g.subTo(a,g)),h.rShiftTo(1,h)):g.isEven()||g.subTo(a,g),g.rShiftTo(1,g);for(;e.isEven();)e.rShiftTo(1,e),c?(f.isEven()&&b.isEven()||
(f.addTo(this,f),b.subTo(a,b)),f.rShiftTo(1,f)):b.isEven()||b.subTo(a,b),b.rShiftTo(1,b);0<=d.compareTo(e)?(d.subTo(e,d),c&&h.subTo(f,h),g.subTo(b,g)):(e.subTo(d,e),c&&f.subTo(h,f),b.subTo(g,b))}if(0!==e.compareTo(Sk.builtin.biginteger.ONE))return Sk.builtin.biginteger.ZERO;if(0<=b.compareTo(a))return b.subtract(a);if(0>b.signum())b.addTo(a,b);else return b;return 0>b.signum()?b.add(a):b};Sk.builtin.biginteger.lowprimes=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,
107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509];Sk.builtin.biginteger.lplim=67108864/Sk.builtin.biginteger.lowprimes[Sk.builtin.biginteger.lowprimes.length-1];Sk.builtin.biginteger.prototype.bnIsProbablePrime=function(a){var b,c,d=this.abs();if(1==d.t&&d[0]<=Sk.builtin.biginteger.lowprimes[Sk.builtin.biginteger.lowprimes.length-
1]){for(c=0;c<Sk.builtin.biginteger.lowprimes.length;++c)if(d[0]==Sk.builtin.biginteger.lowprimes[c])return!0;return!1}if(d.isEven())return!1;for(c=1;c<Sk.builtin.biginteger.lowprimes.length;){var e=Sk.builtin.biginteger.lowprimes[c];for(b=c+1;b<Sk.builtin.biginteger.lowprimes.length&&e<Sk.builtin.biginteger.lplim;)e*=Sk.builtin.biginteger.lowprimes[b++];for(e=d.modInt(e);c<b;)if(0===e%Sk.builtin.biginteger.lowprimes[c++])return!1}return d.millerRabin(a)};Sk.builtin.biginteger.prototype.bnpMillerRabin=
function(a){var b,c,d=this.subtract(Sk.builtin.biginteger.ONE),e=d.getLowestSetBit();if(0>=e)return!1;var h=d.shiftRight(e);a=a+1>>1;a>Sk.builtin.biginteger.lowprimes.length&&(a=Sk.builtin.biginteger.lowprimes.length);var g=Sk.builtin.biginteger.nbi();for(c=0;c<a;++c){g.fromInt(Sk.builtin.biginteger.lowprimes[c]);var f=g.modPow(h,this);if(0!==f.compareTo(Sk.builtin.biginteger.ONE)&&0!==f.compareTo(d)){for(b=1;b++<e&&0!==f.compareTo(d);)if(f=f.modPowInt(2,this),0===f.compareTo(Sk.builtin.biginteger.ONE))return!1;
if(0!==f.compareTo(d))return!1}}return!0};Sk.builtin.biginteger.prototype.isnegative=function(){return 0>this.s};Sk.builtin.biginteger.prototype.ispositive=function(){return 0<=this.s};Sk.builtin.biginteger.prototype.trueCompare=function(a){return 0<=this.s&&0>a.s?1:0>this.s&&0<=a.s?-1:this.compare(a)};Sk.builtin.biginteger.prototype.chunkSize=Sk.builtin.biginteger.prototype.bnpChunkSize;Sk.builtin.biginteger.prototype.toRadix=Sk.builtin.biginteger.prototype.bnpToRadix;Sk.builtin.biginteger.prototype.fromRadix=
Sk.builtin.biginteger.prototype.bnpFromRadix;Sk.builtin.biginteger.prototype.fromNumber=Sk.builtin.biginteger.prototype.bnpFromNumber;Sk.builtin.biginteger.prototype.bitwiseTo=Sk.builtin.biginteger.prototype.bnpBitwiseTo;Sk.builtin.biginteger.prototype.changeBit=Sk.builtin.biginteger.prototype.bnpChangeBit;Sk.builtin.biginteger.prototype.addTo=Sk.builtin.biginteger.prototype.bnpAddTo;Sk.builtin.biginteger.prototype.dMultiply=Sk.builtin.biginteger.prototype.bnpDMultiply;Sk.builtin.biginteger.prototype.dAddOffset=
Sk.builtin.biginteger.prototype.bnpDAddOffset;Sk.builtin.biginteger.prototype.multiplyLowerTo=Sk.builtin.biginteger.prototype.bnpMultiplyLowerTo;Sk.builtin.biginteger.prototype.multiplyUpperTo=Sk.builtin.biginteger.prototype.bnpMultiplyUpperTo;Sk.builtin.biginteger.prototype.modInt=Sk.builtin.biginteger.prototype.bnpModInt;Sk.builtin.biginteger.prototype.millerRabin=Sk.builtin.biginteger.prototype.bnpMillerRabin;Sk.builtin.biginteger.prototype.clone=Sk.builtin.biginteger.prototype.bnClone;Sk.builtin.biginteger.prototype.intValue=
Sk.builtin.biginteger.prototype.bnIntValue;Sk.builtin.biginteger.prototype.byteValue=Sk.builtin.biginteger.prototype.bnByteValue;Sk.builtin.biginteger.prototype.shortValue=Sk.builtin.biginteger.prototype.bnShortValue;Sk.builtin.biginteger.prototype.signum=Sk.builtin.biginteger.prototype.bnSigNum;Sk.builtin.biginteger.prototype.toByteArray=Sk.builtin.biginteger.prototype.bnToByteArray;Sk.builtin.biginteger.prototype.equals=Sk.builtin.biginteger.prototype.bnEquals;Sk.builtin.biginteger.prototype.compare=
Sk.builtin.biginteger.prototype.compareTo;Sk.builtin.biginteger.prototype.min=Sk.builtin.biginteger.prototype.bnMin;Sk.builtin.biginteger.prototype.max=Sk.builtin.biginteger.prototype.bnMax;Sk.builtin.biginteger.prototype.and=Sk.builtin.biginteger.prototype.bnAnd;Sk.builtin.biginteger.prototype.or=Sk.builtin.biginteger.prototype.bnOr;Sk.builtin.biginteger.prototype.xor=Sk.builtin.biginteger.prototype.bnXor;Sk.builtin.biginteger.prototype.andNot=Sk.builtin.biginteger.prototype.bnAndNot;Sk.builtin.biginteger.prototype.not=
Sk.builtin.biginteger.prototype.bnNot;Sk.builtin.biginteger.prototype.shiftLeft=Sk.builtin.biginteger.prototype.bnShiftLeft;Sk.builtin.biginteger.prototype.shiftRight=Sk.builtin.biginteger.prototype.bnShiftRight;Sk.builtin.biginteger.prototype.getLowestSetBit=Sk.builtin.biginteger.prototype.bnGetLowestSetBit;Sk.builtin.biginteger.prototype.bitCount=Sk.builtin.biginteger.prototype.bnBitCount;Sk.builtin.biginteger.prototype.testBit=Sk.builtin.biginteger.prototype.bnTestBit;Sk.builtin.biginteger.prototype.setBit=
Sk.builtin.biginteger.prototype.bnSetBit;Sk.builtin.biginteger.prototype.clearBit=Sk.builtin.biginteger.prototype.bnClearBit;Sk.builtin.biginteger.prototype.flipBit=Sk.builtin.biginteger.prototype.bnFlipBit;Sk.builtin.biginteger.prototype.add=Sk.builtin.biginteger.prototype.bnAdd;Sk.builtin.biginteger.prototype.subtract=Sk.builtin.biginteger.prototype.bnSubtract;Sk.builtin.biginteger.prototype.multiply=Sk.builtin.biginteger.prototype.bnMultiply;Sk.builtin.biginteger.prototype.divide=Sk.builtin.biginteger.prototype.bnDivide;
Sk.builtin.biginteger.prototype.remainder=Sk.builtin.biginteger.prototype.bnRemainder;Sk.builtin.biginteger.prototype.divideAndRemainder=Sk.builtin.biginteger.prototype.bnDivideAndRemainder;Sk.builtin.biginteger.prototype.modPow=Sk.builtin.biginteger.prototype.bnModPow;Sk.builtin.biginteger.prototype.modInverse=Sk.builtin.biginteger.prototype.bnModInverse;Sk.builtin.biginteger.prototype.pow=Sk.builtin.biginteger.prototype.bnPow;Sk.builtin.biginteger.prototype.gcd=Sk.builtin.biginteger.prototype.bnGCD;
Sk.builtin.biginteger.prototype.isProbablePrime=Sk.builtin.biginteger.prototype.bnIsProbablePrime},function(m,p){Sk.builtin.int_=function(a,c){if(!(this instanceof Sk.builtin.int_))return new Sk.builtin.int_(a,c);if(this instanceof Sk.builtin.bool)return this;if(a instanceof Sk.builtin.int_&&void 0===c)return this.v=a.v,this;if(c!==Sk.builtin.none.none$&&void 0!==c&&!Sk.builtin.checkInt(c)){if(Sk.builtin.checkFloat(c))throw new Sk.builtin.TypeError("integer argument expected, got "+Sk.abstr.typeName(c));
if(c.__index__)c=Sk.misceval.callsimArray(c.__index__,[c]);else if(c.__int__)c=Sk.misceval.callsimArray(c.__int__,[c]);else throw new Sk.builtin.AttributeError(Sk.abstr.typeName(c)+" instance has no attribute '__index__' or '__int__'");}if(a instanceof Sk.builtin.str){c=Sk.builtin.asnum$(c);c===Sk.builtin.none.none$&&(c=10);var b=Sk.str2number(a.v,c,parseInt,function(a){return-a},"int");if(b>Sk.builtin.int_.threshold$||b<-Sk.builtin.int_.threshold$)return new Sk.builtin.lng(a,c);this.v=b;return this}if(void 0!==
c&&c!==Sk.builtin.none.none$)throw new Sk.builtin.TypeError("int() can't convert non-string with explicit base");if(void 0===a||a===Sk.builtin.none)a=0;if(void 0!==a&&a.tp$getattr&&(b=a.tp$getattr(Sk.builtin.str.$int_))){var e=Sk.misceval.callsimArray(b);var h="__int__"}else void 0!==a&&a.__int__?(e=Sk.misceval.callsimArray(a.__int__,[a]),h="__int__"):void 0!==a&&a.tp$getattr&&(b=a.tp$getattr(Sk.builtin.str.$trunc))?(e=Sk.misceval.callsimArray(b),h="__trunc__"):void 0!==a&&a.__trunc__&&(e=Sk.misceval.callsimArray(a.__trunc__,
[a]),h="__trunc__");if(void 0===e||Sk.builtin.checkInt(e))void 0!==e&&(a=e);else throw new Sk.builtin.TypeError(h+" returned non-Integral (type "+Sk.abstr.typeName(e)+")");if(!Sk.builtin.checkNumber(a))throw new Sk.builtin.TypeError("int() argument must be a string or a number, not '"+Sk.abstr.typeName(a)+"'");a=Sk.builtin.asnum$(a);if(a>Sk.builtin.int_.threshold$||a<-Sk.builtin.int_.threshold$)return new Sk.builtin.lng(a);-1<a&&1>a&&(a=0);this.v=parseInt(a,c);return this};Sk.builtin.int_.$shiftconsts=
[.5,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,2147483648,4294967296,8589934592,17179869184,34359738368,68719476736,137438953472,274877906944,549755813888,1099511627776,2199023255552,4398046511104,8796093022208,17592186044416,35184372088832,70368744177664,0x800000000000,281474976710656,562949953421312,0x4000000000000,0x8000000000000,4503599627370496,9007199254740992];
Sk.abstr.setUpInheritance("int",Sk.builtin.int_,Sk.builtin.numtype);Sk.builtin.int_.prototype.nb$int_=function(){return this};Sk.builtin.int_.prototype.nb$float_=function(){return new Sk.builtin.float_(this.v)};Sk.builtin.int_.prototype.nb$lng=function(){return new Sk.builtin.lng(this.v)};Sk.builtin.int_.prototype.__trunc__=new Sk.builtin.func(function(a){return a});Sk.builtin.int_.prototype.__index__=new Sk.builtin.func(function(a){return a});Sk.builtin.int_.prototype.__complex__=new Sk.builtin.func(function(a){return Sk.builtin.NotImplemented.NotImplemented$});
Sk.builtin.int_.prototype.__format__=Sk.formatting.mkNumber__format__(!1);Sk.builtin.int_.prototype.tp$index=function(){return this.v};Sk.builtin.int_.prototype.tp$hash=function(){return new Sk.builtin.int_(this.v)};Sk.builtin.int_.threshold$=Math.pow(2,53)-1;Sk.builtin.int_.prototype.clone=function(){return new Sk.builtin.int_(this.v)};Sk.builtin.int_.prototype.nb$add=function(a){if(a instanceof Sk.builtin.int_){var c=this.v+a.v;return c>Sk.builtin.int_.threshold$||c<-Sk.builtin.int_.threshold$?
(c=new Sk.builtin.lng(this.v),c.nb$add(a)):new Sk.builtin.int_(c)}return a instanceof Sk.builtin.lng?(c=new Sk.builtin.lng(this.v),c.nb$add(a)):a instanceof Sk.builtin.float_?(c=new Sk.builtin.float_(this.v),c.nb$add(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.nb$reflected_add=function(a){return Sk.builtin.int_.prototype.nb$add.call(this,a)};Sk.builtin.int_.prototype.nb$subtract=function(a){if(a instanceof Sk.builtin.int_){var c=this.v-a.v;return c>Sk.builtin.int_.threshold$||
c<-Sk.builtin.int_.threshold$?(c=new Sk.builtin.lng(this.v),c.nb$subtract(a)):new Sk.builtin.int_(c)}return a instanceof Sk.builtin.lng?(c=new Sk.builtin.lng(this.v),c.nb$subtract(a)):a instanceof Sk.builtin.float_?(c=new Sk.builtin.float_(this.v),c.nb$subtract(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.nb$reflected_subtract=function(a){var c=this.nb$negative();return Sk.builtin.int_.prototype.nb$add.call(c,a)};Sk.builtin.int_.prototype.nb$multiply=function(a){if(a instanceof
Sk.builtin.int_){var c=this.v*a.v;return c>Sk.builtin.int_.threshold$||c<-Sk.builtin.int_.threshold$?(c=new Sk.builtin.lng(this.v),c.nb$multiply(a)):new Sk.builtin.int_(c)}return a instanceof Sk.builtin.lng?(c=new Sk.builtin.lng(this.v),c.nb$multiply(a)):a instanceof Sk.builtin.float_?(c=new Sk.builtin.float_(this.v),c.nb$multiply(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.nb$reflected_multiply=function(a){return Sk.builtin.int_.prototype.nb$multiply.call(this,a)};Sk.builtin.int_.prototype.nb$divide=
function(a){if(Sk.__future__.division){var c=new Sk.builtin.float_(this.v);return c.nb$divide(a)}return a instanceof Sk.builtin.int_?this.nb$floor_divide(a):a instanceof Sk.builtin.lng?(c=new Sk.builtin.lng(this.v),c.nb$divide(a)):a instanceof Sk.builtin.float_?(c=new Sk.builtin.float_(this.v),c.nb$divide(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.nb$reflected_divide=function(a){return this.nb$reflected_floor_divide(a)};Sk.builtin.int_.prototype.nb$floor_divide=function(a){if(a instanceof
Sk.builtin.int_){if(0===a.v)throw new Sk.builtin.ZeroDivisionError("integer division or modulo by zero");return new Sk.builtin.int_(Math.floor(this.v/a.v))}if(a instanceof Sk.builtin.lng){var c=new Sk.builtin.lng(this.v);return c.nb$floor_divide(a)}return a instanceof Sk.builtin.float_?(c=new Sk.builtin.float_(this.v),c.nb$floor_divide(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.nb$reflected_floor_divide=function(a){return a instanceof Sk.builtin.int_?a.nb$divide(this):
Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.nb$remainder=function(a){if(a instanceof Sk.builtin.int_){var c=Sk.abstr.numberBinOp(this,a,"FloorDiv");c=Sk.abstr.numberBinOp(c,a,"Mult");c=Sk.abstr.numberBinOp(this,c,"Sub");c=c.v;0>a.v&&0===c?c=-0:0===c&&-Infinity===Infinity/c&&(c=0);return new Sk.builtin.int_(c)}return a instanceof Sk.builtin.lng?(c=new Sk.builtin.lng(this.v),c.nb$remainder(a)):a instanceof Sk.builtin.float_?(c=new Sk.builtin.float_(this.v),c.nb$remainder(a)):
Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.nb$reflected_remainder=function(a){return a instanceof Sk.builtin.int_?a.nb$remainder(this):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.nb$divmod=function(a){if(a instanceof Sk.builtin.int_)return new Sk.builtin.tuple([this.nb$floor_divide(a),this.nb$remainder(a)]);if(a instanceof Sk.builtin.lng){var c=new Sk.builtin.lng(this.v);return c.nb$divmod(a)}return a instanceof Sk.builtin.float_?(c=new Sk.builtin.float_(this.v),
c.nb$divmod(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.nb$reflected_divmod=function(a){return a instanceof Sk.builtin.int_?new Sk.builtin.tuple([a.nb$floor_divide(this),a.nb$remainder(this)]):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.nb$power=function(a,c){if(a instanceof Sk.builtin.int_&&(void 0===c||c instanceof Sk.builtin.int_)){var b=Math.pow(this.v,a.v);b>Sk.builtin.int_.threshold$||b<-Sk.builtin.int_.threshold$?(b=new Sk.builtin.lng(this.v),
b=b.nb$power(a,c)):b=0>a.v?new Sk.builtin.float_(b):new Sk.builtin.int_(b);if(void 0!==c){if(0>a.v)throw new Sk.builtin.TypeError("pow() 2nd argument cannot be negative when 3rd argument specified");return b.nb$remainder(c)}return b}return a instanceof Sk.builtin.lng?(b=new Sk.builtin.lng(this.v),b.nb$power(a)):a instanceof Sk.builtin.float_?(c=new Sk.builtin.float_(this.v),c.nb$power(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.nb$reflected_power=function(a,c){return a instanceof
Sk.builtin.int_?a.nb$power(this,c):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.nb$abs=function(){return new Sk.builtin.int_(Math.abs(this.v))};Sk.builtin.int_.prototype.nb$and=function(a){if(a instanceof Sk.builtin.int_){a=Sk.builtin.asnum$(a);var c=this.v&a;void 0!==c&&0>c&&(c+=4294967296);if(void 0!==c)return new Sk.builtin.int_(c)}return a instanceof Sk.builtin.lng?(c=new Sk.builtin.lng(this.v),c.nb$and(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.nb$reflected_and=
Sk.builtin.int_.prototype.nb$and;Sk.builtin.int_.prototype.nb$or=function(a){if(a instanceof Sk.builtin.int_){a=Sk.builtin.asnum$(a);var c=this.v|a;void 0!==c&&0>c&&(c+=4294967296);if(void 0!==c)return new Sk.builtin.int_(c)}return a instanceof Sk.builtin.lng?(c=new Sk.builtin.lng(this.v),c.nb$and(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.nb$reflected_or=Sk.builtin.int_.prototype.nb$or;Sk.builtin.int_.prototype.nb$xor=function(a){if(a instanceof Sk.builtin.int_){a=Sk.builtin.asnum$(a);
var c=this.v^a;void 0!==c&&0>c&&(c+=4294967296);if(void 0!==c)return new Sk.builtin.int_(c)}return a instanceof Sk.builtin.lng?(c=new Sk.builtin.lng(this.v),c.nb$xor(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.nb$reflected_xor=Sk.builtin.int_.prototype.nb$xor;Sk.builtin.int_.prototype.nb$lshift=function(a){if(0===this.v)return this;if(a instanceof Sk.builtin.int_){var c=Sk.builtin.asnum$(a);if(void 0!==c){if(0>c)throw new Sk.builtin.ValueError("negative shift count");
if(53<c)return(new Sk.builtin.lng(this.v)).nb$lshift(new Sk.builtin.int_(c));var b=2*this.v*Sk.builtin.int_.$shiftconsts[c];if(b>Sk.builtin.int_.threshold$||b<-Sk.builtin.int_.threshold$)return new Sk.builtin.lng(b)}if(void 0!==b)return new Sk.builtin.int_(b)}return a instanceof Sk.builtin.lng?(b=new Sk.builtin.lng(this.v),b.nb$lshift(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.nb$reflected_lshift=function(a){return a instanceof Sk.builtin.int_?a.nb$lshift(this):Sk.builtin.NotImplemented.NotImplemented$};
Sk.builtin.int_.prototype.nb$rshift=function(a){if(a instanceof Sk.builtin.int_){var c=Sk.builtin.asnum$(a);if(void 0!==c){if(0>c)throw new Sk.builtin.ValueError("negative shift count");var b=this.v>>c;0<this.v&&0>b&&(b&=Math.pow(2,32-c)-1)}if(void 0!==b)return new Sk.builtin.int_(b)}return a instanceof Sk.builtin.lng?(b=new Sk.builtin.lng(this.v),b.nb$rshift(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.nb$reflected_rshift=function(a){return a instanceof Sk.builtin.int_?
a.nb$rshift(this):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.nb$invert=function(){return new Sk.builtin.int_(~this.v)};Sk.builtin.int_.prototype.nb$inplace_add=Sk.builtin.int_.prototype.nb$add;Sk.builtin.int_.prototype.nb$inplace_subtract=Sk.builtin.int_.prototype.nb$subtract;Sk.builtin.int_.prototype.nb$inplace_multiply=Sk.builtin.int_.prototype.nb$multiply;Sk.builtin.int_.prototype.nb$inplace_divide=Sk.builtin.int_.prototype.nb$divide;Sk.builtin.int_.prototype.nb$inplace_remainder=
Sk.builtin.int_.prototype.nb$remainder;Sk.builtin.int_.prototype.nb$inplace_floor_divide=Sk.builtin.int_.prototype.nb$floor_divide;Sk.builtin.int_.prototype.nb$inplace_power=Sk.builtin.int_.prototype.nb$power;Sk.builtin.int_.prototype.nb$inplace_and=Sk.builtin.int_.prototype.nb$and;Sk.builtin.int_.prototype.nb$inplace_or=Sk.builtin.int_.prototype.nb$or;Sk.builtin.int_.prototype.nb$inplace_xor=Sk.builtin.int_.prototype.nb$xor;Sk.builtin.int_.prototype.nb$inplace_lshift=Sk.builtin.int_.prototype.nb$lshift;
Sk.builtin.int_.prototype.nb$inplace_rshift=Sk.builtin.int_.prototype.nb$rshift;Sk.builtin.int_.prototype.nb$negative=function(){return new Sk.builtin.int_(-this.v)};Sk.builtin.int_.prototype.nb$positive=function(){return this.clone()};Sk.builtin.int_.prototype.nb$nonzero=function(){return 0!==this.v};Sk.builtin.int_.prototype.nb$isnegative=function(){return 0>this.v};Sk.builtin.int_.prototype.nb$ispositive=function(){return 0<=this.v};Sk.builtin.int_.prototype.numberCompare=function(a){return a instanceof
Sk.builtin.int_?this.v-a.v:a instanceof Sk.builtin.lng?-a.longCompare(this):a instanceof Sk.builtin.float_?-a.numberCompare(this):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.ob$eq=function(a){return a instanceof Sk.builtin.int_||a instanceof Sk.builtin.lng||a instanceof Sk.builtin.float_?new Sk.builtin.bool(0==this.numberCompare(a)):a===Sk.builtin.none.none$?Sk.builtin.bool.false$:Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.ob$ne=function(a){return a instanceof
Sk.builtin.int_||a instanceof Sk.builtin.lng||a instanceof Sk.builtin.float_?new Sk.builtin.bool(0!=this.numberCompare(a)):a===Sk.builtin.none.none$?Sk.builtin.bool.true$:Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.ob$lt=function(a){return a instanceof Sk.builtin.int_||a instanceof Sk.builtin.lng||a instanceof Sk.builtin.float_?new Sk.builtin.bool(0>this.numberCompare(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.ob$le=function(a){return a instanceof
Sk.builtin.int_||a instanceof Sk.builtin.lng||a instanceof Sk.builtin.float_?new Sk.builtin.bool(0>=this.numberCompare(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.ob$gt=function(a){return a instanceof Sk.builtin.int_||a instanceof Sk.builtin.lng||a instanceof Sk.builtin.float_?new Sk.builtin.bool(0<this.numberCompare(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.ob$ge=function(a){return a instanceof Sk.builtin.int_||a instanceof Sk.builtin.lng||
a instanceof Sk.builtin.float_?new Sk.builtin.bool(0<=this.numberCompare(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.int_.prototype.round$=function(a,c){Sk.builtin.pyCheckArgsLen("__round__",arguments.length,1,2);if(void 0!==c&&!Sk.misceval.isIndex(c))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(c)+"' object cannot be interpreted as an index");var b=Sk.builtin.asnum$(a);var e=void 0===c?0:Sk.misceval.asIndex(c);if(Sk.__future__.bankers_rounding){b*=Math.pow(10,e);var h=Math.round(b);
e=(.5===(0<b?b:-b)%1?0===h%2?h:h-1:h)/Math.pow(10,e)}else e=Math.pow(10,e),e=Math.round(b*e)/e;return new Sk.builtin.int_(e)};Sk.builtin.int_.prototype.conjugate=new Sk.builtin.func(function(a){return new Sk.builtin.int_(a.v)});Sk.builtin.int_.prototype.$r=function(){return new Sk.builtin.str(this.str$(10,!0))};Sk.builtin.int_.prototype.tp$str=function(){return new Sk.builtin.str(this.str$(10,!0))};Sk.builtin.int_.prototype.str$=function(a,c){void 0===c&&(c=!0);c=c?this.v:Math.abs(this.v);return void 0===
a||10===a?c.toString():c.toString(a)};const a=/_(?=[^_])/g;Sk.str2number=function(b,c,d,e,h){var g=b,f=!1,k;b=b.replace(/^\s+|\s+$/g,"");"-"===b.charAt(0)&&(f=!0,b=b.substring(1));"+"===b.charAt(0)&&(b=b.substring(1));if(null===c||void 0===c)c=10;if((2>c||36<c)&&0!==c)throw new Sk.builtin.ValueError(h+"() base must be >= 2 and <= 36");if("0x"===b.substring(0,2).toLowerCase())if(16===c||0===c)b=b.substring(2),c=16;else{if(34>c)throw new Sk.builtin.ValueError("invalid literal for "+h+"() with base "+
c+": '"+g+"'");}else if("0b"===b.substring(0,2).toLowerCase())if(2===c||0===c)b=b.substring(2),c=2;else{if(12>c)throw new Sk.builtin.ValueError("invalid literal for "+h+"() with base "+c+": '"+g+"'");}else if("0o"===b.substring(0,2).toLowerCase())if(8===c||0===c)b=b.substring(2),c=8;else{if(25>c)throw new Sk.builtin.ValueError("invalid literal for "+h+"() with base "+c+": '"+g+"'");}else if("0"===b.charAt(0)){if("0"===b)return 0;if(8===c||0===c)c=8}0===c&&(c=10);if(-1!==b.indexOf("_")){if(-1!==b.indexOf("__"))throw new Sk.builtin.ValueError("invalid literal for "+
h+"() with base "+c+": '"+g+"'");b=10!==c?b.replace(a,""):b.charAt(0)+b.substring(1).replace(a,"")}if(0===b.length)throw new Sk.builtin.ValueError("invalid literal for "+h+"() with base "+c+": '"+g+"'");for(k=0;k<b.length;k+=1){var n=b.charCodeAt(k);var l=c;48<=n&&57>=n?l=n-48:65<=n&&90>=n?l=n-65+10:97<=n&&122>=n&&(l=n-97+10);if(l>=c)throw new Sk.builtin.ValueError("invalid literal for "+h+"() with base "+c+": '"+g+"'");}l=d(b,c);f&&(l=e(l));return l};Sk.exportSymbol("Sk.builtin.int_",Sk.builtin.int_)},
function(m,p){Sk.builtin.bool=function(a){Sk.builtin.pyCheckArgsLen("bool",arguments.length,1);return Sk.misceval.isTrue(a)?Sk.builtin.bool.true$:Sk.builtin.bool.false$};Sk.abstr.setUpInheritance("bool",Sk.builtin.bool,Sk.builtin.int_);Sk.builtin.bool.prototype.$r=function(){return this.v?new Sk.builtin.str("True"):new Sk.builtin.str("False")};Sk.builtin.bool.prototype.tp$hash=function(){return new Sk.builtin.int_(this.v)};Sk.builtin.bool.prototype.__int__=new Sk.builtin.func(function(a){a=Sk.builtin.asnum$(a);
return new Sk.builtin.int_(a)});Sk.builtin.bool.prototype.__float__=new Sk.builtin.func(function(a){return new Sk.builtin.float_(Sk.ffi.remapToJs(a))});Sk.builtin.bool.prototype.__format__=new Sk.builtin.func(function(a){return a.$r()});Sk.builtin.bool.prototype.nb$and=function(a){return a.ob$type===Sk.builtin.bool?new Sk.builtin.bool(this.v&a.v):Sk.builtin.int_.prototype.nb$and.call(this,a)};Sk.builtin.bool.prototype.nb$or=function(a){return a.ob$type===Sk.builtin.bool?new Sk.builtin.bool(this.v|
a.v):Sk.builtin.int_.prototype.nb$or.call(this,a)};Sk.builtin.bool.prototype.nb$xor=function(a){return a.ob$type===Sk.builtin.bool?new Sk.builtin.bool(this.v^a.v):Sk.builtin.int_.prototype.nb$xor.call(this,a)};Sk.builtin.bool.prototype.ob$eq=function(a){return Sk.builtin.int_.prototype.ob$eq.call(this,a)};Sk.builtin.bool.prototype.ob$ne=function(a){return Sk.builtin.int_.prototype.ob$ne.call(this,a)};Sk.builtin.bool.prototype.ob$lt=function(a){return Sk.builtin.int_.prototype.ob$lt.call(this,a)};
Sk.builtin.bool.prototype.ob$le=function(a){return Sk.builtin.int_.prototype.ob$le.call(this,a)};Sk.builtin.bool.prototype.ob$gt=function(a){return Sk.builtin.int_.prototype.ob$gt.call(this,a)};Sk.builtin.bool.prototype.ob$ge=function(a){return Sk.builtin.int_.prototype.ob$ge.call(this,a)};Sk.exportSymbol("Sk.builtin.bool",Sk.builtin.bool);Sk.builtin.bool.true$=Object.create(Sk.builtin.bool.prototype,{v:{value:1,enumerable:!0}});Sk.builtin.bool.false$=Object.create(Sk.builtin.bool.prototype,{v:{value:0,
enumerable:!0}})},function(m,p){Sk.builtin.float_=function(c){if(!(this instanceof Sk.builtin.float_))return new Sk.builtin.float_(c);if(void 0===c)this.v=0;else if("number"===typeof c)this.v=c;else if(c.nb$float_){c=c.nb$float_();if(c.constructor!==Sk.builtin.float_)throw new Sk.builtin.TypeError("__float__ returned non-float (type "+Sk.abstr.typeName(c)+")");this.v=c.v}else if(Sk.builtin.checkString(c)){{let d=c=c.$jsstr();if(-1!==c.indexOf("_")){if(a.test(c))throw new Sk.builtin.ValueError("could not convert string to float: '"+
c+"'");d=c.charAt(0)+c.substring(1).replace(b,"")}if(c.match(/^-inf$/i))c=-Infinity;else if(c.match(/^[+]?inf$/i))c=Infinity;else if(c.match(/^[-+]?nan$/i))c=NaN;else{if(isNaN(d))throw new Sk.builtin.ValueError("float: Argument: "+c+" is not number");c=parseFloat(d)}c=new Sk.builtin.float_(c)}this.v=c.v}else if("boolean"===typeof c)this.v=c?1:0;else if("string"===typeof c)this.v=parseFloat(c);else throw new Sk.builtin.TypeError("float() argument must be a string or a number");};Sk.abstr.setUpInheritance("float",
Sk.builtin.float_,Sk.builtin.numtype);const a=/_[eE]|[eE]_|\._|_\.|[+-]_|__/,b=/_(?=[^_])/g;Sk.builtin.float_.prototype.nb$int_=function(){var a=this.v;a=0>a?Math.ceil(a):Math.floor(a);return new Sk.builtin.int_(a)};Sk.builtin.float_.prototype.nb$float_=function(){return this};Sk.builtin.float_.prototype.nb$lng=function(){return new Sk.builtin.lng(this.v)};Sk.builtin.float_.PyFloat_Check=function(a){return void 0===a?!1:Sk.builtin.checkNumber(a)||Sk.builtin.checkFloat(a)||Sk.builtin.issubclass(a.ob$type,
Sk.builtin.float_)?!0:!1};Sk.builtin.float_.PyFloat_Check_Exact=function(a){return Sk.builtin.checkFloat(a)};Sk.builtin.float_.PyFloat_AsDouble=function(a){if(a&&Sk.builtin.float_.PyFloat_Check(a))return Sk.ffi.remapToJs(a);if(null==a)throw Error("bad argument for internal PyFloat_AsDouble function");var c=Sk.builtin.type.typeLookup(a.ob$type,Sk.builtin.str.$float_);if(null==c)throw new Sk.builtin.TypeError("a float is required");a=Sk.misceval.callsimArray(c,[a]);if(!Sk.builtin.float_.PyFloat_Check(a))throw new Sk.builtin.TypeError("nb_float should return float object");
return Sk.ffi.remapToJs(a)};Sk.builtin.float_.prototype.tp$index=function(){return this.v};Sk.builtin.float_.prototype.tp$hash=function(){return this.nb$int_()};Sk.builtin.float_.prototype.clone=function(){return new Sk.builtin.float_(this.v)};Sk.builtin.float_.prototype.toFixed=function(a){a=Sk.builtin.asnum$(a);return this.v.toFixed(a)};Sk.builtin.float_.prototype.nb$add=function(a){return a instanceof Sk.builtin.int_||a instanceof Sk.builtin.float_?new Sk.builtin.float_(this.v+a.v):a instanceof
Sk.builtin.lng?new Sk.builtin.float_(this.v+parseFloat(a.str$(10,!0))):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.float_.prototype.nb$reflected_add=function(a){return Sk.builtin.float_.prototype.nb$add.call(this,a)};Sk.builtin.float_.prototype.nb$subtract=function(a){return a instanceof Sk.builtin.int_||a instanceof Sk.builtin.float_?new Sk.builtin.float_(this.v-a.v):a instanceof Sk.builtin.lng?new Sk.builtin.float_(this.v-parseFloat(a.str$(10,!0))):Sk.builtin.NotImplemented.NotImplemented$};
Sk.builtin.float_.prototype.nb$reflected_subtract=function(a){var c=this.nb$negative();return Sk.builtin.float_.prototype.nb$add.call(c,a)};Sk.builtin.float_.prototype.nb$multiply=function(a){return a instanceof Sk.builtin.int_||a instanceof Sk.builtin.float_?new Sk.builtin.float_(this.v*a.v):a instanceof Sk.builtin.lng?new Sk.builtin.float_(this.v*parseFloat(a.str$(10,!0))):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.float_.prototype.nb$reflected_multiply=function(a){return Sk.builtin.float_.prototype.nb$multiply.call(this,
a)};Sk.builtin.float_.prototype.nb$divide=function(a){if(a instanceof Sk.builtin.int_||a instanceof Sk.builtin.float_){if(0===a.v)throw new Sk.builtin.ZeroDivisionError("integer division or modulo by zero");return Infinity===this.v?Infinity===a.v||-Infinity===a.v?new Sk.builtin.float_(NaN):a.nb$isnegative()?new Sk.builtin.float_(-Infinity):new Sk.builtin.float_(Infinity):-Infinity===this.v?Infinity===a.v||-Infinity===a.v?new Sk.builtin.float_(NaN):a.nb$isnegative()?new Sk.builtin.float_(Infinity):
new Sk.builtin.float_(-Infinity):new Sk.builtin.float_(this.v/a.v)}if(a instanceof Sk.builtin.lng){if(0===a.longCompare(Sk.builtin.biginteger.ZERO))throw new Sk.builtin.ZeroDivisionError("integer division or modulo by zero");return Infinity===this.v?a.nb$isnegative()?new Sk.builtin.float_(-Infinity):new Sk.builtin.float_(Infinity):-Infinity===this.v?a.nb$isnegative()?new Sk.builtin.float_(Infinity):new Sk.builtin.float_(-Infinity):new Sk.builtin.float_(this.v/parseFloat(a.str$(10,!0)))}return Sk.builtin.NotImplemented.NotImplemented$};
Sk.builtin.float_.prototype.nb$reflected_divide=function(a){if(a instanceof Sk.builtin.int_||a instanceof Sk.builtin.lng)a=new Sk.builtin.float_(a);return a instanceof Sk.builtin.float_?a.nb$divide(this):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.float_.prototype.nb$floor_divide=function(a){if(a instanceof Sk.builtin.int_||a instanceof Sk.builtin.float_){if(Infinity===this.v||-Infinity===this.v)return new Sk.builtin.float_(NaN);if(0===a.v)throw new Sk.builtin.ZeroDivisionError("integer division or modulo by zero");
return Infinity===a.v?this.nb$isnegative()?new Sk.builtin.float_(-1):new Sk.builtin.float_(0):-Infinity===a.v?this.nb$isnegative()||!this.nb$nonzero()?new Sk.builtin.float_(0):new Sk.builtin.float_(-1):new Sk.builtin.float_(Math.floor(this.v/a.v))}if(a instanceof Sk.builtin.lng){if(0===a.longCompare(Sk.builtin.biginteger.ZERO))throw new Sk.builtin.ZeroDivisionError("integer division or modulo by zero");return Infinity===this.v||-Infinity===this.v?new Sk.builtin.float_(NaN):new Sk.builtin.float_(Math.floor(this.v/
parseFloat(a.str$(10,!0))))}return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.float_.prototype.nb$reflected_floor_divide=function(a){if(a instanceof Sk.builtin.int_||a instanceof Sk.builtin.lng)a=new Sk.builtin.float_(a);return a instanceof Sk.builtin.float_?a.nb$floor_divide(this):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.float_.prototype.nb$remainder=function(a){if(a instanceof Sk.builtin.int_||a instanceof Sk.builtin.float_){if(0===a.v)throw new Sk.builtin.ZeroDivisionError("integer division or modulo by zero");
if(0===this.v)return new Sk.builtin.float_(0);if(Infinity===a.v)return Infinity===this.v||-Infinity===this.v?new Sk.builtin.float_(NaN):this.nb$ispositive()?new Sk.builtin.float_(this.v):new Sk.builtin.float_(Infinity);var c=this.v%a.v;0>this.v?0<a.v&&0>c&&(c+=a.v):0>a.v&&0!==c&&(c+=a.v);0>a.v&&0===c?c=-0:0===c&&-Infinity===Infinity/c&&(c=0);return new Sk.builtin.float_(c)}if(a instanceof Sk.builtin.lng){if(0===a.longCompare(Sk.builtin.biginteger.ZERO))throw new Sk.builtin.ZeroDivisionError("integer division or modulo by zero");
if(0===this.v)return new Sk.builtin.float_(0);var b=parseFloat(a.str$(10,!0));c=this.v%b;0>c?0<b&&0!==c&&(c+=b):0>b&&0!==c&&(c+=b);a.nb$isnegative()&&0===c?c=-0:0===c&&-Infinity===Infinity/c&&(c=0);return new Sk.builtin.float_(c)}return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.float_.prototype.nb$reflected_remainder=function(a){if(a instanceof Sk.builtin.int_||a instanceof Sk.builtin.lng)a=new Sk.builtin.float_(a);return a instanceof Sk.builtin.float_?a.nb$remainder(this):Sk.builtin.NotImplemented.NotImplemented$};
Sk.builtin.float_.prototype.nb$divmod=function(a){if(a instanceof Sk.builtin.int_||a instanceof Sk.builtin.lng)a=new Sk.builtin.float_(a);return a instanceof Sk.builtin.float_?new Sk.builtin.tuple([this.nb$floor_divide(a),this.nb$remainder(a)]):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.float_.prototype.nb$reflected_divmod=function(a){if(a instanceof Sk.builtin.int_||a instanceof Sk.builtin.lng)a=new Sk.builtin.float_(a);return a instanceof Sk.builtin.float_?new Sk.builtin.tuple([a.nb$floor_divide(this),
a.nb$remainder(this)]):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.float_.prototype.nb$power=function(a,b){if(a instanceof Sk.builtin.int_||a instanceof Sk.builtin.float_){if(0>this.v&&0!==a.v%1)throw new Sk.builtin.NegativePowerError("cannot raise a negative number to a fractional power");if(0===this.v&&0>a.v)throw new Sk.builtin.NegativePowerError("cannot raise zero to a negative power");b=new Sk.builtin.float_(Math.pow(this.v,a.v));if(Infinity===Math.abs(b.v)&&Infinity!==Math.abs(this.v)&&
Infinity!==Math.abs(a.v))throw new Sk.builtin.OverflowError("Numerical result out of range");return b}if(a instanceof Sk.builtin.lng){if(0===this.v&&0>a.longCompare(Sk.builtin.biginteger.ZERO))throw new Sk.builtin.NegativePowerError("cannot raise zero to a negative power");return new Sk.builtin.float_(Math.pow(this.v,parseFloat(a.str$(10,!0))))}return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.float_.prototype.nb$reflected_power=function(a,b){if(a instanceof Sk.builtin.int_||a instanceof
Sk.builtin.lng)a=new Sk.builtin.float_(a);return a instanceof Sk.builtin.float_?a.nb$power(this,b):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.float_.prototype.nb$abs=function(){return new Sk.builtin.float_(Math.abs(this.v))};Sk.builtin.float_.prototype.nb$inplace_add=Sk.builtin.float_.prototype.nb$add;Sk.builtin.float_.prototype.nb$inplace_subtract=Sk.builtin.float_.prototype.nb$subtract;Sk.builtin.float_.prototype.nb$inplace_multiply=Sk.builtin.float_.prototype.nb$multiply;Sk.builtin.float_.prototype.nb$inplace_divide=
Sk.builtin.float_.prototype.nb$divide;Sk.builtin.float_.prototype.nb$inplace_remainder=Sk.builtin.float_.prototype.nb$remainder;Sk.builtin.float_.prototype.nb$inplace_floor_divide=Sk.builtin.float_.prototype.nb$floor_divide;Sk.builtin.float_.prototype.nb$inplace_power=Sk.builtin.float_.prototype.nb$power;Sk.builtin.float_.prototype.nb$negative=function(){return new Sk.builtin.float_(-this.v)};Sk.builtin.float_.prototype.nb$positive=function(){return this.clone()};Sk.builtin.float_.prototype.nb$nonzero=
function(){return 0!==this.v};Sk.builtin.float_.prototype.nb$isnegative=function(){return 0>this.v};Sk.builtin.float_.prototype.nb$ispositive=function(){return 0<=this.v};Sk.builtin.float_.prototype.numberCompare=function(a){if(a instanceof Sk.builtin.int_||a instanceof Sk.builtin.float_)return Infinity==this.v&&Infinity==a.v||-Infinity==this.v&&-Infinity==a.v?0:this.v-a.v;if(a instanceof Sk.builtin.lng){if(0===this.v%1){var c=new Sk.builtin.lng(this.v);return a=c.longCompare(a)}a=this.nb$subtract(a);
if(a instanceof Sk.builtin.float_)return a.v;if(a instanceof Sk.builtin.lng)return a.longCompare(Sk.builtin.biginteger.ZERO)}return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.float_.prototype.ob$eq=function(a){return a instanceof Sk.builtin.int_||a instanceof Sk.builtin.lng||a instanceof Sk.builtin.float_?new Sk.builtin.bool(0==this.numberCompare(a)):a===Sk.builtin.none.none$?Sk.builtin.bool.false$:Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.float_.prototype.ob$ne=function(a){return a instanceof
Sk.builtin.int_||a instanceof Sk.builtin.lng||a instanceof Sk.builtin.float_?new Sk.builtin.bool(0!=this.numberCompare(a)):a===Sk.builtin.none.none$?Sk.builtin.bool.true$:Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.float_.prototype.ob$lt=function(a){return a instanceof Sk.builtin.int_||a instanceof Sk.builtin.lng||a instanceof Sk.builtin.float_?new Sk.builtin.bool(0>this.numberCompare(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.float_.prototype.ob$le=function(a){return a instanceof
Sk.builtin.int_||a instanceof Sk.builtin.lng||a instanceof Sk.builtin.float_?new Sk.builtin.bool(0>=this.numberCompare(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.float_.prototype.ob$gt=function(a){return a instanceof Sk.builtin.int_||a instanceof Sk.builtin.lng||a instanceof Sk.builtin.float_?new Sk.builtin.bool(0<this.numberCompare(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.float_.prototype.ob$ge=function(a){return a instanceof Sk.builtin.int_||a instanceof Sk.builtin.lng||
a instanceof Sk.builtin.float_?new Sk.builtin.bool(0<=this.numberCompare(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.float_.prototype.round$=function(a,b){Sk.builtin.pyCheckArgsLen("__round__",arguments.length,1,2);if(void 0!==b&&!Sk.misceval.isIndex(b))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(b)+"' object cannot be interpreted as an index");var c=Sk.builtin.asnum$(a);var d=void 0===b?0:Sk.misceval.asIndex(b);if(Sk.__future__.bankers_rounding){c*=Math.pow(10,d);var g=Math.round(c);
d=(.5===(0<c?c:-c)%1?0===g%2?g:g-1:g)/Math.pow(10,d);return void 0===b?new Sk.builtin.int_(d):new Sk.builtin.float_(d)}d=Math.pow(10,d);d=Math.round(c*d)/d;return new Sk.builtin.float_(d)};Sk.builtin.float_.prototype.__format__=Sk.formatting.mkNumber__format__(!0);Sk.builtin.float_.prototype.conjugate=new Sk.builtin.func(function(a){return new Sk.builtin.float_(a.v)});Sk.builtin.float_.prototype.$r=function(){return new Sk.builtin.str(this.str$(10,!0))};Sk.builtin.float_.prototype.tp$str=function(){return new Sk.builtin.str(this.str$(10,
!0))};Sk.builtin.float_.prototype.str$=function(a,b){if(isNaN(this.v))return"nan";void 0===b&&(b=!0);if(Infinity==this.v)return"inf";if(-Infinity==this.v&&b)return"-inf";if(-Infinity==this.v&&!b)return"inf";b=b?this.v:Math.abs(this.v);if(void 0===a||10===a){var c=Sk.__future__.python3?b.toPrecision(16):b.toPrecision(12);var d=c.indexOf(".");a=b.toString().slice(0,d);d=b.toString().slice(d);a.match(/^-?0$/)&&d.slice(1).match(/^0{4,}/)&&(c=12>c.length?b.toExponential():b.toExponential(11));if(0>c.indexOf("e")&&
0<=c.indexOf(".")){for(;"0"==c.charAt(c.length-1);)c=c.substring(0,c.length-1);"."==c.charAt(c.length-1)&&(c+="0")}c=c.replace(/\.0+e/,"e","i");c=c.replace(/(e[-+])([1-9])$/,"$10$2");c=c.replace(/0+(e.*)/,"$1")}else c=b.toString(a);0===this.v&&-Infinity===1/this.v&&(c="-"+c);0>c.indexOf(".")&&0>c.indexOf("E")&&0>c.indexOf("e")&&(c+=".0");return c}},function(m,p){var a=new Sk.builtin.ExternalError("Sk.builtin.nmber is deprecated.");Sk.builtin.nmber=function(a,c){throw new Sk.builtin.ExternalError("Sk.builtin.nmber is deprecated. Please replace with Sk.builtin.int_, Sk.builtin.float_, or Sk.builtin.assk$.");
};Sk.builtin.nmber.prototype.tp$index=function(){return this.v};Sk.builtin.nmber.prototype.tp$hash=function(){throw a;};Sk.builtin.nmber.fromInt$=function(b){throw a;};Sk.builtin.nmber.prototype.clone=function(){throw a;};Sk.builtin.nmber.prototype.toFixed=function(b){throw a;};Sk.builtin.nmber.prototype.nb$add=function(b){throw a;};Sk.builtin.nmber.prototype.nb$subtract=function(b){throw a;};Sk.builtin.nmber.prototype.nb$multiply=function(b){throw a;};Sk.builtin.nmber.prototype.nb$divide=function(b){throw a;
};Sk.builtin.nmber.prototype.nb$floor_divide=function(b){throw a;};Sk.builtin.nmber.prototype.nb$remainder=function(b){throw a;};Sk.builtin.nmber.prototype.nb$divmod=function(b){throw a;};Sk.builtin.nmber.prototype.nb$power=function(b){throw a;};Sk.builtin.nmber.prototype.nb$and=function(b){throw a;};Sk.builtin.nmber.prototype.nb$or=function(b){throw a;};Sk.builtin.nmber.prototype.nb$xor=function(b){throw a;};Sk.builtin.nmber.prototype.nb$lshift=function(b){throw a;};Sk.builtin.nmber.prototype.nb$rshift=
function(b){throw a;};Sk.builtin.nmber.prototype.nb$inplace_add=Sk.builtin.nmber.prototype.nb$add;Sk.builtin.nmber.prototype.nb$inplace_subtract=Sk.builtin.nmber.prototype.nb$subtract;Sk.builtin.nmber.prototype.nb$inplace_multiply=Sk.builtin.nmber.prototype.nb$multiply;Sk.builtin.nmber.prototype.nb$inplace_divide=Sk.builtin.nmber.prototype.nb$divide;Sk.builtin.nmber.prototype.nb$inplace_remainder=Sk.builtin.nmber.prototype.nb$remainder;Sk.builtin.nmber.prototype.nb$inplace_floor_divide=Sk.builtin.nmber.prototype.nb$floor_divide;
Sk.builtin.nmber.prototype.nb$inplace_power=Sk.builtin.nmber.prototype.nb$power;Sk.builtin.nmber.prototype.nb$inplace_and=Sk.builtin.nmber.prototype.nb$and;Sk.builtin.nmber.prototype.nb$inplace_or=Sk.builtin.nmber.prototype.nb$or;Sk.builtin.nmber.prototype.nb$inplace_xor=Sk.builtin.nmber.prototype.nb$xor;Sk.builtin.nmber.prototype.nb$inplace_lshift=Sk.builtin.nmber.prototype.nb$lshift;Sk.builtin.nmber.prototype.nb$inplace_rshift=Sk.builtin.nmber.prototype.nb$rshift;Sk.builtin.nmber.prototype.nb$negative=
function(){throw a;};Sk.builtin.nmber.prototype.nb$positive=function(){throw a;};Sk.builtin.nmber.prototype.nb$nonzero=function(){throw a;};Sk.builtin.nmber.prototype.nb$isnegative=function(){throw a;};Sk.builtin.nmber.prototype.nb$ispositive=function(){throw a;};Sk.builtin.nmber.prototype.numberCompare=function(b){throw a;};Sk.builtin.nmber.prototype.__eq__=function(b,c){throw a;};Sk.builtin.nmber.prototype.__ne__=function(b,c){throw a;};Sk.builtin.nmber.prototype.__lt__=function(b,c){throw a;};
Sk.builtin.nmber.prototype.__le__=function(b,c){throw a;};Sk.builtin.nmber.prototype.__gt__=function(b,c){throw a;};Sk.builtin.nmber.prototype.__ge__=function(b,c){throw a;};Sk.builtin.nmber.prototype.round$=function(b,c){throw a;};Sk.builtin.nmber.prototype.$r=function(){throw a;};Sk.builtin.nmber.prototype.tp$str=function(){throw a;};Sk.builtin.nmber.prototype.str$=function(b,c){throw a;};Sk.exportSymbol("Sk.builtin.nmber",Sk.builtin.nmber)},function(m,p){Sk.builtin.lng=function(a,b){b=Sk.builtin.asnum$(b);
if(!(this instanceof Sk.builtin.lng))return new Sk.builtin.lng(a,b);if(void 0===a)return this.biginteger=new Sk.builtin.biginteger(0),this;if(a instanceof Sk.builtin.lng)return this.biginteger=a.biginteger.clone(),this;if(a instanceof Sk.builtin.biginteger)return this.biginteger=a,this;if(a instanceof String||"string"===typeof a)return Sk.longFromStr(a,b);if(a instanceof Sk.builtin.str)return Sk.longFromStr(a.v,b);if(void 0!==a&&!Sk.builtin.checkString(a)&&!Sk.builtin.checkNumber(a))if(!0===a)a=1;
else if(!1===a)a=0;else throw new Sk.builtin.TypeError("long() argument must be a string or a number, not '"+Sk.abstr.typeName(a)+"'");a=Sk.builtin.asnum$nofloat(a);this.biginteger=new Sk.builtin.biginteger(a);return this};Sk.abstr.setUpInheritance("long",Sk.builtin.lng,Sk.builtin.numtype);Sk.builtin.lng.prototype.tp$index=function(){return parseInt(this.str$(10,!0),10)};Sk.builtin.lng.prototype.tp$hash=function(){return new Sk.builtin.int_(this.tp$index())};Sk.builtin.lng.prototype.nb$int_=function(){return this.cantBeInt()?
new Sk.builtin.lng(this):new Sk.builtin.int_(this.toInt$())};Sk.builtin.lng.prototype.round$=function(a,b){Sk.builtin.pyCheckArgsLen("__round__",arguments.length,1,2);if(void 0!==b&&!Sk.misceval.isIndex(b))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(b)+"' object cannot be interpreted as an index");var c=Sk.builtin.asnum$(a);var d=void 0===b?0:Sk.misceval.asIndex(b);if(Sk.__future__.bankers_rounding){c*=Math.pow(10,d);var e=Math.round(c);d=(.5===(0<c?c:-c)%1?0===e%2?e:e-1:e)/Math.pow(10,d)}else d=
Math.pow(10,d),d=Math.round(c*d)/d;return new Sk.builtin.lng(d)};Sk.builtin.lng.prototype.__index__=new Sk.builtin.func(function(a){return a.nb$int_(a)});Sk.builtin.lng.prototype.__format__=Sk.formatting.mkNumber__format__(!1);Sk.builtin.lng.prototype.nb$lng_=function(){return this};Sk.builtin.lng.prototype.nb$float_=function(){let a=Sk.builtin.asnum$(this);a=parseFloat(a);if(!isFinite(a))throw new Sk.builtin.OverflowError("int too large to convert to float");return new Sk.builtin.float_(a)};Sk.builtin.lng.MAX_INT$=
new Sk.builtin.lng(Sk.builtin.int_.threshold$);Sk.builtin.lng.MIN_INT$=new Sk.builtin.lng(-Sk.builtin.int_.threshold$);Sk.builtin.lng.prototype.cantBeInt=function(){return 0<this.longCompare(Sk.builtin.lng.MAX_INT$)||0>this.longCompare(Sk.builtin.lng.MIN_INT$)};Sk.builtin.lng.fromInt$=function(a){return new Sk.builtin.lng(a)};Sk.longFromStr=function(a,b){a=Sk.str2number(a,b,function(a,b){return 10===b?new Sk.builtin.biginteger(a):new Sk.builtin.biginteger(a,b)},function(a){return a.negate()},"long");
return new Sk.builtin.lng(a)};Sk.exportSymbol("Sk.longFromStr",Sk.longFromStr);Sk.builtin.lng.prototype.toInt$=function(){return parseInt(this.biginteger.toString(),10)};Sk.builtin.lng.prototype.clone=function(){return new Sk.builtin.lng(this)};Sk.builtin.lng.prototype.conjugate=new Sk.builtin.func(function(a){return a.clone()});Sk.builtin.lng.prototype.nb$add=function(a){if(a instanceof Sk.builtin.float_){var b=new Sk.builtin.float_(this.str$(10,!0));return b.nb$add(a)}a instanceof Sk.builtin.int_&&
(a=new Sk.builtin.lng(a.v));return a instanceof Sk.builtin.lng?new Sk.builtin.lng(this.biginteger.add(a.biginteger)):a instanceof Sk.builtin.biginteger?new Sk.builtin.lng(this.biginteger.add(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.lng.prototype.nb$reflected_add=function(a){return Sk.builtin.lng.prototype.nb$add.call(this,a)};Sk.builtin.lng.prototype.nb$inplace_add=Sk.builtin.lng.prototype.nb$add;Sk.builtin.lng.prototype.nb$subtract=function(a){if(a instanceof Sk.builtin.float_){var b=
new Sk.builtin.float_(this.str$(10,!0));return b.nb$subtract(a)}a instanceof Sk.builtin.int_&&(a=new Sk.builtin.lng(a.v));return a instanceof Sk.builtin.lng?new Sk.builtin.lng(this.biginteger.subtract(a.biginteger)):a instanceof Sk.builtin.biginteger?new Sk.builtin.lng(this.biginteger.subtract(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.lng.prototype.nb$reflected_subtract=function(a){var b=this.nb$negative();return Sk.builtin.lng.prototype.nb$add.call(b,a)};Sk.builtin.lng.prototype.nb$inplace_subtract=
Sk.builtin.lng.prototype.nb$subtract;Sk.builtin.lng.prototype.nb$multiply=function(a){if(a instanceof Sk.builtin.float_){var b=new Sk.builtin.float_(this.str$(10,!0));return b.nb$multiply(a)}a instanceof Sk.builtin.int_&&(a=new Sk.builtin.lng(a.v));return a instanceof Sk.builtin.lng?new Sk.builtin.lng(this.biginteger.multiply(a.biginteger)):a instanceof Sk.builtin.biginteger?new Sk.builtin.lng(this.biginteger.multiply(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.lng.prototype.nb$reflected_multiply=
function(a){return Sk.builtin.lng.prototype.nb$multiply.call(this,a)};Sk.builtin.lng.prototype.nb$inplace_multiply=Sk.builtin.lng.prototype.nb$multiply;Sk.builtin.lng.prototype.nb$divide=function(a){if(a instanceof Sk.builtin.float_){var b=new Sk.builtin.float_(this.str$(10,!0));return b.nb$divide(a)}a instanceof Sk.builtin.int_&&(a=new Sk.builtin.lng(a.v));if(a instanceof Sk.builtin.lng){b=this.nb$isnegative();var c=a.nb$isnegative();if(b&&!c||c&&!b){a=this.biginteger.divideAndRemainder(a.biginteger);
if(0===a[1].trueCompare(Sk.builtin.biginteger.ZERO))return new Sk.builtin.lng(a[0]);a=a[0].subtract(Sk.builtin.biginteger.ONE);return new Sk.builtin.lng(a)}return new Sk.builtin.lng(this.biginteger.divide(a.biginteger))}return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.lng.prototype.nb$reflected_divide=function(a){a instanceof Sk.builtin.int_&&(a=new Sk.builtin.lng(a.v));return a instanceof Sk.builtin.lng?a.nb$divide(this):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.lng.prototype.nb$floor_divide=
function(a){if(a instanceof Sk.builtin.float_){var b=new Sk.builtin.float_(this.str$(10,!0));return b.nb$floor_divide(a)}a instanceof Sk.builtin.int_&&(a=new Sk.builtin.lng(a.v));return a instanceof Sk.builtin.lng?a.nb$divide(this):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.lng.prototype.nb$divmod=function(a){a instanceof Sk.builtin.int_&&(a=new Sk.builtin.lng(a.v));return a instanceof Sk.builtin.lng?new Sk.builtin.tuple([this.nb$floor_divide(a),this.nb$remainder(a)]):Sk.builtin.NotImplemented.NotImplemented$};
Sk.builtin.lng.prototype.nb$reflected_divmod=function(a){a instanceof Sk.builtin.int_&&(a=new Sk.builtin.lng(a.v));return a instanceof Sk.builtin.lng?new Sk.builtin.tuple([a.nb$floor_divide(this),a.nb$remainder(this)]):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.lng.prototype.nb$inplace_divide=Sk.builtin.lng.prototype.nb$divide;Sk.builtin.lng.prototype.nb$floor_divide=Sk.builtin.lng.prototype.nb$divide;Sk.builtin.lng.prototype.nb$reflected_floor_divide=Sk.builtin.lng.prototype.nb$reflected_divide;
Sk.builtin.lng.prototype.nb$inplace_floor_divide=Sk.builtin.lng.prototype.nb$floor_divide;Sk.builtin.lng.prototype.nb$remainder=function(a){if(0===this.biginteger.trueCompare(Sk.builtin.biginteger.ZERO))return a instanceof Sk.builtin.float_?new Sk.builtin.float_(0):new Sk.builtin.lng(0);if(a instanceof Sk.builtin.float_){var b=new Sk.builtin.float_(this.str$(10,!0));return b.nb$remainder(a)}a instanceof Sk.builtin.int_&&(a=new Sk.builtin.lng(a.v));return a instanceof Sk.builtin.lng?(b=new Sk.builtin.lng(this.biginteger.remainder(a.biginteger)),
this.nb$isnegative()?a.nb$ispositive()&&b.nb$nonzero()&&(b=b.nb$add(a).nb$remainder(a)):a.nb$isnegative()&&b.nb$nonzero()&&(b=b.nb$add(a)),b):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.lng.prototype.nb$reflected_remainder=function(a){a instanceof Sk.builtin.int_&&(a=new Sk.builtin.lng(a.v));return a instanceof Sk.builtin.lng?a.nb$remainder(this):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.lng.prototype.nb$inplace_remainder=Sk.builtin.lng.prototype.nb$remainder;Sk.builtin.lng.prototype.nb$divmod=
function(a){a===Sk.builtin.bool.true$&&(a=new Sk.builtin.lng(1));a===Sk.builtin.bool.false$&&(a=new Sk.builtin.lng(0));a instanceof Sk.builtin.int_&&(a=new Sk.builtin.lng(a.v));if(a instanceof Sk.builtin.lng)return new Sk.builtin.tuple([this.nb$floor_divide(a),this.nb$remainder(a)]);if(a instanceof Sk.builtin.float_){var b=new Sk.builtin.float_(this.str$(10,!0));return b.nb$divmod(a)}return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.lng.prototype.nb$power=function(a,b){if(void 0!==b)return a=
new Sk.builtin.biginteger(Sk.builtin.asnum$(a)),b=new Sk.builtin.biginteger(Sk.builtin.asnum$(b)),new Sk.builtin.lng(this.biginteger.modPowInt(a,b));if(a instanceof Sk.builtin.float_||a instanceof Sk.builtin.int_&&0>a.v)return b=new Sk.builtin.float_(this.str$(10,!0)),b.nb$power(a);a instanceof Sk.builtin.int_&&(a=new Sk.builtin.lng(a.v));return a instanceof Sk.builtin.lng?void 0!==b?(a=new Sk.builtin.biginteger(Sk.builtin.asnum$(a)),b=new Sk.builtin.biginteger(Sk.builtin.asnum$(b)),new Sk.builtin.lng(this.biginteger.modPowInt(a,
b))):a.nb$isnegative()?(b=new Sk.builtin.float_(this.str$(10,!0)),b.nb$power(a)):new Sk.builtin.lng(this.biginteger.pow(a.biginteger)):a instanceof Sk.builtin.biginteger?void 0!==b?(b=new Sk.builtin.biginteger(Sk.builtin.asnum$(b)),new Sk.builtin.lng(this.biginteger.modPowInt(a,b))):a.isnegative()?(b=new Sk.builtin.float_(this.str$(10,!0)),b.nb$power(a)):new Sk.builtin.lng(this.biginteger.pow(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.lng.prototype.nb$reflected_power=function(a,b){a instanceof
Sk.builtin.int_&&(a=new Sk.builtin.lng(a.v));return a instanceof Sk.builtin.lng?a.nb$power(this,b):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.lng.prototype.nb$inplace_power=Sk.builtin.lng.prototype.nb$power;Sk.builtin.lng.prototype.nb$abs=function(){return new Sk.builtin.lng(this.biginteger.bnAbs())};Sk.builtin.lng.prototype.nb$lshift=function(a){a instanceof Sk.builtin.int_&&(a=new Sk.builtin.lng(a.v));if(a instanceof Sk.builtin.lng){if(0>a.biginteger.signum())throw new Sk.builtin.ValueError("negative shift count");
return new Sk.builtin.lng(this.biginteger.shiftLeft(a.biginteger))}if(a instanceof Sk.builtin.biginteger){if(0>a.signum())throw new Sk.builtin.ValueError("negative shift count");return new Sk.builtin.lng(this.biginteger.shiftLeft(a))}return Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.lng.prototype.nb$reflected_lshift=function(a){a instanceof Sk.builtin.int_&&(a=new Sk.builtin.lng(a.v));return a instanceof Sk.builtin.lng?a.nb$lshift(this):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.lng.prototype.nb$inplace_lshift=
Sk.builtin.lng.prototype.nb$lshift;Sk.builtin.lng.prototype.nb$rshift=function(a){a instanceof Sk.builtin.int_&&(a=new Sk.builtin.lng(a.v));if(a instanceof Sk.builtin.lng){if(0>a.biginteger.signum())throw new Sk.builtin.ValueError("negative shift count");return new Sk.builtin.lng(this.biginteger.shiftRight(a.biginteger))}if(a instanceof Sk.builtin.biginteger){if(0>a.signum())throw new Sk.builtin.ValueError("negative shift count");return new Sk.builtin.lng(this.biginteger.shiftRight(a))}return Sk.builtin.NotImplemented.NotImplemented$};
Sk.builtin.lng.prototype.nb$reflected_rshift=function(a){a instanceof Sk.builtin.int_&&(a=new Sk.builtin.lng(a.v));return a instanceof Sk.builtin.lng?a.nb$rshift(this):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.lng.prototype.nb$inplace_rshift=Sk.builtin.lng.prototype.nb$rshift;Sk.builtin.lng.prototype.nb$and=function(a){a instanceof Sk.builtin.int_&&(a=new Sk.builtin.lng(a.v));return a instanceof Sk.builtin.lng?new Sk.builtin.lng(this.biginteger.and(a.biginteger)):a instanceof Sk.builtin.biginteger?
new Sk.builtin.lng(this.biginteger.and(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.lng.prototype.nb$reflected_and=Sk.builtin.lng.prototype.nb$and;Sk.builtin.lng.prototype.nb$inplace_and=Sk.builtin.lng.prototype.nb$and;Sk.builtin.lng.prototype.nb$or=function(a){a instanceof Sk.builtin.int_&&(a=new Sk.builtin.lng(a.v));return a instanceof Sk.builtin.lng?new Sk.builtin.lng(this.biginteger.or(a.biginteger)):a instanceof Sk.builtin.biginteger?new Sk.builtin.lng(this.biginteger.or(a)):Sk.builtin.NotImplemented.NotImplemented$};
Sk.builtin.lng.prototype.nb$reflected_or=Sk.builtin.lng.prototype.nb$or;Sk.builtin.lng.prototype.nb$inplace_or=Sk.builtin.lng.prototype.nb$or;Sk.builtin.lng.prototype.nb$xor=function(a){a instanceof Sk.builtin.int_&&(a=new Sk.builtin.lng(a.v));return a instanceof Sk.builtin.lng?new Sk.builtin.lng(this.biginteger.xor(a.biginteger)):a instanceof Sk.builtin.biginteger?new Sk.builtin.lng(this.biginteger.xor(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.lng.prototype.nb$reflected_xor=Sk.builtin.lng.prototype.nb$xor;
Sk.builtin.lng.prototype.nb$inplace_xor=Sk.builtin.lng.prototype.nb$xor;Sk.builtin.lng.prototype.nb$negative=function(){return new Sk.builtin.lng(this.biginteger.negate())};Sk.builtin.lng.prototype.nb$invert=function(){return new Sk.builtin.lng(this.biginteger.not())};Sk.builtin.lng.prototype.nb$positive=function(){return this.clone()};Sk.builtin.lng.prototype.nb$nonzero=function(){return 0!==this.biginteger.trueCompare(Sk.builtin.biginteger.ZERO)};Sk.builtin.lng.prototype.nb$isnegative=function(){return this.biginteger.isnegative()};
Sk.builtin.lng.prototype.nb$ispositive=function(){return!this.biginteger.isnegative()};Sk.builtin.lng.prototype.longCompare=function(a){"number"===typeof a&&(a=new Sk.builtin.lng(a));if(a instanceof Sk.builtin.int_||a instanceof Sk.builtin.float_&&0===a.v%1)return a=new Sk.builtin.lng(a.v),this.longCompare(a);if(a instanceof Sk.builtin.float_){var b=new Sk.builtin.float_(this);return b.numberCompare(a)}return a instanceof Sk.builtin.lng?this.biginteger.subtract(a.biginteger):a instanceof Sk.builtin.biginteger?
this.biginteger.subtract(a):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.lng.prototype.ob$eq=function(a){return a instanceof Sk.builtin.int_||a instanceof Sk.builtin.lng||a instanceof Sk.builtin.float_?new Sk.builtin.bool(0==this.longCompare(a)):a===Sk.builtin.none.none$?Sk.builtin.bool.false$:Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.lng.prototype.ob$ne=function(a){return a instanceof Sk.builtin.int_||a instanceof Sk.builtin.lng||a instanceof Sk.builtin.float_?new Sk.builtin.bool(0!=
this.longCompare(a)):a===Sk.builtin.none.none$?Sk.builtin.bool.true$:Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.lng.prototype.ob$lt=function(a){return a instanceof Sk.builtin.int_||a instanceof Sk.builtin.lng||a instanceof Sk.builtin.float_?new Sk.builtin.bool(0>this.longCompare(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.lng.prototype.ob$le=function(a){return a instanceof Sk.builtin.int_||a instanceof Sk.builtin.lng||a instanceof Sk.builtin.float_?new Sk.builtin.bool(0>=
this.longCompare(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.lng.prototype.ob$gt=function(a){return a instanceof Sk.builtin.int_||a instanceof Sk.builtin.lng||a instanceof Sk.builtin.float_?new Sk.builtin.bool(0<this.longCompare(a)):Sk.builtin.NotImplemented.NotImplemented$};Sk.builtin.lng.prototype.ob$ge=function(a){return a instanceof Sk.builtin.int_||a instanceof Sk.builtin.lng||a instanceof Sk.builtin.float_?new Sk.builtin.bool(0<=this.longCompare(a)):Sk.builtin.NotImplemented.NotImplemented$};
Sk.builtin.lng.prototype.$r=function(){var a=Sk.__future__.python3?"":"L";return new Sk.builtin.str(this.str$(10,!0)+a)};Sk.builtin.lng.prototype.tp$str=function(){return new Sk.builtin.str(this.str$(10,!0))};Sk.builtin.lng.prototype.str$=function(a,b){void 0===b&&(b=!0);b=b?this.biginteger:this.biginteger.abs();return void 0===a||10===a?b.toString():b.toString(a)}},function(m,p){Math.hypot=Math.hypot||function(){for(var a=0,b=arguments.length,e=0;e<b;e++){if(Infinity===arguments[e]||-Infinity===
arguments[e])return Infinity;a+=arguments[e]*arguments[e]}return Math.sqrt(a)};Sk.builtin.complex=function(a,b){Sk.builtin.pyCheckArgsLen("complex",arguments.length,0,2);var c,d=!1,g=!1;if(!(this instanceof Sk.builtin.complex))return new Sk.builtin.complex(a,b);if("number"===typeof a&&"number"===typeof b)return this.real=a,this.imag=b,this;var f=null==a?Sk.builtin.bool.false$:a;if(f instanceof Sk.builtin.complex&&null==b)return a;if(null!=f&&Sk.builtin.checkString(f)){if(null!=b)throw new Sk.builtin.TypeError("complex() can't take second arg if first is a string");
return Sk.builtin.complex.complex_subtype_from_string(f)}if(null!=b&&Sk.builtin.checkString(b))throw new Sk.builtin.TypeError("complex() second arg can't be a string");var k=Sk.builtin.complex.try_complex_special_method(f);if(null!=k&&k!==Sk.builtin.NotImplemented.NotImplemented$){if(!Sk.builtin.checkComplex(k))throw new Sk.builtin.TypeError("__complex__ should return a complex object");f=k}k=Sk.builtin.asnum$(f);null!=b&&(c=Sk.builtin.asnum$(b));var n=function(a){if(Sk.builtin.checkNumber(a)||void 0!==
Sk.builtin.type.typeLookup(a.ob$type,Sk.builtin.str.$float_))return!0};if(null==k||!n(f)&&!Sk.builtin.checkComplex(f)||null!=b&&(null==c||!n(b)&&!Sk.builtin.checkComplex(b)))throw new Sk.builtin.TypeError("complex() argument must be a string or number");if(Sk.builtin.complex._complex_check(f))c=f.real,f=f.imag,d=!0;else{k=Sk.builtin.float_.PyFloat_AsDouble(f);if(null==k)return null;c=k;f=0}if(null==b)k=0;else if(Sk.builtin.complex._complex_check(b)){k=b.real;var l=b.imag;g=!0}else{k=Sk.builtin.float_.PyFloat_AsDouble(b);
if(null==k)return null;l=0}!0===g&&(c-=l);!0===d&&(k+=f);this.real=c;this.imag=k;return this};Sk.abstr.setUpInheritance("complex",Sk.builtin.complex,Sk.builtin.numtype);Sk.builtin.complex.prototype.__class__=Sk.builtin.complex;Sk.builtin.complex.prototype.nb$int_=function(){throw new Sk.builtin.TypeError("can't convert complex to int");};Sk.builtin.complex.prototype.nb$float_=function(){throw new Sk.builtin.TypeError("can't convert complex to float");};Sk.builtin.complex.prototype.nb$lng=function(){throw new Sk.builtin.TypeError("can't convert complex to long");
};Sk.builtin.complex.prototype.__doc__=new Sk.builtin.str("complex(real[, imag]) -> complex number\n\nCreate a complex number from a real part and an optional imaginary part.\nThis is equivalent to (real + imag*1j) where imag defaults to 0.");Sk.builtin.complex._isNegativeZero=function(a){return 0!==a?!1:-Infinity===1/a};Sk.builtin.complex.try_complex_special_method=function(a){if(null==a)return null;var c=Sk.abstr.lookupSpecial(a,Sk.builtin.str.$complex);return null!=c?a=Sk.misceval.callsimArray(c,
[a]):null};Sk.builtin.complex.check_number_or_complex=function(a){if(!Sk.builtin.checkNumber(a)&&"complex"!==a.tp$name)throw new Sk.builtin.TypeError("unsupported operand type(s) for +: 'complex' and '"+Sk.abstr.typeName(a)+"'");Sk.builtin.checkNumber(a)&&(a=new Sk.builtin.complex(a));return a};const a=/_[eE]|[eE]_|\._|_\.|[+-]_|_j|j_/,b=/_(?=[^_])/g;Sk.builtin.complex.complex_subtype_from_string=function(c){var d=0,e=0,h=!1;if(Sk.builtin.checkString(c))c=Sk.ffi.remapToJs(c);else if("string"!==typeof c)throw new TypeError("provided unsupported string-alike argument");
if(-1!==c.indexOf("\x00")||0===c.length||""===c)throw new Sk.builtin.ValueError("complex() arg is a malformed string");var g=0;c=c.replace(/inf|infinity/gi,"Infinity");for(c=c.replace(/nan/gi,"NaN");" "===c[g];)g++;if("("===c[g])for(h=!0,g++;" "===c[g];)g++;if(-1!==c.indexOf("_")){if(a.test(c))throw new Sk.builtin.ValueError("could not convert string to complex: '"+c+"'");c=c.charAt(0)+c.substring(1).replace(b,"")}var f=/^(?:[+-]?(?:(?:(?:\d*\.\d+)|(?:\d+\.?))(?:[eE][+-]?\d+)?|NaN|Infinity))/;var k=
c.substr(g);var n=k.match(f);if(null!==n)if(g+=n[0].length,"j"===c[g]||"J"===c[g])e=parseFloat(n[0]),g++;else if("+"===c[g]||"-"===c[g]){d=parseFloat(n[0]);n=c.substr(g).match(f);null!==n?(e=parseFloat(n[0]),g+=n[0].length):(e="+"===c[g]?1:-1,g++);if("j"!==c[g]&&"J"!==c[g])throw new Sk.builtin.ValueError("complex() arg is malformed string");g++}else d=parseFloat(n[0]);else n=n=k.match(/^([+-]?[jJ])/),null!==n&&(e=1===n[0].length?1:"+"===n[0][0]?1:-1,g+=n[0].length);for(;" "===c[g];)g++;if(h){if(")"!==
c[g])throw new Sk.builtin.ValueError("complex() arg is malformed string");for(g++;" "===c[g];)g++}if(c.length!==g)throw new Sk.builtin.ValueError("complex() arg is malformed string");return new Sk.builtin.complex(d,e)};Sk.builtin.complex.prototype.tp$hash=function(){return new Sk.builtin.int_(1000003*this.imag+this.real)};Sk.builtin.complex.prototype.nb$add=function(a){a=Sk.builtin.complex.check_number_or_complex(a);return new Sk.builtin.complex(this.real+a.real,this.imag+a.imag)};Sk.builtin.complex.prototype.nb$reflected_add=
Sk.builtin.complex.prototype.nb$add;Sk.builtin.complex._c_diff=function(a,b){return new Sk.builtin.complex(a.real-b.real,a.imag-b.imag)};Sk.builtin.complex.prototype.nb$subtract=function(a){var c=Sk.builtin.complex.check_number_or_complex(this);a=Sk.builtin.complex.check_number_or_complex(a);return Sk.builtin.complex._c_diff(c,a)};Sk.builtin.complex.prototype.nb$reflected_subtract=function(a){return this.nb$negative().nb$add(a)};Sk.builtin.complex.prototype.nb$multiply=function(a){a=Sk.builtin.complex.check_number_or_complex(a);
return new Sk.builtin.complex(this.real*a.real-this.imag*a.imag,this.real*a.imag+this.imag*a.real)};Sk.builtin.complex.prototype.nb$reflected_multiply=Sk.builtin.complex.prototype.nb$multiply;Sk.builtin.complex.prototype.nb$divide=function(a){a=Sk.builtin.complex.check_number_or_complex(a);var c=a.real;var b=a.imag;a=this.real;var h=this.imag;var g=Math.abs(c);var f=Math.abs(b);if(g>=f){if(0===g)throw new Sk.builtin.ZeroDivisionError("complex division by zero");g=b/c;f=c+b*g;c=(a+h*g)/f;a=(h-a*g)/
f}else f>=g?(g=c/b,f=c*g+b,Sk.asserts.assert(0!==b),c=(a*g+h)/f,a=(h*g-a)/f):a=c=NaN;return new Sk.builtin.complex(c,a)};Sk.builtin.complex.prototype.nb$reflected_divide=function(a){a=Sk.builtin.complex.check_number_or_complex(a);return a.nb$divide(this)};Sk.builtin.complex.prototype.nb$floor_divide=function(a){throw new Sk.builtin.TypeError("can't take floor of complex number.");};Sk.builtin.complex.prototype.nb$remainder=function(a){throw new Sk.builtin.TypeError("can't mod complex numbers.");};
Sk.builtin.complex.prototype.nb$power=function(a,b){if(null!=b&&!Sk.builtin.checkNone(b))throw new Sk.builtin.ValueError("complex modulo");b=Sk.builtin.complex.check_number_or_complex(a);a=b.real|0;return 0===b.imag&&b.real===a?Sk.builtin.complex.c_powi(this,a):Sk.builtin.complex.c_pow(this,b)};Sk.builtin.complex.c_pow=function(a,b){var c=b.real;b=b.imag;var d=a.real;var g=a.imag;if(0===c&&0===b)b=1,a=0;else if(0===d&&0===g){if(0!==b||0>c)throw new Sk.builtin.ZeroDivisionError("complex division by zero");
a=b=0}else{var f=Math.hypot(d,g);a=Math.pow(f,c);d=Math.atan2(g,d);c*=d;0!==b&&(a/=Math.exp(d*b),c+=b*Math.log(f));b=a*Math.cos(c);a*=Math.sin(c)}return new Sk.builtin.complex(b,a)};Sk.builtin.complex.c_powi=function(a,b){return 100<b||-100>b?(b=new Sk.builtin.complex(b,0),Sk.builtin.complex.c_pow(a,b)):0<b?Sk.builtin.complex.c_powu(a,b):(new Sk.builtin.complex(1,0)).nb$divide(Sk.builtin.complex.c_powu(a,-b))};Sk.builtin.complex.c_powu=function(a,b){var c,d=1;for(c=new Sk.builtin.complex(1,0);0<d&&
b>=d;)b&d&&(c=c.nb$multiply(a)),d<<=1,a=a.nb$multiply(a);return c};Sk.builtin.complex.prototype.nb$inplace_add=Sk.builtin.complex.prototype.nb$add;Sk.builtin.complex.prototype.nb$inplace_subtract=Sk.builtin.complex.prototype.nb$subtract;Sk.builtin.complex.prototype.nb$inplace_multiply=Sk.builtin.complex.prototype.nb$multiply;Sk.builtin.complex.prototype.nb$inplace_divide=Sk.builtin.complex.prototype.nb$divide;Sk.builtin.complex.prototype.nb$inplace_remainder=Sk.builtin.complex.prototype.nb$remainder;
Sk.builtin.complex.prototype.nb$inplace_floor_divide=Sk.builtin.complex.prototype.nb$floor_divide;Sk.builtin.complex.prototype.nb$inplace_power=Sk.builtin.complex.prototype.nb$power;Sk.builtin.complex.prototype.nb$negative=function(){return new Sk.builtin.complex(-this.real,-this.imag)};Sk.builtin.complex.prototype.nb$positive=function(){return Sk.builtin.complex.check_number_or_complex(this)};Sk.builtin.complex._complex_check=function(a){return void 0===a?!1:a instanceof Sk.builtin.complex?!0:!1};
Sk.builtin.complex.prototype.tp$richcompare=function(a,b){if("Eq"!==b&&"NotEq"!==b){if(Sk.builtin.checkNumber(a)||Sk.builtin.complex._complex_check(a))throw new Sk.builtin.TypeError("no ordering relation is defined for complex numbers");return Sk.builtin.NotImplemented.NotImplemented$}var c=Sk.builtin.complex.check_number_or_complex(this);var d=c.real;c=c.imag;if(Sk.builtin.checkInt(a)){if(0===c)return a=Sk.misceval.richCompareBool(new Sk.builtin.float_(d),a,b),b=new Sk.builtin.bool(a);a=!1}else if(Sk.builtin.checkFloat(a))a=
d===Sk.builtin.float_.PyFloat_AsDouble(a)&&0===c;else if(Sk.builtin.complex._complex_check(a)){var g=a.imag;a=d===a.real&&c===g}else return Sk.builtin.NotImplemented.NotImplemented$;"NotEq"===b&&(a=!a);return b=new Sk.builtin.bool(a)};Sk.builtin.complex.prototype.__eq__=function(a,b){return Sk.builtin.complex.prototype.tp$richcompare.call(a,b,"Eq")};Sk.builtin.complex.prototype.__ne__=function(a,b){return Sk.builtin.complex.prototype.tp$richcompare.call(a,b,"NotEq")};Sk.builtin.complex.prototype.__lt__=
function(a,b){throw new Sk.builtin.TypeError("unorderable types: "+Sk.abstr.typeName(a)+" < "+Sk.abstr.typeName(b));};Sk.builtin.complex.prototype.__le__=function(a,b){throw new Sk.builtin.TypeError("unorderable types: "+Sk.abstr.typeName(a)+" <= "+Sk.abstr.typeName(b));};Sk.builtin.complex.prototype.__gt__=function(a,b){throw new Sk.builtin.TypeError("unorderable types: "+Sk.abstr.typeName(a)+" > "+Sk.abstr.typeName(b));};Sk.builtin.complex.prototype.__ge__=function(a,b){throw new Sk.builtin.TypeError("unorderable types: "+
Sk.abstr.typeName(a)+" >= "+Sk.abstr.typeName(b));};Sk.builtin.complex.prototype.__float__=function(a){throw new Sk.builtin.TypeError("can't convert complex to float");};Sk.builtin.complex.prototype.__int__=function(a){throw new Sk.builtin.TypeError("can't convert complex to int");};Sk.builtin.complex.prototype.$internalGenericGetAttr=Sk.builtin.object.prototype.GenericGetAttr;Sk.builtin.complex.prototype.tp$getattr=function(a){return a===Sk.builtin.str.$real||a===Sk.builtin.str.$imag?new Sk.builtin.float_(this[a.$jsstr()]):
this.$internalGenericGetAttr(a)};Sk.builtin.complex.prototype.tp$setattr=function(a,b){if(a===Sk.builtin.str.$real||a===Sk.builtin.str.$imag)throw new Sk.builtin.AttributeError("readonly attribute");return Sk.builtin.object.prototype.tp$setattr.call(this,a,b)};Sk.builtin.complex.complex_format=function(a,b,e){if(null==a||!Sk.builtin.complex._complex_check(a))throw Error("Invalid internal method call: Sk.complex.complex_format() called with invalid value type.");var c,d,f=c="";if(d=0===a.real)d=a.real,
d=1==(d?0>d?-1:1:0>1/d?-1:1);d?(d="",b=Sk.builtin.complex.PyOS_double_to_string(a.imag,e,b,0,null)):(d=c=Sk.builtin.complex.PyOS_double_to_string(a.real,e,b,0,null),b=Sk.builtin.complex.PyOS_double_to_string(a.imag,e,b,Sk.builtin.complex.PyOS_double_to_string.Py_DTSF_SIGN,null),0===a.imag&&-Infinity===1/a.imag&&b&&"-"!==b[0]&&(b="-"+b),c="(",f=")");return new Sk.builtin.str(""+c+d+b+"j"+f)};Sk.builtin.complex.prototype.$r=function(){return Sk.builtin.complex.complex_format(this,null,"g")};Sk.builtin.complex.prototype.tp$str=
function(){return Sk.builtin.complex.complex_format(this,null,"g")};Sk.builtin.complex.prototype.int$format=function(a,b){if(null==b)return null;if(Sk.builtin.checkString(b))return a=Sk.builtin.complex._PyComplex_FormatAdvanced(a,b);throw new Sk.builtin.TypeError("__format__ requires str or unicode");};Sk.builtin.complex.prototype.int$format.co_name=new Sk.builtin.str("__format__");Sk.builtin.complex.prototype.__format__=new Sk.builtin.func(Sk.builtin.complex.prototype.int$format);Sk.builtin.complex._PyComplex_FormatAdvanced=
function(a,b){throw new Sk.builtin.NotImplementedError("__format__ is not implemented for complex type.");};Sk.builtin.complex._is_finite=function(a){return!isNaN(a)&&Infinity!==a&&-Infinity!==a};Sk.builtin.complex._is_infinity=function(a){return Infinity===a||-Infinity===a};Sk.builtin.complex.prototype.nb$abs=function(){var a=this.real;var b=this.imag;if(!Sk.builtin.complex._is_finite(a)||!Sk.builtin.complex._is_finite(b))return Sk.builtin.complex._is_infinity(a)?(a=Math.abs(a),new Sk.builtin.float_(a)):
Sk.builtin.complex._is_infinity(b)?(a=Math.abs(b),new Sk.builtin.float_(a)):new Sk.builtin.float_(NaN);a=Math.hypot(a,b);if(!Sk.builtin.complex._is_finite(a))throw new Sk.builtin.OverflowError("absolute value too large");return new Sk.builtin.float_(a)};Sk.builtin.complex.prototype.__abs__=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("__abs__",arguments.length,0,0,!1,!0);return a.nb$abs()});Sk.builtin.complex.prototype.int$bool=function(a){return new Sk.builtin.bool(a.real||a.imag)};
Sk.builtin.complex.prototype.int$bool.co_name=new Sk.builtin.str("__bool__");Sk.builtin.complex.prototype.__bool__=new Sk.builtin.func(Sk.builtin.complex.prototype.int$bool);Sk.builtin.complex.prototype.int$truediv=function(a,b){Sk.builtin.pyCheckArgsLen("__truediv__",arguments.length,1,1,!0);return a.nb$divide.call(a,b)};Sk.builtin.complex.prototype.int$truediv.co_name=new Sk.builtin.str("__truediv__");Sk.builtin.complex.prototype.__truediv__=new Sk.builtin.func(Sk.builtin.complex.prototype.int$truediv);
Sk.builtin.complex.prototype.int$hash=function(a){Sk.builtin.pyCheckArgsLen("__hash__",arguments.length,0,0,!0);return a.tp$hash.call(a)};Sk.builtin.complex.prototype.int$hash.co_name=new Sk.builtin.str("__hash__");Sk.builtin.complex.prototype.__hash__=new Sk.builtin.func(Sk.builtin.complex.prototype.int$hash);Sk.builtin.complex.prototype.int$add=function(a,b){Sk.builtin.pyCheckArgsLen("__add__",arguments.length,1,1,!0);return a.nb$add.call(a,b)};Sk.builtin.complex.prototype.int$add.co_name=new Sk.builtin.str("__add__");
Sk.builtin.complex.prototype.__add__=new Sk.builtin.func(Sk.builtin.complex.prototype.int$add);Sk.builtin.complex.prototype.int$repr=function(a){Sk.builtin.pyCheckArgsLen("__repr__",arguments.length,0,0,!0);return a.r$.call(a)};Sk.builtin.complex.prototype.int$repr.co_name=new Sk.builtin.str("__repr__");Sk.builtin.complex.prototype.__repr__=new Sk.builtin.func(Sk.builtin.complex.prototype.int$repr);Sk.builtin.complex.prototype.int$str=function(a){Sk.builtin.pyCheckArgsLen("__str__",arguments.length,
0,0,!0);return a.tp$str.call(a)};Sk.builtin.complex.prototype.int$str.co_name=new Sk.builtin.str("__str__");Sk.builtin.complex.prototype.__str__=new Sk.builtin.func(Sk.builtin.complex.prototype.int$str);Sk.builtin.complex.prototype.int$sub=function(a,b){Sk.builtin.pyCheckArgsLen("__sub__",arguments.length,1,1,!0);return a.nb$subtract.call(a,b)};Sk.builtin.complex.prototype.int$sub.co_name=new Sk.builtin.str("__sub__");Sk.builtin.complex.prototype.__sub__=new Sk.builtin.func(Sk.builtin.complex.prototype.int$sub);
Sk.builtin.complex.prototype.int$mul=function(a,b){Sk.builtin.pyCheckArgsLen("__mul__",arguments.length,1,1,!0);return a.nb$multiply.call(a,b)};Sk.builtin.complex.prototype.int$mul.co_name=new Sk.builtin.str("__mul__");Sk.builtin.complex.prototype.__mul__=new Sk.builtin.func(Sk.builtin.complex.prototype.int$mul);Sk.builtin.complex.prototype.int$div=function(a,b){Sk.builtin.pyCheckArgsLen("__div__",arguments.length,1,1,!0);return a.nb$divide.call(a,b)};Sk.builtin.complex.prototype.int$div.co_name=
new Sk.builtin.str("__div__");Sk.builtin.complex.prototype.__div__=new Sk.builtin.func(Sk.builtin.complex.prototype.int$div);Sk.builtin.complex.prototype.int$floordiv=function(a,b){Sk.builtin.pyCheckArgsLen("__floordiv__",arguments.length,1,1,!0);return a.nb$floor_divide.call(a,b)};Sk.builtin.complex.prototype.int$floordiv.co_name=new Sk.builtin.str("__floordiv__");Sk.builtin.complex.prototype.__floordiv__=new Sk.builtin.func(Sk.builtin.complex.prototype.int$floordiv);Sk.builtin.complex.prototype.int$mod=
function(a,b){Sk.builtin.pyCheckArgsLen("__mod__",arguments.length,1,1,!0);return a.nb$remainder.call(a,b)};Sk.builtin.complex.prototype.int$mod.co_name=new Sk.builtin.str("__mod__");Sk.builtin.complex.prototype.__mod__=new Sk.builtin.func(Sk.builtin.complex.prototype.int$mod);Sk.builtin.complex.prototype.int$pow=function(a,b,e){Sk.builtin.pyCheckArgsLen("__pow__",arguments.length,1,2,!0);return a.nb$power.call(a,b,e)};Sk.builtin.complex.prototype.int$pow.co_name=new Sk.builtin.str("__pow__");Sk.builtin.complex.prototype.__pow__=
new Sk.builtin.func(Sk.builtin.complex.prototype.int$pow);Sk.builtin.complex.prototype.int$neg=function(a){Sk.builtin.pyCheckArgsLen("__neg__",arguments.length,0,0,!0);return a.nb$negative.call(a)};Sk.builtin.complex.prototype.__neg__=new Sk.builtin.func(Sk.builtin.complex.prototype.int$neg);Sk.builtin.complex.prototype.int$pos=function(a){Sk.builtin.pyCheckArgsLen("__pos__",arguments.length,0,0,!0);return a.nb$positive.call(a)};Sk.builtin.complex.prototype.int$pos.co_name=new Sk.builtin.str("__pos__");
Sk.builtin.complex.prototype.__pos__=new Sk.builtin.func(Sk.builtin.complex.prototype.int$pos);Sk.builtin.complex.prototype.int$conjugate=function(a){Sk.builtin.pyCheckArgsLen("conjugate",arguments.length,0,0,!0);return new Sk.builtin.complex(a.real,-a.imag)};Sk.builtin.complex.prototype.int$conjugate.co_name=new Sk.builtin.str("conjugate");Sk.builtin.complex.prototype.conjugate=new Sk.builtin.func(Sk.builtin.complex.prototype.int$conjugate);Sk.builtin.complex.prototype.int$divmod=function(a,b){Sk.builtin.pyCheckArgsLen("__divmod__",
arguments.length,1,1,!0);var c=Sk.builtin.complex.check_number_or_complex(a);var d=Sk.builtin.complex.check_number_or_complex(b);var g=c.nb$divide.call(c,d);g.real=Math.floor(g.real);g.imag=0;c=c.nb$subtract.call(c,d.nb$multiply.call(d,g));return new Sk.builtin.tuple([g,c])};Sk.builtin.complex.prototype.int$divmod.co_name=new Sk.builtin.str("__divmod__");Sk.builtin.complex.prototype.__divmod__=new Sk.builtin.func(Sk.builtin.complex.prototype.int$divmod);Sk.builtin.complex.prototype.int$getnewargs=
function(a){Sk.builtin.pyCheckArgsLen("__getnewargs__",arguments.length,0,0,!0);return new Sk.builtin.tuple([new Sk.builtin.float_(a.real),new Sk.builtin.float_(a.imag)])};Sk.builtin.complex.prototype.int$getnewargs.co_name=new Sk.builtin.str("__getnewargs__");Sk.builtin.complex.prototype.__getnewargs__=new Sk.builtin.func(Sk.builtin.complex.prototype.int$getnewargs);Sk.builtin.complex.prototype.int$nonzero=function(a){Sk.builtin.pyCheckArgsLen("__nonzero__",arguments.length,0,0,!0);return 0!==a.real||
0!==a.imag?Sk.builtin.bool.true$:Sk.builtin.bool.false$};Sk.builtin.complex.prototype.int$nonzero.co_name=new Sk.builtin.str("__nonzero__");Sk.builtin.complex.prototype.__nonzero__=new Sk.builtin.func(Sk.builtin.complex.prototype.int$nonzero);Sk.builtin.complex.prototype.nb$bool=function(){return new Sk.builtin.bool(this.real||this.imag)};Sk.builtin.complex.prototype.nb$nonzero=function(){return new Sk.builtin.bool(this.real||this.imag)};Sk.exportSymbol("Sk.builtin.complex",Sk.builtin.complex);Sk.builtin.complex.PyOS_double_to_string=
function(a,b,e,h,g){g=!1;switch(b){case "e":case "f":case "g":break;case "E":g=!0;b="e";break;case "F":g=!0;b="f";break;case "r":if(0!==e)throw Error("Bad internall call");e=17;b="g";break;default:throw Error("Bad internall call");}if(isNaN(a))a="nan";else if(Infinity===a)a="inf";else if(-Infinity===a)a="-inf";else{h&Sk.builtin.complex.PyOS_double_to_string.Py_DTSF_ADD_DOT_0&&(b="g");var c="%"+(h&Sk.builtin.complex.PyOS_double_to_string.Py_DTSF_ALT?"#":"");null!=e&&(c=c+"."+e);c=new Sk.builtin.str(c+
b);a=c.nb$remainder(new Sk.builtin.float_(a));a=a.v}h&Sk.builtin.complex.PyOS_double_to_string.Py_DTSF_SIGN&&"-"!==a[0]&&(a="+"+a);g&&(a=a.toUpperCase());return a};Sk.builtin.complex.PyOS_double_to_string.Py_DTSF_SIGN=1;Sk.builtin.complex.PyOS_double_to_string.Py_DTSF_ADD_DOT_0=2;Sk.builtin.complex.PyOS_double_to_string.Py_DTSF_ALT=4;Sk.builtin.complex.PyOS_double_to_string.Py_DTST_FINITE=0;Sk.builtin.complex.PyOS_double_to_string.Py_DTST_INFINITE=1;Sk.builtin.complex.PyOS_double_to_string.Py_DTST_NAN=
2},function(m,p){Sk.builtin.slice=function(a,b,c){Sk.builtin.pyCheckArgsLen("slice",arguments.length,1,3,!1,!1);if(void 0!==c&&Sk.misceval.isIndex(c)&&0===Sk.misceval.asIndex(c))throw new Sk.builtin.ValueError("slice step cannot be zero");if(!(this instanceof Sk.builtin.slice))return new Sk.builtin.slice(a,b,c);void 0===b&&void 0===c&&(b=a,a=Sk.builtin.none.none$);void 0===b&&(b=Sk.builtin.none.none$);void 0===c&&(c=Sk.builtin.none.none$);this.start=a;this.stop=b;this.step=c;this.__class__=Sk.builtin.slice;
this.$d=new Sk.builtin.dict([Sk.builtin.slice$start,this.start,Sk.builtin.slice$stop,this.stop,Sk.builtin.slice$step,this.step]);return this};Sk.abstr.setUpInheritance("slice",Sk.builtin.slice,Sk.builtin.object);Sk.builtin.slice.prototype.$r=function(){var a=Sk.builtin.repr(this.start).v,b=Sk.builtin.repr(this.stop).v,c=Sk.builtin.repr(this.step).v;return new Sk.builtin.str("slice("+a+", "+b+", "+c+")")};Sk.builtin.slice.prototype.tp$richcompare=function(a,b){if(!a.__class__||a.__class__!=Sk.builtin.slice)return"Eq"===
b?!1:"NotEq"===b?!0:Sk.__future__.python3?Sk.builtin.NotImplemented.NotImplemented$:!1;var c=new Sk.builtin.tuple([this.start,this.stop,this.step]);a=new Sk.builtin.tuple([a.start,a.stop,a.step]);return c.tp$richcompare(a,b)};Sk.builtin.slice.prototype.slice_indices_=function(a){if(Sk.builtin.checkNone(this.start))var b=null;else if(Sk.misceval.isIndex(this.start))b=Sk.misceval.asIndex(this.start);else throw new Sk.builtin.TypeError("slice indices must be integers or None");if(Sk.builtin.checkNone(this.stop))var c=
null;else if(Sk.misceval.isIndex(this.stop))c=Sk.misceval.asIndex(this.stop);else throw new Sk.builtin.TypeError("slice indices must be integers or None");if(Sk.builtin.checkNone(this.step))var d=null;else if(Sk.misceval.isIndex(this.step))d=Sk.misceval.asIndex(this.step);else throw new Sk.builtin.TypeError("slice indices must be integers or None");null===d&&(d=1);0<d?(null===b&&(b=0),null===c&&(c=a),c>a&&(c=a),0>b&&(b=a+b,0>b&&(b=0)),0>c&&(c=a+c)):(null===b&&(b=a-1),b>=a&&(b=a-1),null===c?c=-1:0>
c&&(c=a+c,0>c&&(c=-1)),0>b&&(b=a+b));return[b,c,d]};Sk.builtin.slice.prototype.indices=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("indices",arguments.length,2,2,!1,!1);b=Sk.builtin.asnum$(b);var c=a.slice_indices_(b);return new Sk.builtin.tuple([new Sk.builtin.int_(c[0]),new Sk.builtin.int_(c[1]),new Sk.builtin.int_(c[2])])});Sk.builtin.slice.prototype.sssiter$=function(a,b){var c=this.slice_indices_(a),d=c[0];a=c[1];c=c[2];if(0<c)for(;d<a;d+=c)b(d);else for(;d>a;d+=c)b(d)};Sk.builtin.slice$start=
new Sk.builtin.str("start");Sk.builtin.slice$stop=new Sk.builtin.str("stop");Sk.builtin.slice$step=new Sk.builtin.str("step")},function(m,p){Sk.builtin.set=function(a){var b;if(!(this instanceof Sk.builtin.set))return Sk.builtin.pyCheckArgsLen("set",arguments.length,0,1),new Sk.builtin.set(a);this.set_reset_();if(void 0!==a){var c=a;c.sk$asarray&&(c=c.sk$asarray());if("[object Array]"===Object.prototype.toString.apply(c)){var d=c.length;for(b=0;b<d;b++)Sk.builtin.set.prototype.add.func_code(this,
c[b])}else if(Sk.builtin.checkIterable(c))for(c=Sk.abstr.iter(c),b=c.tp$iternext();void 0!==b;b=c.tp$iternext())Sk.builtin.set.prototype.add.func_code(this,b);else throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(a)+"' object is not iterable");}return this};Sk.abstr.setUpInheritance("set",Sk.builtin.set,Sk.builtin.object);Sk.abstr.markUnhashable(Sk.builtin.set);Sk.builtin.set.prototype.__class__=Sk.builtin.set;Sk.builtin.set.prototype.set_reset_=function(){this.v=new Sk.builtin.dict([])};Sk.builtin.set.prototype.$r=
function(){var a,b=[];var c=Sk.abstr.iter(this);for(a=c.tp$iternext();void 0!==a;a=c.tp$iternext())b.push(Sk.misceval.objectRepr(a).v);return Sk.__future__.python3?0===b.length?new Sk.builtin.str("set()"):new Sk.builtin.str("{"+b.join(", ")+"}"):new Sk.builtin.str("set(["+b.join(", ")+"])")};Sk.builtin.set.prototype.sk$asarray=function(){return this.v.sk$asarray()};Sk.builtin.set.prototype.ob$eq=function(a){return this===a?Sk.builtin.bool.true$:a instanceof Sk.builtin.set&&Sk.builtin.set.prototype.sq$length.call(this)===
Sk.builtin.set.prototype.sq$length.call(a)?this.issubset.func_code(this,a):Sk.builtin.bool.false$};Sk.builtin.set.prototype.ob$ne=function(a){return this===a?Sk.builtin.bool.false$:a instanceof Sk.builtin.set&&Sk.builtin.set.prototype.sq$length.call(this)===Sk.builtin.set.prototype.sq$length.call(a)?this.issubset.func_code(this,a).v?Sk.builtin.bool.false$:Sk.builtin.bool.true$:Sk.builtin.bool.true$};Sk.builtin.set.prototype.ob$lt=function(a){return this===a||Sk.builtin.set.prototype.sq$length.call(this)>=
Sk.builtin.set.prototype.sq$length.call(a)?Sk.builtin.bool.false$:this.issubset.func_code(this,a)};Sk.builtin.set.prototype.ob$le=function(a){return this===a?Sk.builtin.bool.true$:Sk.builtin.set.prototype.sq$length.call(this)>Sk.builtin.set.prototype.sq$length.call(a)?Sk.builtin.bool.false$:this.issubset.func_code(this,a)};Sk.builtin.set.prototype.ob$gt=function(a){return this===a||Sk.builtin.set.prototype.sq$length.call(this)<=Sk.builtin.set.prototype.sq$length.call(a)?Sk.builtin.bool.false$:this.issuperset.func_code(this,
a)};Sk.builtin.set.prototype.ob$ge=function(a){return this===a?Sk.builtin.bool.true$:Sk.builtin.set.prototype.sq$length.call(this)<Sk.builtin.set.prototype.sq$length.call(a)?Sk.builtin.bool.false$:this.issuperset.func_code(this,a)};Sk.builtin.set.prototype.nb$and=function(a){if(Sk.__future__.python3&&!(a instanceof Sk.builtin.set))throw new Sk.builtin.TypeError("unsupported operand type(s) for &: 'set' and '"+Sk.abstr.typeName(a)+"'");return this.intersection.func_code(this,a)};Sk.builtin.set.prototype.nb$or=
function(a){if(Sk.__future__.python3&&!(a instanceof Sk.builtin.set))throw new Sk.builtin.TypeError("unsupported operand type(s) for |: 'set' and '"+Sk.abstr.typeName(a)+"'");return this.union.func_code(this,a)};Sk.builtin.set.prototype.nb$xor=function(a){if(Sk.__future__.python3&&!(a instanceof Sk.builtin.set))throw new Sk.builtin.TypeError("unsupported operand type(s) for ^: 'set' and '"+Sk.abstr.typeName(a)+"'");return this.symmetric_difference.func_code(this,a)};Sk.builtin.set.prototype.nb$subtract=
function(a){if(Sk.__future__.python3&&!(a instanceof Sk.builtin.set))throw new Sk.builtin.TypeError("unsupported operand type(s) for -: 'set' and '"+Sk.abstr.typeName(a)+"'");return this.difference.func_code(this,a)};Sk.builtin.set.prototype.__iter__=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("__iter__",arguments.length,0,0,!1,!0);return new Sk.builtin.set_iter_(a)});Sk.builtin.set.prototype.tp$iter=function(){return new Sk.builtin.set_iter_(this)};Sk.builtin.set.prototype.sq$length=
function(){return this.v.mp$length()};Sk.builtin.set.prototype.sq$contains=function(a){return this.v.sq$contains(a)};Sk.builtin.set.prototype.isdisjoint=new Sk.builtin.func(function(a,b){var c;Sk.builtin.pyCheckArgsLen("isdisjoint",arguments.length,2,2);if(!Sk.builtin.checkIterable(b))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(b)+"' object is not iterable");var d=Sk.abstr.iter(a);for(c=d.tp$iternext();void 0!==c;c=d.tp$iternext())if(c=Sk.abstr.sequenceContains(b,c))return Sk.builtin.bool.false$;
return Sk.builtin.bool.true$});Sk.builtin.set.prototype.issubset=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("issubset",arguments.length,2,2);if(!Sk.builtin.checkIterable(b))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(b)+"' object is not iterable");var c=a.sq$length();var d=b.sq$length();if(c>d)return Sk.builtin.bool.false$;c=Sk.abstr.iter(a);for(d=c.tp$iternext();void 0!==d;d=c.tp$iternext())if(d=Sk.abstr.sequenceContains(b,d),!d)return Sk.builtin.bool.false$;return Sk.builtin.bool.true$});
Sk.builtin.set.prototype.issuperset=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("issuperset",arguments.length,2,2);return Sk.builtin.set.prototype.issubset.func_code(b,a)});Sk.builtin.set.prototype.union=new Sk.builtin.func(function(a){var b;Sk.builtin.pyCheckArgsLen("union",arguments.length,1);var c=Sk.builtin.set.prototype.copy.func_code(a);var d=[c];for(b=1;b<arguments.length;b++)d.push(arguments[b]);Sk.builtin.set.prototype.update.func_code.apply(null,d);return c});Sk.builtin.set.prototype.intersection=
new Sk.builtin.func(function(a){var b;Sk.builtin.pyCheckArgsLen("intersection",arguments.length,1);var c=Sk.builtin.set.prototype.copy.func_code(a);var d=[c];for(b=1;b<arguments.length;b++)d.push(arguments[b]);Sk.builtin.set.prototype.intersection_update.func_code.apply(null,d);return c});Sk.builtin.set.prototype.difference=new Sk.builtin.func(function(a,b){var c;Sk.builtin.pyCheckArgsLen("difference",arguments.length,2);var d=Sk.builtin.set.prototype.copy.func_code(a);var e=[d];for(c=1;c<arguments.length;c++)e.push(arguments[c]);
Sk.builtin.set.prototype.difference_update.func_code.apply(null,e);return d});Sk.builtin.set.prototype.symmetric_difference=new Sk.builtin.func(function(a,b){var c;Sk.builtin.pyCheckArgsLen("symmetric_difference",arguments.length,2,2);var d=Sk.builtin.set.prototype.union.func_code(a,b);var e=Sk.abstr.iter(d);for(c=e.tp$iternext();void 0!==c;c=e.tp$iternext())Sk.abstr.sequenceContains(a,c)&&Sk.abstr.sequenceContains(b,c)&&Sk.builtin.set.prototype.discard.func_code(d,c);return d});Sk.builtin.set.prototype.copy=
new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("copy",arguments.length,1,1);return new Sk.builtin.set(a)});Sk.builtin.set.prototype.update=new Sk.builtin.func(function(a,b){var c,d;Sk.builtin.pyCheckArgsLen("update",arguments.length,2);for(c=1;c<arguments.length;c++){var e=arguments[c];if(!Sk.builtin.checkIterable(e))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(e)+"' object is not iterable");e=Sk.abstr.iter(e);for(d=e.tp$iternext();void 0!==d;d=e.tp$iternext())Sk.builtin.set.prototype.add.func_code(a,
d)}return Sk.builtin.none.none$});Sk.builtin.set.prototype.intersection_update=new Sk.builtin.func(function(a,b){var c,d;Sk.builtin.pyCheckArgsLen("intersection_update",arguments.length,2);for(c=1;c<arguments.length;c++)if(!Sk.builtin.checkIterable(arguments[c]))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(arguments[c])+"' object is not iterable");var e=Sk.abstr.iter(a);for(d=e.tp$iternext();void 0!==d;d=e.tp$iternext())for(c=1;c<arguments.length;c++)if(!Sk.abstr.sequenceContains(arguments[c],
d)){Sk.builtin.set.prototype.discard.func_code(a,d);break}return Sk.builtin.none.none$});Sk.builtin.set.prototype.difference_update=new Sk.builtin.func(function(a,b){var c,d;Sk.builtin.pyCheckArgsLen("difference_update",arguments.length,2);for(c=1;c<arguments.length;c++)if(!Sk.builtin.checkIterable(arguments[c]))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(arguments[c])+"' object is not iterable");var e=Sk.abstr.iter(a);for(d=e.tp$iternext();void 0!==d;d=e.tp$iternext())for(c=1;c<arguments.length;c++)if(Sk.abstr.sequenceContains(arguments[c],
d)){Sk.builtin.set.prototype.discard.func_code(a,d);break}return Sk.builtin.none.none$});Sk.builtin.set.prototype.symmetric_difference_update=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("symmetric_difference_update",arguments.length,2,2);var c=Sk.builtin.set.prototype.symmetric_difference.func_code(a,b);a.set_reset_();Sk.builtin.set.prototype.update.func_code(a,c);return Sk.builtin.none.none$});Sk.builtin.set.prototype.add=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("add",
arguments.length,2,2);a.v.mp$ass_subscript(b,!0);return Sk.builtin.none.none$});Sk.builtin.set.prototype.discard=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("discard",arguments.length,2,2);Sk.builtin.dict.prototype.pop.func_code(a.v,b,Sk.builtin.none.none$);return Sk.builtin.none.none$});Sk.builtin.set.prototype.pop=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("pop",arguments.length,1,1);if(0===a.sq$length())throw new Sk.builtin.KeyError("pop from an empty set");var b=
Sk.abstr.iter(a).tp$iternext();Sk.builtin.set.prototype.discard.func_code(a,b);return b});Sk.builtin.set.prototype.remove=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("remove",arguments.length,2,2);a.v.mp$del_subscript(b);return Sk.builtin.none.none$});Sk.builtin.set.prototype.__contains__=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("__contains__",arguments.length,2,2);return new Sk.builtin.bool(a.sq$contains(b))});Sk.exportSymbol("Sk.builtin.set",Sk.builtin.set);Sk.builtin.set_iter_=
function(a){if(!(this instanceof Sk.builtin.set_iter_))return new Sk.builtin.set_iter_(a);var b=Sk.builtin.create_dict_iter_(a.v);b.$obj=a;b.$r=function(){return new Sk.builtin.str("<setiterator>")};return b};Sk.abstr.setUpInheritance("setiterator",Sk.builtin.set_iter_,Sk.builtin.object);Sk.builtin.set_iter_.prototype.__class__=Sk.builtin.set_iter_;Sk.builtin.set_iter_.prototype.__iter__=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("__iter__",arguments.length,0,0,!0,!1);return a});Sk.builtin.set_iter_.prototype.next$=
function(a){a=a.tp$iternext();if(void 0===a)throw new Sk.builtin.StopIteration;return a}},function(m,p){Sk.builtin.frozenset=function(a){var b;if(!(this instanceof Sk.builtin.frozenset))return Sk.builtin.pyCheckArgsLen("frozenset",arguments.length,0,1),new Sk.builtin.frozenset(a);this.frozenset_reset_();if(void 0!==a){var c=a;c.sk$asarray&&(c=c.sk$asarray());if("[object Array]"===Object.prototype.toString.apply(c)){var d=c.length;for(b=0;b<d;b++)this.v.mp$ass_subscript(c[b],!0)}else if(Sk.builtin.checkIterable(c))for(c=
Sk.abstr.iter(c),b=c.tp$iternext();void 0!==b;b=c.tp$iternext())this.v.mp$ass_subscript(b,!0);else throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(a)+"' object is not iterable");}return this};Sk.abstr.setUpInheritance("frozenset",Sk.builtin.frozenset,Sk.builtin.object);Sk.builtin.frozenset.prototype.__class__=Sk.builtin.frozenset;Sk.builtin.frozenset.prototype.frozenset_reset_=function(){this.v=new Sk.builtin.dict([])};Sk.builtin.frozenset.prototype.$r=function(){var a,b=[];var c=Sk.abstr.iter(this);
for(a=c.tp$iternext();void 0!==a;a=c.tp$iternext())b.push(Sk.misceval.objectRepr(a).v);return Sk.__future__.python3?0===b.length?new Sk.builtin.str("frozenset()"):new Sk.builtin.str("frozenset({"+b.join(", ")+"})"):new Sk.builtin.str("frozenset(["+b.join(", ")+"])")};Sk.builtin.frozenset.prototype.sk$asarray=function(){return this.v.sk$asarray()};Sk.builtin.frozenset.prototype.tp$hash=function(){let a=1927868237;const b=this.sk$asarray();a*=b.length+1;for(let c=0;c<b.length;c++){const d=Sk.builtin.hash(b[c]).v;
a^=3644798167*(d^d<<16^89869747)}return this.$savedHash_=a=new Sk.builtin.int_(69069*a+907133923)};Sk.builtin.frozenset.prototype.ob$eq=function(a){return this===a?Sk.builtin.bool.true$:a instanceof Sk.builtin.frozenset&&Sk.builtin.frozenset.prototype.sq$length.call(this)===Sk.builtin.frozenset.prototype.sq$length.call(a)?this.issubset.func_code(this,a):Sk.builtin.bool.false$};Sk.builtin.frozenset.prototype.ob$ne=function(a){return this===a?Sk.builtin.bool.false$:a instanceof Sk.builtin.frozenset&&
Sk.builtin.frozenset.prototype.sq$length.call(this)===Sk.builtin.frozenset.prototype.sq$length.call(a)?this.issubset.func_code(this,a).v?Sk.builtin.bool.false$:Sk.builtin.bool.true$:Sk.builtin.bool.true$};Sk.builtin.frozenset.prototype.ob$lt=function(a){return this===a||Sk.builtin.frozenset.prototype.sq$length.call(this)>=Sk.builtin.frozenset.prototype.sq$length.call(a)?Sk.builtin.bool.false$:this.issubset.func_code(this,a)};Sk.builtin.frozenset.prototype.ob$le=function(a){return this===a?Sk.builtin.bool.true$:
Sk.builtin.frozenset.prototype.sq$length.call(this)>Sk.builtin.frozenset.prototype.sq$length.call(a)?Sk.builtin.bool.false$:this.issubset.func_code(this,a)};Sk.builtin.frozenset.prototype.ob$gt=function(a){return this===a||Sk.builtin.frozenset.prototype.sq$length.call(this)<=Sk.builtin.frozenset.prototype.sq$length.call(a)?Sk.builtin.bool.false$:this.issuperset.func_code(this,a)};Sk.builtin.frozenset.prototype.ob$ge=function(a){return this===a?Sk.builtin.bool.true$:Sk.builtin.frozenset.prototype.sq$length.call(this)<
Sk.builtin.frozenset.prototype.sq$length.call(a)?Sk.builtin.bool.false$:this.issuperset.func_code(this,a)};Sk.builtin.frozenset.prototype.nb$and=function(a){if(Sk.__future__.python3&&!(a instanceof Sk.builtin.frozenset))throw new Sk.builtin.TypeError("unsupported operand type(s) for &: 'frozenset' and '"+Sk.abstr.typeName(a)+"'");return this.intersection.func_code(this,a)};Sk.builtin.frozenset.prototype.nb$or=function(a){if(Sk.__future__.python3&&!(a instanceof Sk.builtin.frozenset))throw new Sk.builtin.TypeError("unsupported operand type(s) for |: 'frozenset' and '"+
Sk.abstr.typeName(a)+"'");return this.union.func_code(this,a)};Sk.builtin.frozenset.prototype.nb$xor=function(a){if(Sk.__future__.python3&&!(a instanceof Sk.builtin.frozenset))throw new Sk.builtin.TypeError("unsupported operand type(s) for ^: 'frozenset' and '"+Sk.abstr.typeName(a)+"'");return this.symmetric_difference.func_code(this,a)};Sk.builtin.frozenset.prototype.nb$subtract=function(a){if(Sk.__future__.python3&&!(a instanceof Sk.builtin.frozenset))throw new Sk.builtin.TypeError("unsupported operand type(s) for -: 'frozenset' and '"+
Sk.abstr.typeName(a)+"'");return this.difference.func_code(this,a)};Sk.builtin.frozenset.prototype.__iter__=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("__iter__",arguments.length,0,0,!1,!0);return new Sk.builtin.set_iter_(a)});Sk.builtin.frozenset.prototype.tp$iter=function(){return new Sk.builtin.set_iter_(this)};Sk.builtin.frozenset.prototype.sq$length=function(){return this.v.mp$length()};Sk.builtin.frozenset.prototype.sq$contains=function(a){return this.v.sq$contains(a)};Sk.builtin.frozenset.prototype.isdisjoint=
new Sk.builtin.func(function(a,b){var c;Sk.builtin.pyCheckArgsLen("isdisjoint",arguments.length,2,2);if(!Sk.builtin.checkIterable(b))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(b)+"' object is not iterable");var d=Sk.abstr.iter(a);for(c=d.tp$iternext();void 0!==c;c=d.tp$iternext())if(c=Sk.abstr.sequenceContains(b,c))return Sk.builtin.bool.false$;return Sk.builtin.bool.true$});Sk.builtin.frozenset.prototype.issubset=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("issubset",arguments.length,
2,2);if(!Sk.builtin.checkIterable(b))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(b)+"' object is not iterable");var c=a.sq$length();var d=b.sq$length();if(c>d)return Sk.builtin.bool.false$;c=Sk.abstr.iter(a);for(d=c.tp$iternext();void 0!==d;d=c.tp$iternext())if(d=Sk.abstr.sequenceContains(b,d),!d)return Sk.builtin.bool.false$;return Sk.builtin.bool.true$});Sk.builtin.frozenset.prototype.issuperset=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("issuperset",arguments.length,2,
2);return Sk.builtin.frozenset.prototype.issubset.func_code(b,a)});Sk.builtin.frozenset.prototype.union=new Sk.builtin.func(function(a){var b;Sk.builtin.pyCheckArgsLen("union",arguments.length,1);var c=Sk.builtin.frozenset.prototype.copy.func_code(a);var d=[c];for(b=1;b<arguments.length;b++)d.push(arguments[b]);var e;for(b=0;b<d.length;b++){var h=d[b];if(!Sk.builtin.checkIterable(h))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(h)+"' object is not iterable");h=Sk.abstr.iter(h);for(e=h.tp$iternext();void 0!==
e;e=h.tp$iternext())c.v.mp$ass_subscript(e,!0)}return c});Sk.builtin.frozenset.prototype.intersection=new Sk.builtin.func(function(a){var b;Sk.builtin.pyCheckArgsLen("intersection",arguments.length,1);var c=Sk.builtin.frozenset.prototype.copy.func_code(a);var d=[c];for(b=1;b<arguments.length;b++)d.push(arguments[b]);var e;for(b=1;b<arguments.length;b++)if(!Sk.builtin.checkIterable(arguments[b]))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(arguments[b])+"' object is not iterable");d=Sk.abstr.iter(a);
for(e=d.tp$iternext();void 0!==e;e=d.tp$iternext())for(b=1;b<arguments.length;b++)if(!Sk.abstr.sequenceContains(arguments[b],e)){Sk.builtin.dict.prototype.pop.func_code(c.v,e,Sk.builtin.none.none$);break}return c});Sk.builtin.frozenset.prototype.difference=new Sk.builtin.func(function(a,b){var c;Sk.builtin.pyCheckArgsLen("difference",arguments.length,2);var d=Sk.builtin.frozenset.prototype.copy.func_code(a);var e=[d];for(c=1;c<arguments.length;c++)e.push(arguments[c]);var h;for(c=1;c<arguments.length;c++)if(!Sk.builtin.checkIterable(arguments[c]))throw new Sk.builtin.TypeError("'"+
Sk.abstr.typeName(arguments[c])+"' object is not iterable");e=Sk.abstr.iter(a);for(h=e.tp$iternext();void 0!==h;h=e.tp$iternext())for(c=1;c<arguments.length;c++)if(Sk.abstr.sequenceContains(arguments[c],h)){Sk.builtin.dict.prototype.pop.func_code(d.v,h,Sk.builtin.none.none$);break}return d});Sk.builtin.frozenset.prototype.symmetric_difference=new Sk.builtin.func(function(a,b){var c;Sk.builtin.pyCheckArgsLen("symmetric_difference",arguments.length,2,2);var d=Sk.builtin.frozenset.prototype.union.func_code(a,
b);var e=Sk.abstr.iter(d);for(c=e.tp$iternext();void 0!==c;c=e.tp$iternext())Sk.abstr.sequenceContains(a,c)&&Sk.abstr.sequenceContains(b,c)&&Sk.builtin.dict.prototype.pop.func_code(d.v,c,Sk.builtin.none.none$);return d});Sk.builtin.frozenset.prototype.copy=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("copy",arguments.length,1,1);return new Sk.builtin.frozenset(a)});Sk.exportSymbol("Sk.builtin.frozenset",Sk.builtin.frozenset);Sk.builtin.frozenset.prototype.__contains__=new Sk.builtin.func(function(a,
b){Sk.builtin.pyCheckArgsLen("__contains__",arguments.length,2,2);return new Sk.builtin.bool(a.sq$contains(b))})},function(m,p){m=function(a){Sk.builtin.pyCheckArgsLen("print",arguments.length,0,Infinity,!0,!1);var b=Array.prototype.slice.call(arguments,1),c=new Sk.builtin.dict(a);Sk.ffi.remapToJs(c);var d={sep:" ",end:"\n",file:null};var e=c.mp$lookup(new Sk.builtin.str("sep"));if(void 0!==e){var h=Sk.builtin.checkNone(e);if(Sk.builtin.checkString(e)||h)d.sep=h?d.sep:Sk.ffi.remapToJs(e);else throw new Sk.builtin.TypeError("sep must be None or a string, not "+
Sk.abstr.typeName(e));}e=c.mp$lookup(new Sk.builtin.str("end"));if(void 0!==e)if(h=Sk.builtin.checkNone(e),Sk.builtin.checkString(e)||h)d.end=h?d.end:Sk.ffi.remapToJs(e);else throw new Sk.builtin.TypeError("end must be None or a string, not "+Sk.abstr.typeName(e));e=c.mp$lookup(new Sk.builtin.str("file"));if(void 0!==e)if((h=Sk.builtin.checkNone(e))||void 0!==e.tp$getattr("write"))d.file=h?d.file:e;else throw new Sk.builtin.AttributeError("'"+Sk.abstr.typeName(e)+"' object has no attribute 'write'");
var g="";for(c=0;c<b.length;c++)g+=(new Sk.builtin.str(b[c])).v,g+=d.sep;0<b.length&&0<d.sep.length&&(g=g.substring(0,g.length-d.sep.length));g+=d.end;if(null!==d.file)Sk.misceval.callsimArray(d.file.write,[d.file,new Sk.builtin.str(g)]);else return Sk.misceval.chain(Sk.importModule("sys",!1,!0),function(a){return Sk.misceval.callsimOrSuspendArray(a.$d.stdout.write,[a.$d.stdout,new Sk.builtin.str(g)])})};m.co_kwargs=!0;Sk.builtin.print=new Sk.builtin.func(m);Sk.builtin.print.__doc__=new Sk.builtin.str("print(value, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)\n\nPrints the values to a stream, or to sys.stdout by default.\nOptional keyword arguments:\nfile:  a file-like object (stream); defaults to the current sys.stdout.\nsep:   string inserted between values, default a space.\nend:   string appended after the last value, default a newline.\nflush: whether to forcibly flush the stream.")},
function(m,p){Sk.builtin.module=function(){};Sk.exportSymbol("Sk.builtin.module",Sk.builtin.module);Sk.builtin.module.prototype.ob$type=Sk.builtin.type.makeIntoTypeObj("module",Sk.builtin.module);Sk.builtin.module.prototype.tp$getattr=Sk.builtin.object.prototype.GenericGetAttr;Sk.builtin.module.prototype.tp$setattr=Sk.builtin.object.prototype.GenericSetAttr;Sk.builtin.module.prototype.tp$name="module";Sk.builtin.module.prototype.$r=function(){let a=a=>{a=this.tp$getattr(new Sk.builtin.str(a));return Sk.builtin.repr(a||
Sk.builtin.str.$emptystr).$jsstr()};return new Sk.builtin.str("<module "+a("__name__")+" from "+a("__file__")+">")}},function(m,p){Sk.builtin.structseq_types={};Sk.builtin.make_structseq=function(a,b,c,d){var e=a+"."+b,h=[];a=[];for(var g in c)h.push(g),a.push(c[g]);c=function(a){Sk.builtin.pyCheckArgsLen(e,arguments.length,1,1);var b;if(!(this instanceof Sk.builtin.structseq_types[e])){var c=Object.create(Sk.builtin.structseq_types[e].prototype);c.constructor.apply(c,arguments);return c}if(Array.isArray(a))var d=
a;else{d=[];c=Sk.abstr.iter(a);for(b=c.tp$iternext();void 0!==b;b=c.tp$iternext())d.push(b);if(d.length!=h.length)throw new Sk.builtin.TypeError(e+"() takes a "+h.length+"-sequence ("+d.length+"-sequence given)");}Sk.builtin.tuple.call(this,d);this.__class__=Sk.builtin.structseq_types[e]};Sk.builtin.structseq_types[e]=c;Sk.abstr.inherits(c,Sk.builtin.tuple);d&&(c.prototype.__doc__=d);c.prototype.tp$name=e;c.prototype.ob$type=Sk.builtin.type.makeIntoTypeObj(e,Sk.builtin.structseq_types[e]);c.prototype.ob$type.$d=
new Sk.builtin.dict([]);c.prototype.ob$type.$d.mp$ass_subscript(Sk.builtin.type.basesStr_,new Sk.builtin.tuple([Sk.builtin.tuple]));c.prototype.__getitem__=new Sk.builtin.func(function(a,b){return Sk.builtin.tuple.prototype.mp$subscript.call(a,b)});c.prototype.__reduce__=new Sk.builtin.func(function(a){throw new Sk.builtin.Exception("__reduce__ is not implemented");});c.prototype.$r=function(){var a;if(0===this.v.length)return new Sk.builtin.str(e+"()");var b=[];for(a=0;a<this.v.length;++a)b[a]=h[a]+
"="+Sk.misceval.objectRepr(this.v[a]).v;a=b.join(", ");1===this.v.length&&(a+=",");return new Sk.builtin.str(e+"("+a+")")};c.prototype.tp$setattr=function(a,b){throw new Sk.builtin.AttributeError("readonly property");};c.prototype.tp$getattr=function(a){var b=a.$jsstr();b=h.indexOf(b);return 0<=b?this.v[b]:Sk.builtin.object.prototype.GenericGetAttr(a)};return c};Sk.exportSymbol("Sk.builtin.make_structseq",Sk.builtin.make_structseq)},function(m,p){Sk.builtin.generator=function(a,b,c,d,e){var h;if(a){if(!(this instanceof
Sk.builtin.generator))return new Sk.builtin.generator(a,b,c,d,e);this.func_code=a;this.func_globals=b||null;this.gi$running=!1;this.gi$resumeat=0;this.gi$sentvalue=void 0;this.gi$locals={};this.gi$cells={};if(0<c.length)for(b=0;b<a.co_varnames.length;++b)this.gi$locals[a.co_varnames[b]]=c[b];if(void 0!==e)for(h in e)d[h]=e[h];this.func_closure=d;return this}};Sk.exportSymbol("Sk.builtin.generator",Sk.builtin.generator);Sk.abstr.setUpInheritance("generator",Sk.builtin.generator,Sk.builtin.object);
Sk.builtin.generator.prototype.tp$iter=function(){return this};Sk.builtin.generator.prototype.tp$iternext=function(a,b){var c=this;this.gi$running=!0;void 0===b&&(b=Sk.builtin.none.none$);this.gi$sentvalue=b;b=[this];this.func_closure&&b.push(this.func_closure);return function h(b){if(b instanceof Sk.misceval.Suspension){if(a)return new Sk.misceval.Suspension(h,b);b=Sk.misceval.retryOptionalSuspensionOrThrow(b)}c.gi$running=!1;Sk.asserts.assert(void 0!==b);if(b!==Sk.builtin.none.none$)return c.gi$resumeat=
b[0],b=b[1]}(this.func_code.apply(this.func_globals,b))};Sk.builtin.generator.prototype.next$=function(a){return a.tp$iternext(!0)};Sk.builtin.generator.prototype.$r=function(){return new Sk.builtin.str("<generator object "+this.func_code.co_name.v+">")};Sk.builtin.generator.prototype.send=new Sk.builtin.func(function(a,b){return a.tp$iternext(!0,b)});Sk.builtin.makeGenerator=function(a,b){var c,d=new Sk.builtin.generator(null,null,null);d.tp$iternext=a;for(c in b)b.hasOwnProperty(c)&&(d[c]=b[c]);
return d};Sk.exportSymbol("Sk.builtin.makeGenerator",Sk.builtin.makeGenerator)},function(m,p){Sk.builtin.file=function(a,b,c){var d;if(!(this instanceof Sk.builtin.file))return new Sk.builtin.file(a,b,c);this.mode=b;this.name=Sk.ffi.remapToJs(a);this.closed=!1;if("/dev/stdout"===this.name)this.data$=Sk.builtin.none.none$,this.fileno=1;else if("/dev/stdin"===this.name)this.fileno=0;else if("/dev/stderr"===this.name)this.fileno=2;else{if("w"===Sk.ffi.remapToJs(this.mode))this.fileno=Sk.builtin.file.currentFileno++;
else for(d in this.fileno=Sk.inBrowser?10:11,this.data$=Sk.read(a.v),this.lineList=this.data$.split("\n"),this.lineList=this.lineList.slice(0,-1),this.lineList)this.lineList[d]+="\n";this.currentLine=0}this.pos$=0;this.__class__=Sk.builtin.file;Sk.fileopen&&10<=this.fileno&&Sk.fileopen(this);return this};Sk.builtin.file.currentFileno=12;Sk.abstr.setUpInheritance("file",Sk.builtin.file,Sk.builtin.object);Sk.builtin.file.prototype.$r=function(){return new Sk.builtin.str("<"+(this.closed?"closed":"open")+
"file '"+this.name+"', mode '"+Sk.ffi.remapToJs(this.mode)+"'>")};Sk.builtin.file.prototype.__enter__=new Sk.builtin.func(function(a){return a});Sk.builtin.file.prototype.__exit__=new Sk.builtin.func(function(a){return Sk.misceval.callsimArray(Sk.builtin.file.prototype.close,[a])});Sk.builtin.file.prototype.tp$iter=function(){var a={tp$iter:function(){return a},$obj:this,$index:this.currentLine,$lines:this.lineList,tp$iternext:function(){if(!(a.$index>=a.$lines.length))return new Sk.builtin.str(a.$lines[a.$index++])}};
return a};Sk.builtin.file.prototype.close=new Sk.builtin.func(function(a){a.closed=!0;return Sk.builtin.none.none$});Sk.builtin.file.prototype.flush=new Sk.builtin.func(function(a){});Sk.builtin.file.prototype.fileno=new Sk.builtin.func(function(a){return this.fileno});Sk.builtin.file.prototype.isatty=new Sk.builtin.func(function(a){return!1});Sk.builtin.file.prototype.read=new Sk.builtin.func(function(a,b){var c=a.data$.length;if(a.closed)throw new Sk.builtin.ValueError("I/O operation on closed file");
var d=void 0===b?c:Sk.ffi.remapToJs(b);d=new Sk.builtin.str(a.data$.substr(a.pos$,d));a.pos$=void 0===b?c:a.pos$+Sk.ffi.remapToJs(b);a.pos$>=c&&(a.pos$=c);return d});Sk.builtin.file.$readline=function(a,b,c){if(0===a.fileno){a=Sk.ffi.remapToJs(c);a=Sk.inputfun(a?a:"");if(a instanceof Promise||a&&"function"===typeof a.then){var d=new Sk.misceval.Suspension;d.resume=function(){if(d.data.error)throw d.data.error;return new Sk.builtin.str(d.data.result)};d.data={type:"Sk.promise",promise:a};return d}return new Sk.builtin.str(a)}b=
"";a.currentLine<a.lineList.length&&(b=a.lineList[a.currentLine],a.currentLine++);return new Sk.builtin.str(b)};Sk.builtin.file.prototype.readline=new Sk.builtin.func(function(a,b){return Sk.builtin.file.$readline(a,b,void 0)});Sk.builtin.file.prototype.readlines=new Sk.builtin.func(function(a,b){if(0===a.fileno)return new Sk.builtin.NotImplementedError("readlines ins't implemented because the web doesn't support Ctrl+D");var c=[];for(b=a.currentLine;b<a.lineList.length;b++)c.push(new Sk.builtin.str(a.lineList[b]));
return new Sk.builtin.list(c)});Sk.builtin.file.prototype.seek=new Sk.builtin.func(function(a,b,c){b=Sk.ffi.remapToJs(b);void 0===c&&(c=0);0===c?a.pos$=b:1==c?a.pos$=a.data$.length+b:2==c&&(a.pos$=a.data$.length+b);return Sk.builtin.none.none$});Sk.builtin.file.prototype.tell=new Sk.builtin.func(function(a){return Sk.ffi.remapToPy(a.pos$)});Sk.builtin.file.prototype.truncate=new Sk.builtin.func(function(a,b){Sk.asserts.fail()});Sk.builtin.file.prototype.write=new Sk.builtin.func(function(a,b){var c=
Sk.ffi.remapToJs(a.mode);Sk.ffi.remapToJs(b);if("w"===c||"wb"===c||"a"===c||"ab"===c)if(Sk.filewrite){if(a.closed)throw new Sk.builtin.ValueError("I/O operation on closed file");1===a.fileno?Sk.output(Sk.ffi.remapToJs(b)):Sk.filewrite(a,b)}else 1===a.fileno?Sk.output(Sk.ffi.remapToJs(b)):Sk.asserts.fail();return Sk.builtin.none.none$});Sk.exportSymbol("Sk.builtin.file",Sk.builtin.file)},function(m,p){Sk.ffi=Sk.ffi||{};Sk.ffi.remapToPy=function(a){var b;if(null===a||"undefined"===typeof a)return Sk.builtin.none.none$;
if(a.ob$type)return a;if(a instanceof Sk.misceval.Suspension)return a;if("[object Array]"===Object.prototype.toString.call(a)){var c=[];for(b=0;b<a.length;++b)c.push(Sk.ffi.remapToPy(a[b]));return new Sk.builtin.list(c)}if("object"===typeof a){c=[];for(b in a)c.push(Sk.ffi.remapToPy(b)),c.push(Sk.ffi.remapToPy(a[b]));return new Sk.builtin.dict(c)}if("string"===typeof a)return new Sk.builtin.str(a);if("number"===typeof a)return Sk.builtin.assk$(a);if("boolean"===typeof a)return new Sk.builtin.bool(a);
if("undefined"===typeof a)return Sk.builtin.none.none$;if("function"===typeof a)return new Sk.builtin.func(a);Sk.asserts.fail("unhandled remap type "+typeof a)};Sk.exportSymbol("Sk.ffi.remapToPy",Sk.ffi.remapToPy);Sk.ffi.remapToJs=function(a){var b;if(a instanceof Sk.builtin.dict){var c={};var d=a.tp$iter();for(b=d.tp$iternext();void 0!==b;b=d.tp$iternext()){var e=a.mp$subscript(b);void 0===e&&(e=null);b=Sk.ffi.remapToJs(b);c[b]=Sk.ffi.remapToJs(e)}return c}if(a instanceof Sk.builtin.list||a instanceof
Sk.builtin.tuple){c=[];for(e=0;e<a.v.length;++e)c.push(Sk.ffi.remapToJs(a.v[e]));return c}if(a instanceof Sk.builtin.bool)return a.v?!0:!1;if(a instanceof Sk.builtin.int_)return Sk.builtin.asnum$(a);if(a instanceof Sk.builtin.float_)return Sk.builtin.asnum$(a);if(a instanceof Sk.builtin.lng)return Sk.builtin.asnum$(a);if("number"===typeof a||"boolean"===typeof a||"string"===typeof a)return a;if(void 0!==a)return a.v};Sk.exportSymbol("Sk.ffi.remapToJs",Sk.ffi.remapToJs);Sk.ffi.callback=function(a){return void 0===
a?a:function(){return Sk.misceval.apply(a,void 0,void 0,void 0,Array.prototype.slice.call(arguments,0))}};Sk.exportSymbol("Sk.ffi.callback",Sk.ffi.callback);Sk.ffi.stdwrap=function(a,b){a=new a;a.v=b;return a};Sk.exportSymbol("Sk.ffi.stdwrap",Sk.ffi.stdwrap);Sk.ffi.basicwrap=function(a){if(a instanceof Sk.builtin.int_)return Sk.builtin.asnum$(a);if(a instanceof Sk.builtin.float_)return Sk.builtin.asnum$(a);if(a instanceof Sk.builtin.lng)return Sk.builtin.asnum$(a);if("number"===typeof a||"boolean"===
typeof a)return a;if("string"===typeof a)return new Sk.builtin.str(a);Sk.asserts.fail("unexpected type for basicwrap")};Sk.exportSymbol("Sk.ffi.basicwrap",Sk.ffi.basicwrap);Sk.ffi.unwrapo=function(a){if(void 0!==a)return a.v};Sk.exportSymbol("Sk.ffi.unwrapo",Sk.ffi.unwrapo);Sk.ffi.unwrapn=function(a){return null===a?null:a.v};Sk.exportSymbol("Sk.ffi.unwrapn",Sk.ffi.unwrapn)},function(m,p){Sk.builtin.iterator=function(a,b){var c;if(a instanceof Sk.builtin.generator)return a;if(c=Sk.abstr.lookupSpecial(a,
Sk.builtin.str.$iter))return Sk.misceval.callsimArray(c,[a]);this.sentinel=b;this.flag=!1;this.idx=0;this.obj=a;void 0===b?(this.getitem=Sk.abstr.lookupSpecial(a,Sk.builtin.str.$getitem),this.$r=function(){return new Sk.builtin.str("<iterator object>")}):(this.call=Sk.abstr.lookupSpecial(a,Sk.builtin.str.$call),this.$r=function(){return new Sk.builtin.str("<callable-iterator object>")});return this};Sk.abstr.setUpInheritance("iterator",Sk.builtin.iterator,Sk.builtin.object);Sk.builtin.iterator.prototype.__class__=
Sk.builtin.iterator;Sk.builtin.iterator.prototype.__iter__=new Sk.builtin.func(function(a){return a.tp$iter()});Sk.builtin.iterator.prototype.tp$iter=function(){return this};Sk.builtin.iterator.prototype.tp$iternext=function(a){var b=this;if(!0!==this.flag){if(this.getitem){var c=Sk.misceval.tryCatch(function(){return Sk.misceval.callsimOrSuspendArray(b.getitem,[b.obj,Sk.ffi.remapToPy(b.idx++)])},function(a){if(!(a instanceof Sk.builtin.StopIteration||a instanceof Sk.builtin.IndexError))throw a;});
return a?c:Sk.misceval.retryOptionalSuspensionOrThrow(c)}c=function(a){if(Sk.misceval.richCompareBool(a,b.sentinel,"Eq"))b.flag=!0;else return a};c=this.call?Sk.misceval.chain(Sk.misceval.callsimOrSuspendArray(this.call,[this.obj]),c):Sk.misceval.chain(Sk.misceval.callsimOrSuspendArray(this.obj),c);return a?c:Sk.misceval.retryOptionalSuspensionOrThrow(c)}};Sk.builtin.iterator.prototype.next$=function(a){a=a.tp$iternext();if(!a)throw new Sk.builtin.StopIteration;return a};Sk.exportSymbol("Sk.builtin.iterator",
Sk.builtin.iterator)},function(m,p){Sk.builtin.range_=function(a,b,c,d){if(!(this instanceof Sk.builtin.range_))return new Sk.builtin.range_(a,b,c,d);this.v=d;this.$start=a;this.$stop=b;this.$step=c;this.$r=function(){var a="range("+this.$start+", "+this.$stop;1!=this.$step&&(a+=", "+this.$step);return new Sk.builtin.str(a+")")};return this};Sk.abstr.setUpInheritance("range",Sk.builtin.range_,Sk.builtin.object);Sk.builtin.range_.prototype.__class__=Sk.builtin.range_;Sk.builtin.range_.prototype.mp$subscript=
function(a){var b=this.v.mp$subscript(a);if(b instanceof Sk.builtin.list){if(Sk.builtin.checkNone(a.start))var c=this.v.mp$subscript(new Sk.builtin.int_(0)).v;else try{c=this.v.mp$subscript(a.start).v}catch(e){c=this.$start}try{var d=this.v.mp$subscript(a.stop).v}catch(e){d=this.$stop}a=Sk.builtin.checkNone(a.step)?1:Sk.misceval.asIndex(a.step);a*=this.$step;return new Sk.builtin.range_(c,d,a,b)}return b};Sk.builtin.range_.prototype.__getitem__=new Sk.builtin.func(function(a,b){return Sk.builtin.range_.prototype.mp$subscript.call(a,
b)});Sk.builtin.range_.prototype.sq$contains=function(a){return this.v.sq$contains(a)};Sk.builtin.range_.prototype.sq$length=function(){return this.v.sq$length()};Sk.builtin.range_.prototype.tp$richcompare=function(a,b){a.__class__==Sk.builtin.range_&&(a=a.v);return this.v.tp$richcompare(a,b)};Sk.builtin.range_.prototype.tp$iter=function(){var a=this.v.tp$iter();a.$r=function(){return new Sk.builtin.str("<rangeiterator>")};return a};Sk.builtin.range_.prototype.__iter__=new Sk.builtin.func(function(a){Sk.builtin.pyCheckArgsLen("__iter__",
arguments.length,1,1);return a.tp$iter()});Sk.builtin.range_.prototype.__contains__=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("__contains__",arguments.length,2,2);return new Sk.builtin.bool(a.sq$contains(b))});Sk.builtin.range_.prototype.index=new Sk.builtin.func(function(a,b,c,d){Sk.builtin.pyCheckArgsLen("index",arguments.length,2,4);return Sk.misceval.callsimArray(a.v.index,[a.v,b,c,d])});Sk.builtin.range_.prototype.count=new Sk.builtin.func(function(a,b){Sk.builtin.pyCheckArgsLen("count",
arguments.length,2,2);return Sk.misceval.callsimArray(a.v.count,[a.v,b])})},function(m,p){Sk.builtin.enumerate=function(a,b){if(!(this instanceof Sk.builtin.enumerate))return new Sk.builtin.enumerate(a,b);Sk.builtin.pyCheckArgsLen("enumerate",arguments.length,1,2);if(!Sk.builtin.checkIterable(a))throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(a)+"' object is not iterable");if(void 0!==b)if(Sk.misceval.isIndex(b))b=Sk.misceval.asIndex(b);else throw new Sk.builtin.TypeError("'"+Sk.abstr.typeName(b)+
"' object cannot be interpreted as an index");else b=0;var c=a.tp$iter();this.tp$iter=function(){return this};this.$index=b;this.tp$iternext=function(){var a=c.tp$iternext();if(void 0!==a){var b=new Sk.builtin.int_(this.$index++);return new Sk.builtin.tuple([b,a])}};this.__class__=Sk.builtin.enumerate;return this};Sk.abstr.setUpInheritance("enumerate",Sk.builtin.enumerate,Sk.builtin.object);Sk.builtin.enumerate.prototype.__iter__=new Sk.builtin.func(function(a){return a.tp$iter()});Sk.builtin.enumerate.prototype.next$=
function(a){return a.tp$iternext()};Sk.builtin.enumerate.co_varnames=["iterable","start"];Sk.builtin.enumerate.co_argcount=2;Sk.builtin.enumerate.$defaults=[Sk.builtin.none.none$,0];Sk.builtin.enumerate.co_name=new Sk.builtin.str("enumerate");Sk.builtin.enumerate.prototype.$r=function(){return new Sk.builtin.str("<enumerate object>")}},function(m,p){Sk.builtin.filter_=function(a,b){var c,d;Sk.builtin.pyCheckArgsLen("filter_",arguments.length,2,2);if(!(this instanceof Sk.builtin.filter_))return new Sk.builtin.filter_(a,
b);var e=Sk.abstr.iter(b);var h=function(b){c=a===Sk.builtin.none.none$?b:Sk.misceval.callsimArray(a,[b]);if(Sk.misceval.isTrue(c))return c};this.tp$iter=function(){return this};this.tp$iternext=function(){d=e.tp$iternext();if(void 0!==d){for(c=h(d);void 0===c;){d=e.tp$iternext();if(void 0===d)return;c=h(d)}return d}};this.__class__=Sk.builtin.filter_;return this};Sk.abstr.setUpInheritance("filter",Sk.builtin.filter_,Sk.builtin.object);Sk.builtin.filter_.prototype.__iter__=new Sk.builtin.func(function(a){return a.tp$iter()});
Sk.builtin.filter_.prototype.next$=function(a){return a.tp$iternext()};Sk.builtin.filter_.prototype.$r=function(){return new Sk.builtin.str("<filter object>")};Sk.exportSymbol("Sk.builtin.filter_",Sk.builtin.filter_)},function(m,p){Sk.builtin.zip_=function(){var a,b;if(!(this instanceof Sk.builtin.zip_))return new Sk.builtin.zip_(...arguments);if(0===arguments.length)return new Sk.builtin.zip_(new Sk.builtin.list([]));var c=[];for(a=0;a<arguments.length;a++)try{c.push(Sk.abstr.iter(arguments[a]))}catch(d){if(d instanceof
Sk.builtin.TypeError)throw new Sk.builtin.TypeError("zip argument #"+(a+1)+" must support iteration");throw d;}this.tp$iter=function(){return this};this.tp$iternext=function(){var d=[];for(a=0;a<c.length;a++){b=c[a].tp$iternext();if(void 0===b)return;d.push(b)}return new Sk.builtin.tuple(d)};this.__class__=Sk.builtin.zip_;return this};Sk.abstr.setUpInheritance("zip",Sk.builtin.zip_,Sk.builtin.object);Sk.builtin.zip_.prototype.__iter__=new Sk.builtin.func(function(a){return a.tp$iter()});Sk.builtin.zip_.prototype.next$=
function(a){return a.tp$iternext()};Sk.builtin.zip_.prototype.$r=function(){return new Sk.builtin.str("<zip object>")};Sk.exportSymbol("Sk.builtin.zip_",Sk.builtin.zip_)},function(m,p){Sk.builtin.map_=function(a,b){var c,d,e,h;Sk.builtin.pyCheckArgsLen("map_",arguments.length,2);if(!(this instanceof Sk.builtin.map_)){var g=Array.prototype.slice.apply(arguments).slice(1);return new Sk.builtin.map_(a,...g)}if(2<arguments.length){var f=Array.prototype.slice.apply(arguments).slice(1);for(d=0;d<f.length;d++)f[d]=
Sk.abstr.iter(f[d]);var k=function(){h=[];for(d=0;d<f.length;d++){c=f[d].tp$iternext();if(void 0===c)return;h.push(c)}return h}}else b=Sk.abstr.iter(b),k=function(){return b.tp$iternext()};this.tp$iternext=function(){e=k();if(void 0!==e){if(a===Sk.builtin.none.none$)return e instanceof Array&&(e=new Sk.builtin.tuple(e)),e;e instanceof Array||(e=[e]);return Sk.misceval.applyOrSuspend(a,void 0,void 0,void 0,e)}};this.tp$iter=function(){return this};this.__class__=Sk.builtin.map_;return this};Sk.abstr.setUpInheritance("map",
Sk.builtin.map_,Sk.builtin.object);Sk.builtin.map_.prototype.__iter__=new Sk.builtin.func(function(a){return a.tp$iter()});Sk.builtin.map_.prototype.next$=function(a){return a.tp$iternext()};Sk.builtin.map_.prototype.$r=function(){return new Sk.builtin.str("<map object>")};Sk.exportSymbol("Sk.builtin.map_",Sk.builtin.map_)},function(m,p){var a={T_ENDMARKER:0,T_NAME:1,T_NUMBER:2,T_STRING:3,T_NEWLINE:4,T_INDENT:5,T_DEDENT:6,T_LPAR:7,T_RPAR:8,T_LSQB:9,T_RSQB:10,T_COLON:11,T_COMMA:12,T_SEMI:13,T_PLUS:14,
T_MINUS:15,T_STAR:16,T_SLASH:17,T_VBAR:18,T_AMPER:19,T_LESS:20,T_GREATER:21,T_EQUAL:22,T_DOT:23,T_PERCENT:24,T_LBRACE:25,T_RBRACE:26,T_EQEQUAL:27,T_NOTEQUAL:28,T_LESSEQUAL:29,T_GREATEREQUAL:30,T_TILDE:31,T_CIRCUMFLEX:32,T_LEFTSHIFT:33,T_RIGHTSHIFT:34,T_DOUBLESTAR:35,T_PLUSEQUAL:36,T_MINEQUAL:37,T_STAREQUAL:38,T_SLASHEQUAL:39,T_PERCENTEQUAL:40,T_AMPEREQUAL:41,T_VBAREQUAL:42,T_CIRCUMFLEXEQUAL:43,T_LEFTSHIFTEQUAL:44,T_RIGHTSHIFTEQUAL:45,T_DOUBLESTAREQUAL:46,T_DOUBLESLASH:47,T_DOUBLESLASHEQUAL:48,T_AT:49,
T_ATEQUAL:50,T_RARROW:51,T_ELLIPSIS:52,T_OP:53,T_AWAIT:54,T_ASYNC:55,T_ERRORTOKEN:56,T_NT_OFFSET:256,T_N_TOKENS:60,T_COMMENT:57,T_NL:58,T_ENCODING:59};m={"!=":a.NOTEQUAL,"%":a.PERCENT,"%=":a.PERCENTEQUAL,"&":a.AMPER,"&=":a.AMPEREQUAL,"(":a.LPAR,")":a.RPAR,"*":a.STAR,"**":a.DOUBLESTAR,"**=":a.DOUBLESTAREQUAL,"*=":a.STAREQUAL,"+":a.PLUS,"+=":a.PLUSEQUAL,",":a.COMMA,"-":a.MINUS,"-=":a.MINEQUAL,"->":a.RARROW,".":a.DOT,"...":a.ELLIPSIS,"/":a.SLASH,"//":a.DOUBLESLASH,"//=":a.DOUBLESLASHEQUAL,"/=":a.SLASHEQUAL,
":":a.COLON,":=":a.COLONEQUAL,";":a.SEMI,"<":a.LESS,"<<":a.LEFTSHIFT,"<<=":a.LEFTSHIFTEQUAL,"<=":a.LESSEQUAL,"=":a.EQUAL,"==":a.EQEQUAL,">":a.GREATER,">=":a.GREATEREQUAL,">>":a.RIGHTSHIFT,">>=":a.RIGHTSHIFTEQUAL,"@":a.AT,"@=":a.ATEQUAL,"[":a.LSQB,"]":a.RSQB,"^":a.CIRCUMFLEX,"^=":a.CIRCUMFLEXEQUAL,"{":a.LBRACE,"|":a.VBAR,"|=":a.VBAREQUAL,"}":a.RBRACE,"~":a.TILDE};var b={};(function(){for(var c in a)b[a[c]]=c})();["tok_name","ISTERMINAL","ISNONTERMINAL","ISEOF"].concat(Object.keys(b).map(function(a){return b[a]}));
Sk.token={};Sk.token.tokens=a;Sk.token.tok_name=b;Sk.token.EXACT_TOKEN_TYPES=m;Sk.token.ISTERMINAL=function(b){return b<a.T_NT_OFFSET};Sk.token.ISNONTERMINAL=function(b){return b>=a.T_NT_OFFSET};Sk.token.ISEOF=function(b){return b==a.T_ENDMARKER};Sk.exportSymbol("Sk.token",Sk.token);Sk.exportSymbol("Sk.token.tokens",Sk.token.tokens);Sk.exportSymbol("Sk.token.tok_name",Sk.token.tok_name);Sk.exportSymbol("Sk.token.EXACT_TOKEN_TYPES");Sk.exportSymbol("Sk.token.ISTERMINAL",Sk.token.ISTERMINAL);Sk.exportSymbol("Sk.token.ISNONTERMINAL",
Sk.token.ISNONTERMINAL);Sk.exportSymbol("Sk.token.ISEOF",Sk.token.ISEOF)},function(m,p){function a(a,b,c,d,f){this.type=a;this.string=b;this.start=c;this.end=d;this.line=f}function b(a){return"("+Array.prototype.slice.call(arguments).join("|")+")"}function c(a){return b.apply(null,arguments)+"?"}function d(a,b){for(var c=a.length;c--;)if(a[c]===b)return!0;return!1}function e(){return" FR RF Br BR Fr r B R b bR f rb rB F Rf U rF u RB br fR fr rf Rb".split(" ")}var h=Sk.token.tokens;const g=Sk.builtin.SyntaxError,
f=Sk.builtin.SyntaxError;a.prototype.exact_type=function(){return this.type==h.T_OP&&this.string in Sk.token.EXACT_TOKEN_TYPES?Sk.token.EXACT_TOKEN_TYPES[this.string]:this.type};var k=/[\\^$.*+?()[\]{}|]/g,n=RegExp(k.source);const l=function(){var a=b("[A-Z]","[a-z]","[\\u{10B99}-\\u{10B9C}\\u{112A9}\\u{115DC}-\\u{115DD}\\u034F\\u115F-\\u1160\\u17B4-\\u17B5\\u2065\\u3164\\uFFA0\\uFFF0-\\uFFF8\\u{E0000}\\u{E0002}-\\u{E001F}\\u{E0080}-\\u{E00FF}\\u{E01F0}-\\u{E0FFF}\\u{112A9}\\u00D7]","[\\u02B0-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0374\\u037A\\u0559\\u06E5-\\u06E6\\u07F4-\\u07F5\\u0971\\u1C78-\\u1C7D\\u1D2C-\\u1D6A\\u1DFD-\\u1DFF\\u2E2F\\u30FC\\uA67F\\uA69C-\\uA69D\\uA717-\\uA71F\\uA788\\uA7F8-\\uA7F9\\uAB5C-\\uAB5F\\uFF70\\uFF9E-\\uFF9F\\u{16F93}-\\u{16F9F}\\u02D0-\\u02D1\\u0640\\u07FA\\u0E46\\u0EC6\\u1843\\u1AA7\\u1C7B\\u3005\\u3031-\\u3035\\u309D-\\u309E\\u30FC-\\u30FE\\uA015\\uA60C\\uA9CF\\uA9E6\\uAA70\\uAADD\\uAAF3-\\uAAF4\\uFF70\\u{16B42}-\\u{16B43}\\u{16FE0}-\\u{16FE1}\\u02B0-\\u02B8\\u02C0-\\u02C1\\u02E0-\\u02E4\\u037A\\u1D2C-\\u1D6A\\u1D78\\u1D9B-\\u1DBF\\u2071\\u207F\\u2090-\\u209C\\u2C7C-\\u2C7D\\uA69C-\\uA69D\\uA770\\uA7F8-\\uA7F9\\uAB5C-\\uAB5F\\uFF9E-\\uFF9F\\u02B2\\u1D62\\u1DA4\\u1DA8\\u2071\\u2C7C\\u2E18-\\u2E19\\u2E2F]",
"[\\u2135-\\u2138\\u{1EE00}-\\u{1EE03}\\u{1EE05}-\\u{1EE1F}\\u{1EE21}-\\u{1EE22}\\u{1EE24}\\u{1EE27}\\u{1EE29}-\\u{1EE32}\\u{1EE34}-\\u{1EE37}\\u{1EE39}\\u{1EE3B}\\u{1EE42}\\u{1EE47}\\u{1EE49}\\u{1EE4B}\\u{1EE4D}-\\u{1EE4F}\\u{1EE51}-\\u{1EE52}\\u{1EE54}\\u{1EE57}\\u{1EE59}\\u{1EE5B}\\u{1EE5D}\\u{1EE5F}\\u{1EE61}-\\u{1EE62}\\u{1EE64}\\u{1EE67}-\\u{1EE6A}\\u{1EE6C}-\\u{1EE72}\\u{1EE74}-\\u{1EE77}\\u{1EE79}-\\u{1EE7C}\\u{1EE7E}\\u{1EE80}-\\u{1EE89}\\u{1EE8B}-\\u{1EE9B}\\u{1EEA1}-\\u{1EEA3}\\u{1EEA5}-\\u{1EEA9}\\u{1EEAB}-\\u{1EEBB}\\u3006\\u3400-\\u4DB5\\u4E00-\\u9FEF\\uF900-\\uFA6D\\uFA70-\\uFAD9\\u{17000}-\\u{187F1}\\u{18800}-\\u{18AF2}\\u{1B170}-\\u{1B2FB}\\u{20000}-\\u{2A6D6}\\u{2A700}-\\u{2B734}\\u{2B740}-\\u{2B81D}\\u{2B820}-\\u{2CEA1}\\u{2CEB0}-\\u{2EBE0}\\u{2F800}-\\u{2FA1D}\\uAAC0\\uAAC2\\uFE20-\\uFE2F\\u{10D22}-\\u{10D23}\\u{1135D}\\u00AA\\u00BA\\u3400-\\u4DB5\\u4E00-\\u9FEF\\uFA0E-\\uFA0F\\uFA11\\uFA13-\\uFA14\\uFA1F\\uFA21\\uFA23-\\uFA24\\uFA27-\\uFA29\\u{20000}-\\u{2A6D6}\\u{2A700}-\\u{2B734}\\u{2B740}-\\u{2B81D}\\u{2B820}-\\u{2CEA1}\\u{2CEB0}-\\u{2EBE0}\\u115F-\\u1160\\u3164\\uFFA0\\u0673\\u17A3-\\u17A4\\u0E40-\\u0E44\\u0EC0-\\u0EC4\\u19B5-\\u19B7\\u19BA\\uAAB5-\\uAAB6\\uAAB9\\uAABB-\\uAABC]",
"[\\u3007\\u3021-\\u3029\\u3038-\\u303A\\u2170-\\u217F\\u2160-\\u216F]","_","[\\u1885-\\u1886\\u2118\\u212E\\u309B-\\u309C]"),c=b(a,"[\\u104A-\\u104B\\u102B-\\u102C\\u102D-\\u1030\\u1031\\u1032-\\u1036\\u1038\\u103B-\\u103C\\u103D-\\u103E\\u1056-\\u1057\\u1058-\\u1059\\u105E-\\u1060\\u1062\\u1067-\\u1068\\u1071-\\u1074\\u1082\\u1083-\\u1084\\u1085-\\u1086\\u109C\\u109D\\u1037\\u1039-\\u103A\\u1087-\\u108C\\u108D\\u108F\\u109A-\\u109B\\uA9E5\\uAA7B\\uAA7C\\uAA7D\\uA9E6\\uAA70\\u104A-\\u104B]","[\\u0903\\u093B\\u093E-\\u0940\\u0949-\\u094C\\u094E-\\u094F\\u0982-\\u0983\\u09BE-\\u09C0\\u09C7-\\u09C8\\u09CB-\\u09CC\\u09D7\\u0A03\\u0A3E-\\u0A40\\u0A83\\u0ABE-\\u0AC0\\u0AC9\\u0ACB-\\u0ACC\\u0B02-\\u0B03\\u0B3E\\u0B40\\u0B47-\\u0B48\\u0B4B-\\u0B4C\\u0B57\\u0BBE-\\u0BBF\\u0BC1-\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCC\\u0BD7\\u0C01-\\u0C03\\u0C41-\\u0C44\\u0C82-\\u0C83\\u0CBE\\u0CC0-\\u0CC4\\u0CC7-\\u0CC8\\u0CCA-\\u0CCB\\u0CD5-\\u0CD6\\u0D02-\\u0D03\\u0D3E-\\u0D40\\u0D46-\\u0D48\\u0D4A-\\u0D4C\\u0D57\\u0D82-\\u0D83\\u0DCF-\\u0DD1\\u0DD8-\\u0DDF\\u0DF2-\\u0DF3\\u0F7F\\u102B-\\u102C\\u1031\\u1038\\u103B-\\u103C\\u1056-\\u1057\\u1062\\u1067-\\u1068\\u1083-\\u1084\\u109C\\u17B6\\u17BE-\\u17C5\\u17C7-\\u17C8\\u1923-\\u1926\\u1929-\\u192B\\u1930-\\u1931\\u1933-\\u1938\\u1A19-\\u1A1A\\u1A55\\u1A57\\u1A61\\u1A63-\\u1A64\\u1A6D-\\u1A72\\u1B04\\u1B35\\u1B3B\\u1B3D-\\u1B41\\u1B43\\u1B82\\u1BA1\\u1BA6-\\u1BA7\\u1BE7\\u1BEA-\\u1BEC\\u1BEE\\u1C24-\\u1C2B\\u1C34-\\u1C35\\u1CF2-\\u1CF3\\uA823-\\uA824\\uA827\\uA880-\\uA881\\uA8B4-\\uA8C3\\uA952\\uA983\\uA9B4-\\uA9B5\\uA9BA-\\uA9BB\\uA9BD-\\uA9BF\\uAA2F-\\uAA30\\uAA33-\\uAA34\\uAA4D\\uAAEB\\uAAEE-\\uAAEF\\uAAF5\\uABE3-\\uABE4\\uABE6-\\uABE7\\uABE9-\\uABEA\\u{11000}\\u{11002}\\u{11082}\\u{110B0}-\\u{110B2}\\u{110B7}-\\u{110B8}\\u{1112C}\\u{11145}-\\u{11146}\\u{11182}\\u{111B3}-\\u{111B5}\\u{111BF}\\u{1122C}-\\u{1122E}\\u{11232}-\\u{11233}\\u{112E0}-\\u{112E2}\\u{11302}-\\u{11303}\\u{1133E}-\\u{1133F}\\u{11341}-\\u{11344}\\u{11347}-\\u{11348}\\u{1134B}-\\u{1134C}\\u{11357}\\u{11362}-\\u{11363}\\u{11435}-\\u{11437}\\u{11440}-\\u{11441}\\u{11445}\\u{114B0}-\\u{114B2}\\u{114B9}\\u{114BB}-\\u{114BE}\\u{114C1}\\u{115AF}-\\u{115B1}\\u{115B8}-\\u{115BB}\\u{115BE}\\u{11630}-\\u{11632}\\u{1163B}-\\u{1163C}\\u{1163E}\\u{116AC}\\u{116AE}-\\u{116AF}\\u{11720}-\\u{11721}\\u{11726}\\u{1182C}-\\u{1182E}\\u{11838}\\u{11A39}\\u{11A57}-\\u{11A58}\\u{11A97}\\u{11C2F}\\u{11C3E}\\u{11CA9}\\u{11CB1}\\u{11CB4}\\u{11D8A}-\\u{11D8E}\\u{11D93}-\\u{11D94}\\u{11D96}\\u{11EF5}-\\u{11EF6}\\u{16F51}-\\u{16F7E}\\u0F3E-\\u0F3F\\u1087-\\u108C\\u108F\\u109A-\\u109B\\u1B44\\u1BAA\\u1CE1\\u1CF7\\u302E-\\u302F\\uA953\\uA9C0\\uAA7B\\uAA7D\\uABEC\\u{111C0}\\u{11235}\\u{1134D}\\u{116B6}\\u{1D16D}-\\u{1D172}\\u09BE\\u09D7\\u0B3E\\u0B57\\u0BBE\\u0BD7\\u0CC2\\u0CD5-\\u0CD6\\u0D3E\\u0D57\\u0DCF\\u0DDF\\u302E-\\u302F\\u{1133E}\\u{11357}\\u{114B0}\\u{114BD}\\u{115AF}\\u{1D165}\\u{1D16E}-\\u{1D172}]",
"[\\u{1D7CE}-\\u{1D7FF}\\uFF10-\\uFF19]","\\u2040","[\\u00B7\\u0387\\u1369-\\u1371\\u19DA]");if(!1===RegExp().unicode)return new RegExp("^"+a+"+"+c+"*$","u");a=b("[A-Z]","[a-z]","_");c=b(a,"[0-9]");return new RegExp("^"+a+"+"+c+"*$")}();(function(a){return b.apply(null,arguments)+"*"})("\\\\\\r?\\n[ \\f\\t]*");c("#[^\\r\\n]*");m=b("[0-9](?:_?[0-9])*\\.(?:[0-9](?:_?[0-9])*)?","\\.[0-9](?:_?[0-9])*")+c("[eE][-+]?[0-9](?:_?[0-9])*");var q=b(m,"[0-9](?:_?[0-9])*[eE][-+]?[0-9](?:_?[0-9])*"),u=b("[0-9](?:_?[0-9])*[jJ]",
q+"[jJ]");m=b.apply(null,e());p=b(m+"'''",m+'"""');b(m+"'[^\\n'\\\\]*(?:\\\\.[^\\n'\\\\]*)*'",m+'"[^\\n"\\\\]*(?:\\\\.[^\\n"\\\\]*)*"');var z=Object.keys(Sk.token.EXACT_TOKEN_TYPES).sort();z=b.apply(this,z.reverse().map(function(a){return a&&n.test(a)?a.replace(k,"\\$&"):a}));var y=b("\\r?\\n",z),t=b(m+"'[^\\n'\\\\]*(?:\\\\.[^\\n'\\\\]*)*"+b("'","\\\\\\r?\\n"),m+'"[^\\n"\\\\]*(?:\\\\.[^\\n"\\\\]*)*'+b('"',"\\\\\\r?\\n")),H=b("\\\\\\r?\\n|$","#[^\\r\\n]*",p),K={};m=e();for(let a of m)K[a+"'"]="^[^'\\\\]*(?:\\\\.[^'\\\\]*)*'",
K[a+'"']='^[^"\\\\]*(?:\\\\.[^"\\\\]*)*"',K[a+"'''"]="^[^'\\\\]*(?:(?:\\\\.|'(?!''))[^'\\\\]*)*'''",K[a+'"""']='^[^"\\\\]*(?:(?:\\\\.|"(?!""))[^"\\\\]*)*"""';let C=[],F=[];for(let a of m)C.push(a+'"'),C.push(a+"'"),F.push(a+'"""'),F.push(a+"'''");Sk._tokenize=function(c,e,k,n){var m=Sk.__future__.python3?"":"(?:L?)";m=b("0[xX](?:_?[0-9a-fA-F])+"+m,"0[bB](?:_?[01])+"+m,Sk.__future__.silent_octal_literal?"0([oO]?)(?:_?[0-7])+"+m:"0([oO])(?:_?[0-7])+"+m,"(?:0(?:_?0)*|[1-9](?:_?[0-9])*)"+m);m=b(u,q,m);
m="[ \\f\\t]*"+b(H,m,y,t,"\\w+");m=new RegExp(m);var p=0,z=0,L=0,E="",W=0,N=null,M=[0],I,Z=void 0,Y=void 0,J,U;void 0!==k&&("utf-8-sig"==k&&(k="utf-8"),n(new a(h.T_ENCODING,k,[0,0],[0,0],"")));for(var A=k="";;){try{k=A,A=e()}catch(w){A=""}p+=1;var G=0,T=A.length;if(E){if(!A)throw new g("EOF in multi-line string",c,Y[0],Y[1]);Z.lastIndex=0;var x=Z.exec(A);if(x)G=J=x[0].length,n(new a(h.T_STRING,E+A.substring(0,J),Y,[p,J],N+A)),E="",W=0,N=null;else{W&&"\\\n"!==A.substring(A.length-2)&&"\\\r\n"!==A.substring(A.length-
3)?(n(new a(h.T_ERRORTOKEN,E+A,Y,[p,A.length],N)),E="",N=null):(E+=A,N+=A);continue}}else if(0!=z||L){if(!A)throw new g("EOF in multi-line statement",c,p,0);L=0}else{if(!A)break;for(I=0;G<T;){if(" "==A[G])I+=1;else if("\t"==A[G])I=8*Math.floor(I/8+1);else if("\f"==A[G])I=0;else break;G+=1}if(G==T)break;if(d("#\r\n",A[G])){if("#"==A[G]){I=A.substring(G);for(T=I.length;0<T&&-1!=="\r\n".indexOf(I.charAt(T-1));--T);T=I.substring(0,T);n(new a(h.T_COMMENT,T,[p,G],[p,G+T.length],A));G+=T.length}n(new a(h.T_NL,
A.substring(G),[p,G],[p,A.length],A));continue}I>M[M.length-1]&&(M.push(I),n(new a(h.T_INDENT,A.substring(G),[p,0],[p,G],A)));for(;I<M[M.length-1];){if(!d(M,I))throw new f("unindent does not match any outer indentation level",c,p,G);M=M.slice(0,-1);n(new a(h.T_DEDENT,"",[p,G],[p,G],A))}}for(;G<T;){for(I=A.charAt(G);" "===I||"\f"===I||"\t"===I;)G+=1,I=A.charAt(G);if(U=m.exec(A.substring(G))){if(I=G,J=I+U[1].length,U=[p,I],x=[p,J],G=J,I!=J){J=A.substring(I,J);var O=A[I];if(d("0123456789",O)||"."==O&&
"."!=J&&"..."!=J)n(new a(h.T_NUMBER,J,U,x,A));else if(d("\r\n",O))0<z?n(new a(h.T_NL,J,U,x,A)):n(new a(h.T_NEWLINE,J,U,x,A));else if("#"==O)n(new a(h.T_COMMENT,J,U,x,A));else if(d(F,J))if(Z=RegExp(K[J]),x=Z.exec(A.substring(G)))G=x[0].length+G,J=A.substring(I,G),n(new a(h.T_STRING,J,U,[p,G],A));else{Y=[p,I];E=A.substring(I);N=A;break}else if(d(C,O)||d(C,J.substring(0,2))||d(C,J.substring(0,3)))if("\n"==J[J.length-1]){Y=[p,I];Z=RegExp(K[O]||K[J[1]]||K[J[2]]);E=A.substring(I);W=1;N=A;break}else n(new a(h.T_STRING,
J,U,x,A));else I=O.normalize("NFKC"),l.test(I)?n(new a(h.T_NAME,J,U,x,A)):"\\"==O?L=1:(d("([{",O)?z+=1:d(")]}",O)&&--z,n(new a(h.T_OP,J,U,x,A)))}}else n(new a(h.T_ERRORTOKEN,A[G],[p,G],[p,G+1],A)),G+=1}}k&&!d("\r\n",k[k.length-1])&&n(new a(h.T_NEWLINE,"",[p-1,k.length],[p-1,k.length+1],""));for(var r in M.slice(1))n(new a(h.T_DEDENT,"",[p,0],[p,0],""));n(new a(h.T_ENDMARKER,"",[p,0],[p,0],""))};Sk.exportSymbol("Sk._tokenize",Sk._tokenize)},function(m,p){Sk.OpMap={"(":Sk.token.tokens.T_LPAR,")":Sk.token.tokens.T_RPAR,
"[":Sk.token.tokens.T_LSQB,"]":Sk.token.tokens.T_RSQB,":":Sk.token.tokens.T_COLON,",":Sk.token.tokens.T_COMMA,";":Sk.token.tokens.T_SEMI,"+":Sk.token.tokens.T_PLUS,"-":Sk.token.tokens.T_MINUS,"*":Sk.token.tokens.T_STAR,"/":Sk.token.tokens.T_SLASH,"|":Sk.token.tokens.T_VBAR,"&":Sk.token.tokens.T_AMPER,"<":Sk.token.tokens.T_LESS,">":Sk.token.tokens.T_GREATER,"=":Sk.token.tokens.T_EQUAL,".":Sk.token.tokens.T_DOT,"%":Sk.token.tokens.T_PERCENT,"`":Sk.token.tokens.T_BACKQUOTE,"{":Sk.token.tokens.T_LBRACE,
"}":Sk.token.tokens.T_RBRACE,"@":Sk.token.tokens.T_AT,"@=":Sk.token.tokens.T_ATEQUAL,"==":Sk.token.tokens.T_EQEQUAL,"!=":Sk.token.tokens.T_NOTEQUAL,"<>":Sk.token.tokens.T_NOTEQUAL,"<=":Sk.token.tokens.T_LESSEQUAL,">=":Sk.token.tokens.T_GREATEREQUAL,"~":Sk.token.tokens.T_TILDE,"^":Sk.token.tokens.T_CIRCUMFLEX,"<<":Sk.token.tokens.T_LEFTSHIFT,">>":Sk.token.tokens.T_RIGHTSHIFT,"**":Sk.token.tokens.T_DOUBLESTAR,"+=":Sk.token.tokens.T_PLUSEQUAL,"-=":Sk.token.tokens.T_MINEQUAL,"*=":Sk.token.tokens.T_STAREQUAL,
"/=":Sk.token.tokens.T_SLASHEQUAL,"%=":Sk.token.tokens.T_PERCENTEQUAL,"&=":Sk.token.tokens.T_AMPEREQUAL,"|=":Sk.token.tokens.T_VBAREQUAL,"^=":Sk.token.tokens.T_CIRCUMFLEXEQUAL,"<<=":Sk.token.tokens.T_LEFTSHIFTEQUAL,">>=":Sk.token.tokens.T_RIGHTSHIFTEQUAL,"**=":Sk.token.tokens.T_DOUBLESTAREQUAL,"//":Sk.token.tokens.T_DOUBLESLASH,"//=":Sk.token.tokens.T_DOUBLESLASHEQUAL,"->":Sk.token.tokens.T_RARROW,"...":Sk.token.tokens.T_ELLIPSIS};Sk.ParseTables={sym:{and_expr:257,and_test:258,annassign:259,arglist:260,
argument:261,arith_expr:262,assert_stmt:263,async_funcdef:264,async_stmt:265,atom:266,atom_expr:267,augassign:268,break_stmt:269,classdef:270,comp_for:271,comp_if:272,comp_iter:273,comp_op:274,comparison:275,compound_stmt:276,continue_stmt:277,debugger_stmt:278,decorated:279,decorator:280,decorators:281,del_stmt:282,dictorsetmaker:283,dotted_as_name:284,dotted_as_names:285,dotted_name:286,encoding_decl:287,eval_input:288,except_clause:289,expr:290,expr_stmt:291,exprlist:292,factor:293,file_input:294,
flow_stmt:295,for_stmt:296,funcdef:297,global_stmt:298,if_stmt:299,import_as_name:300,import_as_names:301,import_from:302,import_name:303,import_stmt:304,lambdef:305,lambdef_nocond:306,nonlocal_stmt:307,not_test:308,or_test:309,parameters:310,pass_stmt:311,power:312,print_stmt:313,raise_stmt:314,return_stmt:315,shift_expr:316,simple_stmt:317,single_input:256,sliceop:318,small_stmt:319,star_expr:320,stmt:321,subscript:322,subscriptlist:323,suite:324,term:325,test:326,test_nocond:327,testlist:328,testlist_comp:329,
testlist_star_expr:330,tfpdef:331,trailer:332,try_stmt:333,typedargslist:334,varargslist:335,vfpdef:336,while_stmt:337,with_item:338,with_stmt:339,xor_expr:340,yield_arg:341,yield_expr:342,yield_stmt:343},number2symbol:{256:"single_input",257:"and_expr",258:"and_test",259:"annassign",260:"arglist",261:"argument",262:"arith_expr",263:"assert_stmt",264:"async_funcdef",265:"async_stmt",266:"atom",267:"atom_expr",268:"augassign",269:"break_stmt",270:"classdef",271:"comp_for",272:"comp_if",273:"comp_iter",
274:"comp_op",275:"comparison",276:"compound_stmt",277:"continue_stmt",278:"debugger_stmt",279:"decorated",280:"decorator",281:"decorators",282:"del_stmt",283:"dictorsetmaker",284:"dotted_as_name",285:"dotted_as_names",286:"dotted_name",287:"encoding_decl",288:"eval_input",289:"except_clause",290:"expr",291:"expr_stmt",292:"exprlist",293:"factor",294:"file_input",295:"flow_stmt",296:"for_stmt",297:"funcdef",298:"global_stmt",299:"if_stmt",300:"import_as_name",301:"import_as_names",302:"import_from",
303:"import_name",304:"import_stmt",305:"lambdef",306:"lambdef_nocond",307:"nonlocal_stmt",308:"not_test",309:"or_test",310:"parameters",311:"pass_stmt",312:"power",313:"print_stmt",314:"raise_stmt",315:"return_stmt",316:"shift_expr",317:"simple_stmt",318:"sliceop",319:"small_stmt",320:"star_expr",321:"stmt",322:"subscript",323:"subscriptlist",324:"suite",325:"term",326:"test",327:"test_nocond",328:"testlist",329:"testlist_comp",330:"testlist_star_expr",331:"tfpdef",332:"trailer",333:"try_stmt",334:"typedargslist",
335:"varargslist",336:"vfpdef",337:"while_stmt",338:"with_item",339:"with_stmt",340:"xor_expr",341:"yield_arg",342:"yield_expr",343:"yield_stmt"},dfas:{256:[[[[1,1],[2,1],[3,2]],[[0,1]],[[2,1]]],{2:1,4:1,5:1,6:1,7:1,8:1,9:1,10:1,11:1,12:1,13:1,14:1,15:1,16:1,17:1,18:1,19:1,20:1,21:1,22:1,23:1,24:1,25:1,26:1,27:1,28:1,29:1,30:1,31:1,32:1,33:1,34:1,35:1,36:1,37:1,38:1,39:1,40:1,41:1,42:1,43:1}],257:[[[[44,1]],[[45,0],[0,1]]],{6:1,7:1,9:1,11:1,12:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,38:1,43:1}],258:[[[[46,
1]],[[47,0],[0,1]]],{6:1,7:1,8:1,9:1,11:1,12:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,38:1,43:1}],259:[[[[48,1]],[[49,2]],[[50,3],[0,2]],[[49,4]],[[0,4]]],{48:1}],260:[[[[51,1]],[[52,2],[0,1]],[[51,1],[0,2]]],{6:1,7:1,8:1,9:1,11:1,12:1,14:1,15:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,38:1,43:1,53:1}],261:[[[[49,1],[15,2],[53,2]],[[50,2],[54,3],[0,1]],[[49,3]],[[0,3]]],{6:1,7:1,8:1,9:1,11:1,12:1,14:1,15:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,38:1,43:1,53:1}],262:[[[[55,1]],[[30,0],[43,0],[0,1]]],{6:1,7:1,9:1,
11:1,12:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,38:1,43:1}],263:[[[[24,1]],[[49,2]],[[52,3],[0,2]],[[49,4]],[[0,4]]],{24:1}],264:[[[[10,1]],[[56,2]],[[0,2]]],{10:1}],265:[[[[10,1]],[[57,2],[56,2],[58,2]],[[0,2]]],{10:1}],266:[[[[6,1],[25,1],[33,1],[9,1],[11,1],[12,2],[35,3],[38,4],[19,1],[7,5]],[[0,1]],[[59,1],[60,6]],[[61,1],[62,7],[63,7]],[[64,1],[63,8]],[[7,5],[0,5]],[[59,1]],[[61,1]],[[64,1]]],{6:1,7:1,9:1,11:1,12:1,19:1,25:1,33:1,35:1,38:1}],267:[[[[29,1],[65,2]],[[65,2]],[[66,2],[0,2]]],{6:1,7:1,
9:1,11:1,12:1,19:1,25:1,29:1,33:1,35:1,38:1}],268:[[[[67,1],[68,1],[69,1],[70,1],[71,1],[72,1],[73,1],[74,1],[75,1],[76,1],[77,1],[78,1],[79,1]],[[0,1]]],{67:1,68:1,69:1,70:1,71:1,72:1,73:1,74:1,75:1,76:1,77:1,78:1,79:1}],269:[[[[39,1]],[[0,1]]],{39:1}],270:[[[[13,1]],[[25,2]],[[48,3],[35,4]],[[80,5]],[[61,6],[81,7]],[[0,5]],[[48,3]],[[61,6]]],{13:1}],271:[[[[10,1],[34,2]],[[34,2]],[[82,3]],[[83,4]],[[84,5]],[[85,6],[0,5]],[[0,6]]],{10:1,34:1}],272:[[[[37,1]],[[86,2]],[[85,3],[0,2]],[[0,3]]],{37:1}],
273:[[[[87,1],[54,1]],[[0,1]]],{10:1,34:1,37:1}],274:[[[[88,1],[89,1],[8,2],[90,1],[88,1],[83,1],[91,1],[92,3],[93,1],[94,1]],[[0,1]],[[83,1]],[[8,1],[0,3]]],{8:1,83:1,88:1,89:1,90:1,91:1,92:1,93:1,94:1}],275:[[[[95,1]],[[96,0],[0,1]]],{6:1,7:1,9:1,11:1,12:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,38:1,43:1}],276:[[[[97,1],[98,1],[58,1],[99,1],[57,1],[100,1],[56,1],[101,1],[102,1]],[[0,1]]],{4:1,10:1,13:1,20:1,21:1,34:1,37:1,41:1,42:1}],277:[[[[40,1]],[[0,1]]],{40:1}],278:[[[[17,1]],[[0,1]]],{17:1}],279:[[[[103,
1]],[[56,2],[104,2],[99,2]],[[0,2]]],{41:1}],280:[[[[41,1]],[[105,2]],[[2,4],[35,3]],[[61,5],[81,6]],[[0,4]],[[2,4]],[[61,5]]],{41:1}],281:[[[[106,1]],[[106,1],[0,1]]],{41:1}],282:[[[[27,1]],[[82,2]],[[0,2]]],{27:1}],283:[[[[49,1],[107,2],[53,3]],[[48,4],[54,5],[52,6],[0,1]],[[54,5],[52,6],[0,2]],[[95,7]],[[49,7]],[[0,5]],[[49,8],[107,8],[0,6]],[[54,5],[52,9],[0,7]],[[52,6],[0,8]],[[49,10],[53,11],[0,9]],[[48,12]],[[95,13]],[[49,13]],[[52,9],[0,13]]],{6:1,7:1,8:1,9:1,11:1,12:1,14:1,15:1,19:1,22:1,
25:1,29:1,30:1,33:1,35:1,38:1,43:1,53:1}],284:[[[[105,1]],[[108,2],[0,1]],[[25,3]],[[0,3]]],{25:1}],285:[[[[109,1]],[[52,0],[0,1]]],{25:1}],286:[[[[25,1]],[[110,0],[0,1]]],{25:1}],287:[[[[25,1]],[[0,1]]],{25:1}],288:[[[[111,1]],[[2,1],[112,2]],[[0,2]]],{6:1,7:1,8:1,9:1,11:1,12:1,14:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,38:1,43:1}],289:[[[[113,1]],[[49,2],[0,1]],[[108,3],[52,3],[0,2]],[[49,4]],[[0,4]]],{113:1}],290:[[[[114,1]],[[115,0],[0,1]]],{6:1,7:1,9:1,11:1,12:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,
38:1,43:1}],291:[[[[116,1]],[[117,2],[50,3],[118,4],[0,1]],[[111,4],[62,4]],[[116,5],[62,5]],[[0,4]],[[50,3],[0,5]]],{6:1,7:1,8:1,9:1,11:1,12:1,14:1,15:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,38:1,43:1}],292:[[[[95,1],[107,1]],[[52,2],[0,1]],[[95,1],[107,1],[0,2]]],{6:1,7:1,9:1,11:1,12:1,15:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,38:1,43:1}],293:[[[[119,2],[30,1],[22,1],[43,1]],[[120,2]],[[0,2]]],{6:1,7:1,9:1,11:1,12:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,38:1,43:1}],294:[[[[2,0],[112,1],[121,0]],[[0,1]]],
{2:1,4:1,5:1,6:1,7:1,8:1,9:1,10:1,11:1,12:1,13:1,14:1,15:1,16:1,17:1,18:1,19:1,20:1,21:1,22:1,23:1,24:1,25:1,26:1,27:1,28:1,29:1,30:1,31:1,32:1,33:1,34:1,35:1,36:1,37:1,38:1,39:1,40:1,41:1,42:1,43:1,112:1}],295:[[[[122,1],[123,1],[124,1],[125,1],[126,1]],[[0,1]]],{5:1,23:1,31:1,39:1,40:1}],296:[[[[34,1]],[[82,2]],[[83,3]],[[111,4]],[[48,5]],[[80,6]],[[127,7],[0,6]],[[48,8]],[[80,9]],[[0,9]]],{34:1}],297:[[[[4,1]],[[25,2]],[[128,3]],[[48,4],[129,5]],[[80,6]],[[49,7]],[[0,6]],[[48,4]]],{4:1}],298:[[[[26,
1]],[[25,2]],[[52,1],[0,2]]],{26:1}],299:[[[[37,1]],[[49,2]],[[48,3]],[[80,4]],[[127,5],[130,1],[0,4]],[[48,6]],[[80,7]],[[0,7]]],{37:1}],300:[[[[25,1]],[[108,2],[0,1]],[[25,3]],[[0,3]]],{25:1}],301:[[[[131,1]],[[52,2],[0,1]],[[131,1],[0,2]]],{25:1}],302:[[[[36,1]],[[105,2],[19,3],[110,3]],[[32,4]],[[105,2],[19,3],[32,4],[110,3]],[[132,5],[15,5],[35,6]],[[0,5]],[[132,7]],[[61,5]]],{36:1}],303:[[[[32,1]],[[133,2]],[[0,2]]],{32:1}],304:[[[[134,1],[135,1]],[[0,1]]],{32:1,36:1}],305:[[[[14,1]],[[48,2],
[136,3]],[[49,4]],[[48,2]],[[0,4]]],{14:1}],306:[[[[14,1]],[[48,2],[136,3]],[[86,4]],[[48,2]],[[0,4]]],{14:1}],307:[[[[18,1]],[[25,2]],[[52,1],[0,2]]],{18:1}],308:[[[[8,1],[137,2]],[[46,2]],[[0,2]]],{6:1,7:1,8:1,9:1,11:1,12:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,38:1,43:1}],309:[[[[138,1]],[[139,0],[0,1]]],{6:1,7:1,8:1,9:1,11:1,12:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,38:1,43:1}],310:[[[[35,1]],[[61,2],[140,3]],[[0,2]],[[61,2]]],{35:1}],311:[[[[28,1]],[[0,1]]],{28:1}],312:[[[[141,1]],[[53,2],[0,1]],
[[120,3]],[[0,3]]],{6:1,7:1,9:1,11:1,12:1,19:1,25:1,29:1,33:1,35:1,38:1}],313:[[[[16,1]],[[49,2],[142,3],[0,1]],[[52,4],[0,2]],[[49,5]],[[49,2],[0,4]],[[52,6],[0,5]],[[49,7]],[[52,8],[0,7]],[[49,7],[0,8]]],{16:1}],314:[[[[5,1]],[[49,2],[0,1]],[[36,3],[52,3],[0,2]],[[49,4]],[[52,5],[0,4]],[[49,6]],[[0,6]]],{5:1}],315:[[[[23,1]],[[111,2],[0,1]],[[0,2]]],{23:1}],316:[[[[143,1]],[[144,0],[142,0],[0,1]]],{6:1,7:1,9:1,11:1,12:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,38:1,43:1}],317:[[[[145,1]],[[2,2],[146,
3]],[[0,2]],[[145,1],[2,2]]],{5:1,6:1,7:1,8:1,9:1,11:1,12:1,14:1,15:1,16:1,17:1,18:1,19:1,22:1,23:1,24:1,25:1,26:1,27:1,28:1,29:1,30:1,31:1,32:1,33:1,35:1,36:1,38:1,39:1,40:1,43:1}],318:[[[[48,1]],[[49,2],[0,1]],[[0,2]]],{48:1}],319:[[[[147,1],[148,1],[149,1],[150,1],[151,1],[152,1],[153,1],[154,1],[155,1],[156,1]],[[0,1]]],{5:1,6:1,7:1,8:1,9:1,11:1,12:1,14:1,15:1,16:1,17:1,18:1,19:1,22:1,23:1,24:1,25:1,26:1,27:1,28:1,29:1,30:1,31:1,32:1,33:1,35:1,36:1,38:1,39:1,40:1,43:1}],320:[[[[15,1]],[[95,2]],
[[0,2]]],{15:1}],321:[[[[1,1],[3,1]],[[0,1]]],{4:1,5:1,6:1,7:1,8:1,9:1,10:1,11:1,12:1,13:1,14:1,15:1,16:1,17:1,18:1,19:1,20:1,21:1,22:1,23:1,24:1,25:1,26:1,27:1,28:1,29:1,30:1,31:1,32:1,33:1,34:1,35:1,36:1,37:1,38:1,39:1,40:1,41:1,42:1,43:1}],322:[[[[49,1],[48,2]],[[48,2],[0,1]],[[49,3],[157,4],[0,2]],[[157,4],[0,3]],[[0,4]]],{6:1,7:1,8:1,9:1,11:1,12:1,14:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,38:1,43:1,48:1}],323:[[[[158,1]],[[52,2],[0,1]],[[158,1],[0,2]]],{6:1,7:1,8:1,9:1,11:1,12:1,14:1,19:1,22:1,
25:1,29:1,30:1,33:1,35:1,38:1,43:1,48:1}],324:[[[[1,1],[2,2]],[[0,1]],[[159,3]],[[121,4]],[[160,1],[121,4]]],{2:1,5:1,6:1,7:1,8:1,9:1,11:1,12:1,14:1,15:1,16:1,17:1,18:1,19:1,22:1,23:1,24:1,25:1,26:1,27:1,28:1,29:1,30:1,31:1,32:1,33:1,35:1,36:1,38:1,39:1,40:1,43:1}],325:[[[[120,1]],[[161,0],[15,0],[162,0],[41,0],[163,0],[0,1]]],{6:1,7:1,9:1,11:1,12:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,38:1,43:1}],326:[[[[84,1],[164,2]],[[37,3],[0,1]],[[0,2]],[[84,4]],[[127,5]],[[49,2]]],{6:1,7:1,8:1,9:1,11:1,12:1,
14:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,38:1,43:1}],327:[[[[165,1],[84,1]],[[0,1]]],{6:1,7:1,8:1,9:1,11:1,12:1,14:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,38:1,43:1}],328:[[[[49,1]],[[52,2],[0,1]],[[49,1],[0,2]]],{6:1,7:1,8:1,9:1,11:1,12:1,14:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,38:1,43:1}],329:[[[[49,1],[107,1]],[[54,2],[52,3],[0,1]],[[0,2]],[[49,4],[107,4],[0,3]],[[52,3],[0,4]]],{6:1,7:1,8:1,9:1,11:1,12:1,14:1,15:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,38:1,43:1}],330:[[[[49,1],[107,1]],[[52,2],[0,1]],
[[49,1],[107,1],[0,2]]],{6:1,7:1,8:1,9:1,11:1,12:1,14:1,15:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,38:1,43:1}],331:[[[[25,1]],[[48,2],[0,1]],[[49,3]],[[0,3]]],{25:1}],332:[[[[35,1],[110,2],[38,3]],[[61,4],[81,5]],[[25,4]],[[166,6]],[[0,4]],[[61,4]],[[64,4]]],{35:1,38:1,110:1}],333:[[[[20,1]],[[48,2]],[[80,3]],[[167,4],[168,5]],[[48,6]],[[48,7]],[[80,8]],[[80,9]],[[167,4],[127,10],[168,5],[0,8]],[[0,9]],[[48,11]],[[80,12]],[[168,5],[0,12]]],{20:1}],334:[[[[15,1],[169,2],[53,3]],[[169,4],[52,5],[0,1]],
[[50,6],[52,7],[0,2]],[[169,8]],[[52,5],[0,4]],[[169,9],[53,3],[0,5]],[[49,10]],[[15,11],[169,2],[53,3],[0,7]],[[52,12],[0,8]],[[50,13],[52,5],[0,9]],[[52,7],[0,10]],[[169,14],[52,15],[0,11]],[[0,12]],[[49,4]],[[52,15],[0,14]],[[169,16],[53,3],[0,15]],[[50,17],[52,15],[0,16]],[[49,14]]],{15:1,25:1,53:1}],335:[[[[15,1],[53,2],[170,3]],[[170,5],[52,4],[0,1]],[[170,6]],[[50,7],[52,8],[0,3]],[[53,2],[170,9],[0,4]],[[52,4],[0,5]],[[52,10],[0,6]],[[49,11]],[[15,12],[53,2],[170,3],[0,8]],[[50,13],[52,4],
[0,9]],[[0,10]],[[52,8],[0,11]],[[52,15],[170,14],[0,12]],[[49,5]],[[52,15],[0,14]],[[53,2],[170,16],[0,15]],[[50,17],[52,15],[0,16]],[[49,14]]],{15:1,25:1,53:1}],336:[[[[25,1]],[[0,1]]],{25:1}],337:[[[[21,1]],[[49,2]],[[48,3]],[[80,4]],[[127,5],[0,4]],[[48,6]],[[80,7]],[[0,7]]],{21:1}],338:[[[[49,1]],[[108,2],[0,1]],[[95,3]],[[0,3]]],{6:1,7:1,8:1,9:1,11:1,12:1,14:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,38:1,43:1}],339:[[[[42,1]],[[171,2]],[[48,3],[52,1]],[[80,4]],[[0,4]]],{42:1}],340:[[[[172,1]],[[173,
0],[0,1]]],{6:1,7:1,9:1,11:1,12:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,38:1,43:1}],341:[[[[111,2],[36,1]],[[49,2]],[[0,2]]],{6:1,7:1,8:1,9:1,11:1,12:1,14:1,19:1,22:1,25:1,29:1,30:1,33:1,35:1,36:1,38:1,43:1}],342:[[[[31,1]],[[174,2],[0,1]],[[0,2]]],{31:1}],343:[[[[62,1]],[[0,1]]],{31:1}]},states:[[[[1,1],[2,1],[3,2]],[[0,1]],[[2,1]]],[[[44,1]],[[45,0],[0,1]]],[[[46,1]],[[47,0],[0,1]]],[[[48,1]],[[49,2]],[[50,3],[0,2]],[[49,4]],[[0,4]]],[[[51,1]],[[52,2],[0,1]],[[51,1],[0,2]]],[[[49,1],[15,2],[53,2]],
[[50,2],[54,3],[0,1]],[[49,3]],[[0,3]]],[[[55,1]],[[30,0],[43,0],[0,1]]],[[[24,1]],[[49,2]],[[52,3],[0,2]],[[49,4]],[[0,4]]],[[[10,1]],[[56,2]],[[0,2]]],[[[10,1]],[[57,2],[56,2],[58,2]],[[0,2]]],[[[6,1],[25,1],[33,1],[9,1],[11,1],[12,2],[35,3],[38,4],[19,1],[7,5]],[[0,1]],[[59,1],[60,6]],[[61,1],[62,7],[63,7]],[[64,1],[63,8]],[[7,5],[0,5]],[[59,1]],[[61,1]],[[64,1]]],[[[29,1],[65,2]],[[65,2]],[[66,2],[0,2]]],[[[67,1],[68,1],[69,1],[70,1],[71,1],[72,1],[73,1],[74,1],[75,1],[76,1],[77,1],[78,1],[79,
1]],[[0,1]]],[[[39,1]],[[0,1]]],[[[13,1]],[[25,2]],[[48,3],[35,4]],[[80,5]],[[61,6],[81,7]],[[0,5]],[[48,3]],[[61,6]]],[[[10,1],[34,2]],[[34,2]],[[82,3]],[[83,4]],[[84,5]],[[85,6],[0,5]],[[0,6]]],[[[37,1]],[[86,2]],[[85,3],[0,2]],[[0,3]]],[[[87,1],[54,1]],[[0,1]]],[[[88,1],[89,1],[8,2],[90,1],[88,1],[83,1],[91,1],[92,3],[93,1],[94,1]],[[0,1]],[[83,1]],[[8,1],[0,3]]],[[[95,1]],[[96,0],[0,1]]],[[[97,1],[98,1],[58,1],[99,1],[57,1],[100,1],[56,1],[101,1],[102,1]],[[0,1]]],[[[40,1]],[[0,1]]],[[[17,1]],
[[0,1]]],[[[103,1]],[[56,2],[104,2],[99,2]],[[0,2]]],[[[41,1]],[[105,2]],[[2,4],[35,3]],[[61,5],[81,6]],[[0,4]],[[2,4]],[[61,5]]],[[[106,1]],[[106,1],[0,1]]],[[[27,1]],[[82,2]],[[0,2]]],[[[49,1],[107,2],[53,3]],[[48,4],[54,5],[52,6],[0,1]],[[54,5],[52,6],[0,2]],[[95,7]],[[49,7]],[[0,5]],[[49,8],[107,8],[0,6]],[[54,5],[52,9],[0,7]],[[52,6],[0,8]],[[49,10],[53,11],[0,9]],[[48,12]],[[95,13]],[[49,13]],[[52,9],[0,13]]],[[[105,1]],[[108,2],[0,1]],[[25,3]],[[0,3]]],[[[109,1]],[[52,0],[0,1]]],[[[25,1]],
[[110,0],[0,1]]],[[[25,1]],[[0,1]]],[[[111,1]],[[2,1],[112,2]],[[0,2]]],[[[113,1]],[[49,2],[0,1]],[[108,3],[52,3],[0,2]],[[49,4]],[[0,4]]],[[[114,1]],[[115,0],[0,1]]],[[[116,1]],[[117,2],[50,3],[118,4],[0,1]],[[111,4],[62,4]],[[116,5],[62,5]],[[0,4]],[[50,3],[0,5]]],[[[95,1],[107,1]],[[52,2],[0,1]],[[95,1],[107,1],[0,2]]],[[[119,2],[30,1],[22,1],[43,1]],[[120,2]],[[0,2]]],[[[2,0],[112,1],[121,0]],[[0,1]]],[[[122,1],[123,1],[124,1],[125,1],[126,1]],[[0,1]]],[[[34,1]],[[82,2]],[[83,3]],[[111,4]],[[48,
5]],[[80,6]],[[127,7],[0,6]],[[48,8]],[[80,9]],[[0,9]]],[[[4,1]],[[25,2]],[[128,3]],[[48,4],[129,5]],[[80,6]],[[49,7]],[[0,6]],[[48,4]]],[[[26,1]],[[25,2]],[[52,1],[0,2]]],[[[37,1]],[[49,2]],[[48,3]],[[80,4]],[[127,5],[130,1],[0,4]],[[48,6]],[[80,7]],[[0,7]]],[[[25,1]],[[108,2],[0,1]],[[25,3]],[[0,3]]],[[[131,1]],[[52,2],[0,1]],[[131,1],[0,2]]],[[[36,1]],[[105,2],[19,3],[110,3]],[[32,4]],[[105,2],[19,3],[32,4],[110,3]],[[132,5],[15,5],[35,6]],[[0,5]],[[132,7]],[[61,5]]],[[[32,1]],[[133,2]],[[0,2]]],
[[[134,1],[135,1]],[[0,1]]],[[[14,1]],[[48,2],[136,3]],[[49,4]],[[48,2]],[[0,4]]],[[[14,1]],[[48,2],[136,3]],[[86,4]],[[48,2]],[[0,4]]],[[[18,1]],[[25,2]],[[52,1],[0,2]]],[[[8,1],[137,2]],[[46,2]],[[0,2]]],[[[138,1]],[[139,0],[0,1]]],[[[35,1]],[[61,2],[140,3]],[[0,2]],[[61,2]]],[[[28,1]],[[0,1]]],[[[141,1]],[[53,2],[0,1]],[[120,3]],[[0,3]]],[[[16,1]],[[49,2],[142,3],[0,1]],[[52,4],[0,2]],[[49,5]],[[49,2],[0,4]],[[52,6],[0,5]],[[49,7]],[[52,8],[0,7]],[[49,7],[0,8]]],[[[5,1]],[[49,2],[0,1]],[[36,3],
[52,3],[0,2]],[[49,4]],[[52,5],[0,4]],[[49,6]],[[0,6]]],[[[23,1]],[[111,2],[0,1]],[[0,2]]],[[[143,1]],[[144,0],[142,0],[0,1]]],[[[145,1]],[[2,2],[146,3]],[[0,2]],[[145,1],[2,2]]],[[[48,1]],[[49,2],[0,1]],[[0,2]]],[[[147,1],[148,1],[149,1],[150,1],[151,1],[152,1],[153,1],[154,1],[155,1],[156,1]],[[0,1]]],[[[15,1]],[[95,2]],[[0,2]]],[[[1,1],[3,1]],[[0,1]]],[[[49,1],[48,2]],[[48,2],[0,1]],[[49,3],[157,4],[0,2]],[[157,4],[0,3]],[[0,4]]],[[[158,1]],[[52,2],[0,1]],[[158,1],[0,2]]],[[[1,1],[2,2]],[[0,1]],
[[159,3]],[[121,4]],[[160,1],[121,4]]],[[[120,1]],[[161,0],[15,0],[162,0],[41,0],[163,0],[0,1]]],[[[84,1],[164,2]],[[37,3],[0,1]],[[0,2]],[[84,4]],[[127,5]],[[49,2]]],[[[165,1],[84,1]],[[0,1]]],[[[49,1]],[[52,2],[0,1]],[[49,1],[0,2]]],[[[49,1],[107,1]],[[54,2],[52,3],[0,1]],[[0,2]],[[49,4],[107,4],[0,3]],[[52,3],[0,4]]],[[[49,1],[107,1]],[[52,2],[0,1]],[[49,1],[107,1],[0,2]]],[[[25,1]],[[48,2],[0,1]],[[49,3]],[[0,3]]],[[[35,1],[110,2],[38,3]],[[61,4],[81,5]],[[25,4]],[[166,6]],[[0,4]],[[61,4]],[[64,
4]]],[[[20,1]],[[48,2]],[[80,3]],[[167,4],[168,5]],[[48,6]],[[48,7]],[[80,8]],[[80,9]],[[167,4],[127,10],[168,5],[0,8]],[[0,9]],[[48,11]],[[80,12]],[[168,5],[0,12]]],[[[15,1],[169,2],[53,3]],[[169,4],[52,5],[0,1]],[[50,6],[52,7],[0,2]],[[169,8]],[[52,5],[0,4]],[[169,9],[53,3],[0,5]],[[49,10]],[[15,11],[169,2],[53,3],[0,7]],[[52,12],[0,8]],[[50,13],[52,5],[0,9]],[[52,7],[0,10]],[[169,14],[52,15],[0,11]],[[0,12]],[[49,4]],[[52,15],[0,14]],[[169,16],[53,3],[0,15]],[[50,17],[52,15],[0,16]],[[49,14]]],
[[[15,1],[53,2],[170,3]],[[170,5],[52,4],[0,1]],[[170,6]],[[50,7],[52,8],[0,3]],[[53,2],[170,9],[0,4]],[[52,4],[0,5]],[[52,10],[0,6]],[[49,11]],[[15,12],[53,2],[170,3],[0,8]],[[50,13],[52,4],[0,9]],[[0,10]],[[52,8],[0,11]],[[52,15],[170,14],[0,12]],[[49,5]],[[52,15],[0,14]],[[53,2],[170,16],[0,15]],[[50,17],[52,15],[0,16]],[[49,14]]],[[[25,1]],[[0,1]]],[[[21,1]],[[49,2]],[[48,3]],[[80,4]],[[127,5],[0,4]],[[48,6]],[[80,7]],[[0,7]]],[[[49,1]],[[108,2],[0,1]],[[95,3]],[[0,3]]],[[[42,1]],[[171,2]],[[48,
3],[52,1]],[[80,4]],[[0,4]]],[[[172,1]],[[173,0],[0,1]]],[[[111,2],[36,1]],[[49,2]],[[0,2]]],[[[31,1]],[[174,2],[0,1]],[[0,2]]],[[[62,1]],[[0,1]]]],labels:[[0,"EMPTY"],[317,null],[4,null],[276,null],[1,"def"],[1,"raise"],[1,"True"],[3,null],[1,"not"],[1,"null"],[55,null],[2,null],[25,null],[1,"class"],[1,"lambda"],[16,null],[1,"print"],[1,"debugger"],[1,"nonlocal"],[52,null],[1,"try"],[1,"while"],[31,null],[1,"return"],[1,"assert"],[1,null],[1,"global"],[1,"del"],[1,"pass"],[54,null],[15,null],[1,
"yield"],[1,"import"],[1,"False"],[1,"for"],[7,null],[1,"from"],[1,"if"],[9,null],[1,"break"],[1,"continue"],[49,null],[1,"with"],[14,null],[316,null],[19,null],[308,null],[1,"and"],[11,null],[326,null],[22,null],[261,null],[12,null],[35,null],[271,null],[325,null],[297,null],[339,null],[296,null],[26,null],[283,null],[8,null],[342,null],[329,null],[10,null],[266,null],[332,null],[45,null],[38,null],[40,null],[50,null],[46,null],[41,null],[42,null],[36,null],[43,null],[48,null],[44,null],[37,null],
[39,null],[324,null],[260,null],[292,null],[1,"in"],[309,null],[273,null],[327,null],[272,null],[28,null],[21,null],[27,null],[29,null],[1,"is"],[30,null],[20,null],[290,null],[274,null],[333,null],[299,null],[270,null],[337,null],[279,null],[265,null],[281,null],[264,null],[286,null],[280,null],[320,null],[1,"as"],[284,null],[23,null],[328,null],[0,null],[1,"except"],[340,null],[18,null],[330,null],[268,null],[259,null],[312,null],[293,null],[321,null],[269,null],[277,null],[314,null],[315,null],
[343,null],[1,"else"],[310,null],[51,null],[1,"elif"],[300,null],[301,null],[285,null],[303,null],[302,null],[335,null],[275,null],[258,null],[1,"or"],[334,null],[267,null],[34,null],[262,null],[33,null],[319,null],[13,null],[295,null],[263,null],[291,null],[311,null],[307,null],[313,null],[282,null],[298,null],[304,null],[278,null],[318,null],[322,null],[5,null],[6,null],[47,null],[17,null],[24,null],[305,null],[306,null],[323,null],[289,null],[1,"finally"],[331,null],[336,null],[338,null],[257,
null],[32,null],[341,null]],keywords:{False:33,"null":9,True:6,and:47,as:108,assert:24,"break":39,"class":13,"continue":40,"debugger":17,def:4,del:27,elif:130,"else":127,except:113,"finally":168,"for":34,from:36,global:26,"if":37,"import":32,"in":83,is:92,lambda:14,nonlocal:18,not:8,or:139,pass:28,print:16,raise:5,"return":23,"try":20,"while":21,"with":42,yield:31},tokens:{0:112,1:25,2:11,3:7,4:2,5:159,6:160,7:35,8:61,9:38,10:64,11:48,12:52,13:146,14:43,15:30,16:15,17:162,18:115,19:45,20:94,21:89,
22:50,23:110,24:163,25:12,26:59,27:90,28:88,29:91,30:93,31:22,32:173,33:144,34:142,35:53,36:74,37:78,38:68,39:79,40:69,41:72,42:73,43:75,44:77,45:67,46:71,47:161,48:76,49:41,50:70,51:129,52:19,54:29,55:10},start:256}},function(m,p){function a(a,b){this.filename=a;this.grammar=b;this.p_flags=0;return this}function b(b,d){void 0===d&&(d="file_input");b=new a(b,Sk.ParseTables);"file_input"===d?b.setup(Sk.ParseTables.sym.file_input):Sk.asserts.fail("todo;");return b}a.FUTURE_PRINT_FUNCTION="print_function";
a.FUTURE_UNICODE_LITERALS="unicode_literals";a.FUTURE_DIVISION="division";a.FUTURE_ABSOLUTE_IMPORT="absolute_import";a.FUTURE_WITH_STATEMENT="with_statement";a.FUTURE_NESTED_SCOPES="nested_scopes";a.FUTURE_GENERATORS="generators";a.CO_FUTURE_PRINT_FUNCTION=65536;a.CO_FUTURE_UNICODE_LITERALS=131072;a.CO_FUTURE_DIVISON=8192;a.CO_FUTURE_ABSOLUTE_IMPORT=16384;a.CO_FUTURE_WITH_STATEMENT=32768;a.prototype.setup=function(a){a=a||this.grammar.start;this.stack=[{dfa:this.grammar.dfas[a],state:0,node:{type:a,
value:null,context:null,children:[]}}];this.used_names={}};a.prototype.addtoken=function(a,b,e){var c,d=this.classify(a,b,e);a:for(;;){var f=this.stack[this.stack.length-1];var k=f.dfa[0];var n=k[f.state];for(c=0;c<n.length;++c){var l=n[c][0];var q=n[c][1];var m=this.grammar.labels[l][0];if(d===l){Sk.asserts.assert(256>m);this.shift(a,b,q,e);for(e=q;1===k[e].length&&0===k[e][0][0]&&k[e][0][1]===e;){this.pop();if(0===this.stack.length)return!0;f=this.stack[this.stack.length-1];e=f.state;k=f.dfa[0]}return!1}if(256<=
m&&(l=this.grammar.dfas[m],l=l[1],l.hasOwnProperty(d))){this.push(m,this.grammar.dfas[m],q,e);continue a}}b:{k=[0,f.state];for(f=n.length;f--;)if(n[f][0]===k[0]&&n[f][1]===k[1]){n=!0;break b}n=!1}if(n){if(this.pop(),0===this.stack.length)throw new Sk.builtin.SyntaxError("too much input",this.filename);}else throw a=e[0][0],new Sk.builtin.SyntaxError("bad input",this.filename,a,e);}};a.prototype.classify=function(b,d,e){if(b===Sk.token.tokens.T_NAME){this.used_names[d]=!0;var c=this.grammar.keywords.hasOwnProperty(d)&&
this.grammar.keywords[d];"print"===d&&(this.p_flags&a.CO_FUTURE_PRINT_FUNCTION||!0===Sk.__future__.print_function)&&(c=!1);if(c)return c}c=this.grammar.tokens.hasOwnProperty(b)&&this.grammar.tokens[b];if(!c){d="#"+b;for(let a in Sk.token.tokens)if(Sk.token.tokens[a]==b){d=a;break}throw new Sk.builtin.SyntaxError("bad token "+d,this.filename,e[0][0],e);}return c};a.prototype.shift=function(a,b,e,h){var c=this.stack[this.stack.length-1].dfa,d=this.stack[this.stack.length-1].node;d.children.push({type:a,
value:b,lineno:h[0][0],col_offset:h[0][1],children:null});this.stack[this.stack.length-1]={dfa:c,state:e,node:d}};a.prototype.push=function(a,b,e,h){a={type:a,value:null,lineno:h[0][0],col_offset:h[0][1],children:[]};this.stack[this.stack.length-1]={dfa:this.stack[this.stack.length-1].dfa,state:e,node:this.stack[this.stack.length-1].node};this.stack.push({dfa:b,state:0,node:a})};a.prototype.pop=function(){var a=this.stack.pop().node;if(a)if(0!==this.stack.length){var b=this.stack[this.stack.length-
1].node;b.children.push(a)}else this.rootnode=a,this.rootnode.used_names=this.used_names};Sk.parse=function(a,d){var c=Sk.token.tokens.T_COMMENT,h=Sk.token.tokens.T_NL,g=Sk.token.tokens.T_OP,f=Sk.token.tokens.T_ENDMARKER,k=Sk.token.tokens.T_ENCODING,n=!1,l=b(a);Sk._tokenize(a,function(a){var b=a.split("\n").reverse().map(function(a){return a+"\n"});return function(){if(0===b.length)throw new Sk.builtin.Exception("EOF");return b.pop()}}(d),"utf-8",function(a){var b=null;a.type!==c&&a.type!==h&&a.type!==
k&&(a.type===g&&(b=Sk.OpMap[a.string]),l.addtoken(b||a.type,a.string,[a.start,a.end,a.line]),a.type===f&&(n=!0))});if(!n)throw new Sk.builtin.SyntaxError("incomplete input",this.filename);return{cst:l.rootnode,flags:l.p_flags}};Sk.parseTreeDump=function(a,b){var c;b=b||"";var d=""+b;if(256<=a.type)for(d+=Sk.ParseTables.number2symbol[a.type]+"\n",c=0;c<a.children.length;++c)d+=Sk.parseTreeDump(a.children[c],b+"  ");else d+=Sk.token.tok_name[a.type]+": "+(new Sk.builtin.str(a.value)).$r().v+"\n";return d};
Sk.exportSymbol("Sk.Parser",a);Sk.exportSymbol("Sk.parse",Sk.parse);Sk.exportSymbol("Sk.parseTreeDump",Sk.parseTreeDump)},function(m,p){Sk.astnodes={};Sk.astnodes.Load=function(){};Sk.astnodes.Store=function(){};Sk.astnodes.Del=function(){};Sk.astnodes.AugLoad=function(){};Sk.astnodes.AugStore=function(){};Sk.astnodes.Param=function(){};Sk.astnodes.And=function(){};Sk.astnodes.Or=function(){};Sk.astnodes.Add=function(){};Sk.astnodes.Sub=function(){};Sk.astnodes.Mult=function(){};Sk.astnodes.MatMult=
function(){};Sk.astnodes.Div=function(){};Sk.astnodes.Mod=function(){};Sk.astnodes.Pow=function(){};Sk.astnodes.LShift=function(){};Sk.astnodes.RShift=function(){};Sk.astnodes.BitOr=function(){};Sk.astnodes.BitXor=function(){};Sk.astnodes.BitAnd=function(){};Sk.astnodes.FloorDiv=function(){};Sk.astnodes.Invert=function(){};Sk.astnodes.Not=function(){};Sk.astnodes.UAdd=function(){};Sk.astnodes.USub=function(){};Sk.astnodes.Eq=function(){};Sk.astnodes.NotEq=function(){};Sk.astnodes.Lt=function(){};
Sk.astnodes.LtE=function(){};Sk.astnodes.Gt=function(){};Sk.astnodes.GtE=function(){};Sk.astnodes.Is=function(){};Sk.astnodes.IsNot=function(){};Sk.astnodes.In=function(){};Sk.astnodes.NotIn=function(){};Sk.astnodes.Module=function(a,b){this.body=a;this.docstring=b;return this};Sk.astnodes.Interactive=function(a){this.body=a;return this};Sk.astnodes.Expression=function(a){this.body=a;return this};Sk.astnodes.Suite=function(a){this.body=a;return this};Sk.astnodes.FunctionDef=function(a,b,c,d,e,h,g,
f){Sk.asserts.assert(null!==g&&void 0!==g);Sk.asserts.assert(null!==f&&void 0!==f);this.name=a;this.args=b;this.body=c;this.decorator_list=d;this.returns=e;this.docstring=h;this.lineno=g;this.col_offset=f;return this};Sk.astnodes.AsyncFunctionDef=function(a,b,c,d,e,h,g,f){Sk.asserts.assert(null!==g&&void 0!==g);Sk.asserts.assert(null!==f&&void 0!==f);this.name=a;this.args=b;this.body=c;this.decorator_list=d;this.returns=e;this.docstring=h;this.lineno=g;this.col_offset=f;return this};Sk.astnodes.ClassDef=
function(a,b,c,d,e,h,g,f){Sk.asserts.assert(null!==g&&void 0!==g);Sk.asserts.assert(null!==f&&void 0!==f);this.name=a;this.bases=b;this.keywords=c;this.body=d;this.decorator_list=e;this.docstring=h;this.lineno=g;this.col_offset=f;return this};Sk.astnodes.Return=function(a,b,c){Sk.asserts.assert(null!==b&&void 0!==b);Sk.asserts.assert(null!==c&&void 0!==c);this.value=a;this.lineno=b;this.col_offset=c;return this};Sk.astnodes.Delete=function(a,b,c){Sk.asserts.assert(null!==b&&void 0!==b);Sk.asserts.assert(null!==
c&&void 0!==c);this.targets=a;this.lineno=b;this.col_offset=c;return this};Sk.astnodes.Assign=function(a,b,c,d){Sk.asserts.assert(null!==c&&void 0!==c);Sk.asserts.assert(null!==d&&void 0!==d);this.targets=a;this.value=b;this.lineno=c;this.col_offset=d;return this};Sk.astnodes.AugAssign=function(a,b,c,d,e){Sk.asserts.assert(null!==d&&void 0!==d);Sk.asserts.assert(null!==e&&void 0!==e);this.target=a;this.op=b;this.value=c;this.lineno=d;this.col_offset=e;return this};Sk.astnodes.AnnAssign=function(a,
b,c,d,e,h){Sk.asserts.assert(null!==e&&void 0!==e);Sk.asserts.assert(null!==h&&void 0!==h);this.target=a;this.annotation=b;this.value=c;this.simple=d;this.lineno=e;this.col_offset=h;return this};Sk.astnodes.For=function(a,b,c,d,e,h){Sk.asserts.assert(null!==e&&void 0!==e);Sk.asserts.assert(null!==h&&void 0!==h);this.target=a;this.iter=b;this.body=c;this.orelse=d;this.lineno=e;this.col_offset=h;return this};Sk.astnodes.AsyncFor=function(a,b,c,d,e,h){Sk.asserts.assert(null!==e&&void 0!==e);Sk.asserts.assert(null!==
h&&void 0!==h);this.target=a;this.iter=b;this.body=c;this.orelse=d;this.lineno=e;this.col_offset=h;return this};Sk.astnodes.While=function(a,b,c,d,e){Sk.asserts.assert(null!==d&&void 0!==d);Sk.asserts.assert(null!==e&&void 0!==e);this.test=a;this.body=b;this.orelse=c;this.lineno=d;this.col_offset=e;return this};Sk.astnodes.If=function(a,b,c,d,e){Sk.asserts.assert(null!==d&&void 0!==d);Sk.asserts.assert(null!==e&&void 0!==e);this.test=a;this.body=b;this.orelse=c;this.lineno=d;this.col_offset=e;return this};
Sk.astnodes.With=function(a,b,c,d){Sk.asserts.assert(null!==c&&void 0!==c);Sk.asserts.assert(null!==d&&void 0!==d);this.items=a;this.body=b;this.lineno=c;this.col_offset=d;return this};Sk.astnodes.AsyncWith=function(a,b,c,d){Sk.asserts.assert(null!==c&&void 0!==c);Sk.asserts.assert(null!==d&&void 0!==d);this.items=a;this.body=b;this.lineno=c;this.col_offset=d;return this};Sk.astnodes.Raise=function(a,b,c,d,e,h){Sk.asserts.assert(null!==e&&void 0!==e);Sk.asserts.assert(null!==h&&void 0!==h);this.exc=
a;this.cause=b;this.inst=c;this.tback=d;this.lineno=e;this.col_offset=h;return this};Sk.astnodes.Try=function(a,b,c,d,e,h){Sk.asserts.assert(null!==e&&void 0!==e);Sk.asserts.assert(null!==h&&void 0!==h);this.body=a;this.handlers=b;this.orelse=c;this.finalbody=d;this.lineno=e;this.col_offset=h;return this};Sk.astnodes.Assert=function(a,b,c,d){Sk.asserts.assert(null!==c&&void 0!==c);Sk.asserts.assert(null!==d&&void 0!==d);this.test=a;this.msg=b;this.lineno=c;this.col_offset=d;return this};Sk.astnodes.Import=
function(a,b,c){Sk.asserts.assert(null!==b&&void 0!==b);Sk.asserts.assert(null!==c&&void 0!==c);this.names=a;this.lineno=b;this.col_offset=c;return this};Sk.astnodes.ImportFrom=function(a,b,c,d,e){Sk.asserts.assert(null!==d&&void 0!==d);Sk.asserts.assert(null!==e&&void 0!==e);this.module=a;this.names=b;this.level=c;this.lineno=d;this.col_offset=e;return this};Sk.astnodes.Global=function(a,b,c){Sk.asserts.assert(null!==b&&void 0!==b);Sk.asserts.assert(null!==c&&void 0!==c);this.names=a;this.lineno=
b;this.col_offset=c;return this};Sk.astnodes.Nonlocal=function(a,b,c){Sk.asserts.assert(null!==b&&void 0!==b);Sk.asserts.assert(null!==c&&void 0!==c);this.names=a;this.lineno=b;this.col_offset=c;return this};Sk.astnodes.Expr=function(a,b,c){Sk.asserts.assert(null!==b&&void 0!==b);Sk.asserts.assert(null!==c&&void 0!==c);this.value=a;this.lineno=b;this.col_offset=c;return this};Sk.astnodes.Pass=function(a,b){Sk.asserts.assert(null!==a&&void 0!==a);Sk.asserts.assert(null!==b&&void 0!==b);this.lineno=
a;this.col_offset=b;return this};Sk.astnodes.Break=function(a,b){Sk.asserts.assert(null!==a&&void 0!==a);Sk.asserts.assert(null!==b&&void 0!==b);this.lineno=a;this.col_offset=b;return this};Sk.astnodes.Continue=function(a,b){Sk.asserts.assert(null!==a&&void 0!==a);Sk.asserts.assert(null!==b&&void 0!==b);this.lineno=a;this.col_offset=b;return this};Sk.astnodes.Print=function(a,b,c,d,e){Sk.asserts.assert(null!==d&&void 0!==d);Sk.asserts.assert(null!==e&&void 0!==e);this.dest=a;this.values=b;this.nl=
c;this.lineno=d;this.col_offset=e;return this};Sk.astnodes.Debugger=function(a,b){Sk.asserts.assert(null!==a&&void 0!==a);Sk.asserts.assert(null!==b&&void 0!==b);this.lineno=a;this.col_offset=b;return this};Sk.astnodes.BoolOp=function(a,b,c,d){Sk.asserts.assert(null!==c&&void 0!==c);Sk.asserts.assert(null!==d&&void 0!==d);this.op=a;this.values=b;this.lineno=c;this.col_offset=d;return this};Sk.astnodes.BinOp=function(a,b,c,d,e){Sk.asserts.assert(null!==d&&void 0!==d);Sk.asserts.assert(null!==e&&void 0!==
e);this.left=a;this.op=b;this.right=c;this.lineno=d;this.col_offset=e;return this};Sk.astnodes.UnaryOp=function(a,b,c,d){Sk.asserts.assert(null!==c&&void 0!==c);Sk.asserts.assert(null!==d&&void 0!==d);this.op=a;this.operand=b;this.lineno=c;this.col_offset=d;return this};Sk.astnodes.Lambda=function(a,b,c,d){Sk.asserts.assert(null!==c&&void 0!==c);Sk.asserts.assert(null!==d&&void 0!==d);this.args=a;this.body=b;this.lineno=c;this.col_offset=d;return this};Sk.astnodes.IfExp=function(a,b,c,d,e){Sk.asserts.assert(null!==
d&&void 0!==d);Sk.asserts.assert(null!==e&&void 0!==e);this.test=a;this.body=b;this.orelse=c;this.lineno=d;this.col_offset=e;return this};Sk.astnodes.Dict=function(a,b,c,d){Sk.asserts.assert(null!==c&&void 0!==c);Sk.asserts.assert(null!==d&&void 0!==d);this.keys=a;this.values=b;this.lineno=c;this.col_offset=d;return this};Sk.astnodes.Set=function(a,b,c){Sk.asserts.assert(null!==b&&void 0!==b);Sk.asserts.assert(null!==c&&void 0!==c);this.elts=a;this.lineno=b;this.col_offset=c;return this};Sk.astnodes.ListComp=
function(a,b,c,d){Sk.asserts.assert(null!==c&&void 0!==c);Sk.asserts.assert(null!==d&&void 0!==d);this.elt=a;this.generators=b;this.lineno=c;this.col_offset=d;return this};Sk.astnodes.SetComp=function(a,b,c,d){Sk.asserts.assert(null!==c&&void 0!==c);Sk.asserts.assert(null!==d&&void 0!==d);this.elt=a;this.generators=b;this.lineno=c;this.col_offset=d;return this};Sk.astnodes.DictComp=function(a,b,c,d,e){Sk.asserts.assert(null!==d&&void 0!==d);Sk.asserts.assert(null!==e&&void 0!==e);this.key=a;this.value=
b;this.generators=c;this.lineno=d;this.col_offset=e;return this};Sk.astnodes.GeneratorExp=function(a,b,c,d){Sk.asserts.assert(null!==c&&void 0!==c);Sk.asserts.assert(null!==d&&void 0!==d);this.elt=a;this.generators=b;this.lineno=c;this.col_offset=d;return this};Sk.astnodes.Await=function(a,b,c){Sk.asserts.assert(null!==b&&void 0!==b);Sk.asserts.assert(null!==c&&void 0!==c);this.value=a;this.lineno=b;this.col_offset=c;return this};Sk.astnodes.Yield=function(a,b,c){Sk.asserts.assert(null!==b&&void 0!==
b);Sk.asserts.assert(null!==c&&void 0!==c);this.value=a;this.lineno=b;this.col_offset=c;return this};Sk.astnodes.YieldFrom=function(a,b,c){Sk.asserts.assert(null!==b&&void 0!==b);Sk.asserts.assert(null!==c&&void 0!==c);this.value=a;this.lineno=b;this.col_offset=c;return this};Sk.astnodes.Compare=function(a,b,c,d,e){Sk.asserts.assert(null!==d&&void 0!==d);Sk.asserts.assert(null!==e&&void 0!==e);this.left=a;this.ops=b;this.comparators=c;this.lineno=d;this.col_offset=e;return this};Sk.astnodes.Call=
function(a,b,c,d,e){Sk.asserts.assert(null!==d&&void 0!==d);Sk.asserts.assert(null!==e&&void 0!==e);this.func=a;this.args=b;this.keywords=c;this.lineno=d;this.col_offset=e;return this};Sk.astnodes.Num=function(a,b,c){Sk.asserts.assert(null!==b&&void 0!==b);Sk.asserts.assert(null!==c&&void 0!==c);this.n=a;this.lineno=b;this.col_offset=c;return this};Sk.astnodes.Str=function(a,b,c){Sk.asserts.assert(null!==b&&void 0!==b);Sk.asserts.assert(null!==c&&void 0!==c);this.s=a;this.lineno=b;this.col_offset=
c;return this};Sk.astnodes.FormattedValue=function(a,b,c,d,e){Sk.asserts.assert(null!==d&&void 0!==d);Sk.asserts.assert(null!==e&&void 0!==e);this.value=a;this.conversion=b;this.format_spec=c;this.lineno=d;this.col_offset=e;return this};Sk.astnodes.JoinedStr=function(a,b,c){Sk.asserts.assert(null!==b&&void 0!==b);Sk.asserts.assert(null!==c&&void 0!==c);this.values=a;this.lineno=b;this.col_offset=c;return this};Sk.astnodes.Bytes=function(a,b,c){Sk.asserts.assert(null!==b&&void 0!==b);Sk.asserts.assert(null!==
c&&void 0!==c);this.s=a;this.lineno=b;this.col_offset=c;return this};Sk.astnodes.NameConstant=function(a,b,c){Sk.asserts.assert(null!==b&&void 0!==b);Sk.asserts.assert(null!==c&&void 0!==c);this.value=a;this.lineno=b;this.col_offset=c;return this};Sk.astnodes.Ellipsis=function(a,b){Sk.asserts.assert(null!==a&&void 0!==a);Sk.asserts.assert(null!==b&&void 0!==b);this.lineno=a;this.col_offset=b;return this};Sk.astnodes.Constant=function(a,b,c){Sk.asserts.assert(null!==b&&void 0!==b);Sk.asserts.assert(null!==
c&&void 0!==c);this.value=a;this.lineno=b;this.col_offset=c;return this};Sk.astnodes.Attribute=function(a,b,c,d,e){Sk.asserts.assert(null!==d&&void 0!==d);Sk.asserts.assert(null!==e&&void 0!==e);this.value=a;this.attr=b;this.ctx=c;this.lineno=d;this.col_offset=e;return this};Sk.astnodes.Subscript=function(a,b,c,d,e){Sk.asserts.assert(null!==d&&void 0!==d);Sk.asserts.assert(null!==e&&void 0!==e);this.value=a;this.slice=b;this.ctx=c;this.lineno=d;this.col_offset=e;return this};Sk.astnodes.Starred=function(a,
b,c,d){Sk.asserts.assert(null!==c&&void 0!==c);Sk.asserts.assert(null!==d&&void 0!==d);this.value=a;this.ctx=b;this.lineno=c;this.col_offset=d;return this};Sk.astnodes.Name=function(a,b,c,d){Sk.asserts.assert(null!==c&&void 0!==c);Sk.asserts.assert(null!==d&&void 0!==d);this.id=a;this.ctx=b;this.lineno=c;this.col_offset=d;return this};Sk.astnodes.List=function(a,b,c,d){Sk.asserts.assert(null!==c&&void 0!==c);Sk.asserts.assert(null!==d&&void 0!==d);this.elts=a;this.ctx=b;this.lineno=c;this.col_offset=
d;return this};Sk.astnodes.Tuple=function(a,b,c,d){Sk.asserts.assert(null!==c&&void 0!==c);Sk.asserts.assert(null!==d&&void 0!==d);this.elts=a;this.ctx=b;this.lineno=c;this.col_offset=d;return this};Sk.astnodes.Slice=function(a,b,c){this.lower=a;this.upper=b;this.step=c;return this};Sk.astnodes.ExtSlice=function(a){this.dims=a;return this};Sk.astnodes.Index=function(a){this.value=a;return this};Sk.astnodes.comprehension=function(a,b,c,d){this.target=a;this.iter=b;this.ifs=c;this.is_async=d;return this};
Sk.astnodes.ExceptHandler=function(a,b,c,d,e){Sk.asserts.assert(null!==d&&void 0!==d);Sk.asserts.assert(null!==e&&void 0!==e);this.type=a;this.name=b;this.body=c;this.lineno=d;this.col_offset=e;return this};Sk.astnodes.arguments_=function(a,b,c,d,e,h){this.args=a;this.vararg=b;this.kwonlyargs=c;this.kw_defaults=d;this.kwarg=e;this.defaults=h;return this};Sk.astnodes.arg=function a(a,b){this.arg=a;this.annotation=b;return this};Sk.astnodes.keyword=function(a,b){this.arg=a;this.value=b;return this};
Sk.astnodes.alias=function(a,b){this.name=a;this.asname=b;return this};Sk.astnodes.withitem=function(a,b){this.context_expr=a;this.optional_vars=b;return this};Sk.astnodes.Module.prototype._astname="Module";Sk.astnodes.Module.prototype._fields=["body",function(a){return a.body},"docstring",function(a){return a.docstring}];Sk.astnodes.Interactive.prototype._astname="Interactive";Sk.astnodes.Interactive.prototype._fields=["body",function(a){return a.body}];Sk.astnodes.Expression.prototype._astname=
"Expression";Sk.astnodes.Expression.prototype._fields=["body",function(a){return a.body}];Sk.astnodes.Suite.prototype._astname="Suite";Sk.astnodes.Suite.prototype._fields=["body",function(a){return a.body}];Sk.astnodes.FunctionDef.prototype._astname="FunctionDef";Sk.astnodes.FunctionDef.prototype._fields=["name",function(a){return a.name},"args",function(a){return a.args},"body",function(a){return a.body},"decorator_list",function(a){return a.decorator_list},"returns",function(a){return a.returns},
"docstring",function(a){return a.docstring}];Sk.astnodes.AsyncFunctionDef.prototype._astname="AsyncFunctionDef";Sk.astnodes.AsyncFunctionDef.prototype._fields=["name",function(a){return a.name},"args",function(a){return a.args},"body",function(a){return a.body},"decorator_list",function(a){return a.decorator_list},"returns",function(a){return a.returns},"docstring",function(a){return a.docstring}];Sk.astnodes.ClassDef.prototype._astname="ClassDef";Sk.astnodes.ClassDef.prototype._fields=["name",function(a){return a.name},
"bases",function(a){return a.bases},"keywords",function(a){return a.keywords},"body",function(a){return a.body},"decorator_list",function(a){return a.decorator_list},"docstring",function(a){return a.docstring}];Sk.astnodes.Return.prototype._astname="Return";Sk.astnodes.Return.prototype._fields=["value",function(a){return a.value}];Sk.astnodes.Delete.prototype._astname="Delete";Sk.astnodes.Delete.prototype._fields=["targets",function(a){return a.targets}];Sk.astnodes.Assign.prototype._astname="Assign";
Sk.astnodes.Assign.prototype._fields=["targets",function(a){return a.targets},"value",function(a){return a.value}];Sk.astnodes.AugAssign.prototype._astname="AugAssign";Sk.astnodes.AugAssign.prototype._fields=["target",function(a){return a.target},"op",function(a){return a.op},"value",function(a){return a.value}];Sk.astnodes.AnnAssign.prototype._astname="AnnAssign";Sk.astnodes.AnnAssign.prototype._fields=["target",function(a){return a.target},"annotation",function(a){return a.annotation},"value",function(a){return a.value},
"simple",function(a){return a.simple}];Sk.astnodes.For.prototype._astname="For";Sk.astnodes.For.prototype._fields=["target",function(a){return a.target},"iter",function(a){return a.iter},"body",function(a){return a.body},"orelse",function(a){return a.orelse}];Sk.astnodes.AsyncFor.prototype._astname="AsyncFor";Sk.astnodes.AsyncFor.prototype._fields=["target",function(a){return a.target},"iter",function(a){return a.iter},"body",function(a){return a.body},"orelse",function(a){return a.orelse}];Sk.astnodes.While.prototype._astname=
"While";Sk.astnodes.While.prototype._fields=["test",function(a){return a.test},"body",function(a){return a.body},"orelse",function(a){return a.orelse}];Sk.astnodes.If.prototype._astname="If";Sk.astnodes.If.prototype._fields=["test",function(a){return a.test},"body",function(a){return a.body},"orelse",function(a){return a.orelse}];Sk.astnodes.With.prototype._astname="With";Sk.astnodes.With.prototype._fields=["items",function(a){return a.items},"body",function(a){return a.body}];Sk.astnodes.AsyncWith.prototype._astname=
"AsyncWith";Sk.astnodes.AsyncWith.prototype._fields=["items",function(a){return a.items},"body",function(a){return a.body}];Sk.astnodes.Raise.prototype._astname="Raise";Sk.astnodes.Raise.prototype._fields=["exc",function(a){return a.exc},"cause",function(a){return a.cause},"inst",function(a){return a.inst},"tback",function(a){return a.tback}];Sk.astnodes.Try.prototype._astname="Try";Sk.astnodes.Try.prototype._fields=["body",function(a){return a.body},"handlers",function(a){return a.handlers},"orelse",
function(a){return a.orelse},"finalbody",function(a){return a.finalbody}];Sk.astnodes.Assert.prototype._astname="Assert";Sk.astnodes.Assert.prototype._fields=["test",function(a){return a.test},"msg",function(a){return a.msg}];Sk.astnodes.Import.prototype._astname="Import";Sk.astnodes.Import.prototype._fields=["names",function(a){return a.names}];Sk.astnodes.ImportFrom.prototype._astname="ImportFrom";Sk.astnodes.ImportFrom.prototype._fields=["module",function(a){return a.module},"names",function(a){return a.names},
"level",function(a){return a.level}];Sk.astnodes.Global.prototype._astname="Global";Sk.astnodes.Global.prototype._fields=["names",function(a){return a.names}];Sk.astnodes.Nonlocal.prototype._astname="Nonlocal";Sk.astnodes.Nonlocal.prototype._fields=["names",function(a){return a.names}];Sk.astnodes.Expr.prototype._astname="Expr";Sk.astnodes.Expr.prototype._fields=["value",function(a){return a.value}];Sk.astnodes.Pass.prototype._astname="Pass";Sk.astnodes.Pass.prototype._fields=[];Sk.astnodes.Break.prototype._astname=
"Break";Sk.astnodes.Break.prototype._fields=[];Sk.astnodes.Continue.prototype._astname="Continue";Sk.astnodes.Continue.prototype._fields=[];Sk.astnodes.Print.prototype._astname="Print";Sk.astnodes.Print.prototype._fields=["dest",function(a){return a.dest},"values",function(a){return a.values},"nl",function(a){return a.nl}];Sk.astnodes.Debugger.prototype._astname="Debugger";Sk.astnodes.Debugger.prototype._fields=[];Sk.astnodes.BoolOp.prototype._astname="BoolOp";Sk.astnodes.BoolOp.prototype._fields=
["op",function(a){return a.op},"values",function(a){return a.values}];Sk.astnodes.BinOp.prototype._astname="BinOp";Sk.astnodes.BinOp.prototype._fields=["left",function(a){return a.left},"op",function(a){return a.op},"right",function(a){return a.right}];Sk.astnodes.UnaryOp.prototype._astname="UnaryOp";Sk.astnodes.UnaryOp.prototype._fields=["op",function(a){return a.op},"operand",function(a){return a.operand}];Sk.astnodes.Lambda.prototype._astname="Lambda";Sk.astnodes.Lambda.prototype._fields=["args",
function(a){return a.args},"body",function(a){return a.body}];Sk.astnodes.IfExp.prototype._astname="IfExp";Sk.astnodes.IfExp.prototype._fields=["test",function(a){return a.test},"body",function(a){return a.body},"orelse",function(a){return a.orelse}];Sk.astnodes.Dict.prototype._astname="Dict";Sk.astnodes.Dict.prototype._fields=["keys",function(a){return a.keys},"values",function(a){return a.values}];Sk.astnodes.Set.prototype._astname="Set";Sk.astnodes.Set.prototype._fields=["elts",function(a){return a.elts}];
Sk.astnodes.ListComp.prototype._astname="ListComp";Sk.astnodes.ListComp.prototype._fields=["elt",function(a){return a.elt},"generators",function(a){return a.generators}];Sk.astnodes.SetComp.prototype._astname="SetComp";Sk.astnodes.SetComp.prototype._fields=["elt",function(a){return a.elt},"generators",function(a){return a.generators}];Sk.astnodes.DictComp.prototype._astname="DictComp";Sk.astnodes.DictComp.prototype._fields=["key",function(a){return a.key},"value",function(a){return a.value},"generators",
function(a){return a.generators}];Sk.astnodes.GeneratorExp.prototype._astname="GeneratorExp";Sk.astnodes.GeneratorExp.prototype._fields=["elt",function(a){return a.elt},"generators",function(a){return a.generators}];Sk.astnodes.Await.prototype._astname="Await";Sk.astnodes.Await.prototype._fields=["value",function(a){return a.value}];Sk.astnodes.Yield.prototype._astname="Yield";Sk.astnodes.Yield.prototype._fields=["value",function(a){return a.value}];Sk.astnodes.YieldFrom.prototype._astname="YieldFrom";
Sk.astnodes.YieldFrom.prototype._fields=["value",function(a){return a.value}];Sk.astnodes.Compare.prototype._astname="Compare";Sk.astnodes.Compare.prototype._fields=["left",function(a){return a.left},"ops",function(a){return a.ops},"comparators",function(a){return a.comparators}];Sk.astnodes.Call.prototype._astname="Call";Sk.astnodes.Call.prototype._fields=["func",function(a){return a.func},"args",function(a){return a.args},"keywords",function(a){return a.keywords}];Sk.astnodes.Num.prototype._astname=
"Num";Sk.astnodes.Num.prototype._fields=["n",function(a){return a.n}];Sk.astnodes.Str.prototype._astname="Str";Sk.astnodes.Str.prototype._fields=["s",function(a){return a.s}];Sk.astnodes.FormattedValue.prototype._astname="FormattedValue";Sk.astnodes.FormattedValue.prototype._fields=["value",function(a){return a.value},"conversion",function(a){return a.conversion},"format_spec",function(a){return a.format_spec}];Sk.astnodes.JoinedStr.prototype._astname="JoinedStr";Sk.astnodes.JoinedStr.prototype._fields=
["values",function(a){return a.values}];Sk.astnodes.Bytes.prototype._astname="Bytes";Sk.astnodes.Bytes.prototype._fields=["s",function(a){return a.s}];Sk.astnodes.NameConstant.prototype._astname="NameConstant";Sk.astnodes.NameConstant.prototype._fields=["value",function(a){return a.value}];Sk.astnodes.Ellipsis.prototype._astname="Ellipsis";Sk.astnodes.Ellipsis.prototype._fields=[];Sk.astnodes.Constant.prototype._astname="Constant";Sk.astnodes.Constant.prototype._fields=["value",function(a){return a.value}];
Sk.astnodes.Attribute.prototype._astname="Attribute";Sk.astnodes.Attribute.prototype._fields=["value",function(a){return a.value},"attr",function(a){return a.attr},"ctx",function(a){return a.ctx}];Sk.astnodes.Subscript.prototype._astname="Subscript";Sk.astnodes.Subscript.prototype._fields=["value",function(a){return a.value},"slice",function(a){return a.slice},"ctx",function(a){return a.ctx}];Sk.astnodes.Starred.prototype._astname="Starred";Sk.astnodes.Starred.prototype._fields=["value",function(a){return a.value},
"ctx",function(a){return a.ctx}];Sk.astnodes.Name.prototype._astname="Name";Sk.astnodes.Name.prototype._fields=["id",function(a){return a.id},"ctx",function(a){return a.ctx}];Sk.astnodes.List.prototype._astname="List";Sk.astnodes.List.prototype._fields=["elts",function(a){return a.elts},"ctx",function(a){return a.ctx}];Sk.astnodes.Tuple.prototype._astname="Tuple";Sk.astnodes.Tuple.prototype._fields=["elts",function(a){return a.elts},"ctx",function(a){return a.ctx}];Sk.astnodes.Load.prototype._astname=
"Load";Sk.astnodes.Load.prototype._isenum=!0;Sk.astnodes.Store.prototype._astname="Store";Sk.astnodes.Store.prototype._isenum=!0;Sk.astnodes.Del.prototype._astname="Del";Sk.astnodes.Del.prototype._isenum=!0;Sk.astnodes.AugLoad.prototype._astname="AugLoad";Sk.astnodes.AugLoad.prototype._isenum=!0;Sk.astnodes.AugStore.prototype._astname="AugStore";Sk.astnodes.AugStore.prototype._isenum=!0;Sk.astnodes.Param.prototype._astname="Param";Sk.astnodes.Param.prototype._isenum=!0;Sk.astnodes.Slice.prototype._astname=
"Slice";Sk.astnodes.Slice.prototype._fields=["lower",function(a){return a.lower},"upper",function(a){return a.upper},"step",function(a){return a.step}];Sk.astnodes.ExtSlice.prototype._astname="ExtSlice";Sk.astnodes.ExtSlice.prototype._fields=["dims",function(a){return a.dims}];Sk.astnodes.Index.prototype._astname="Index";Sk.astnodes.Index.prototype._fields=["value",function(a){return a.value}];Sk.astnodes.And.prototype._astname="And";Sk.astnodes.And.prototype._isenum=!0;Sk.astnodes.Or.prototype._astname=
"Or";Sk.astnodes.Or.prototype._isenum=!0;Sk.astnodes.Add.prototype._astname="Add";Sk.astnodes.Add.prototype._isenum=!0;Sk.astnodes.Sub.prototype._astname="Sub";Sk.astnodes.Sub.prototype._isenum=!0;Sk.astnodes.Mult.prototype._astname="Mult";Sk.astnodes.Mult.prototype._isenum=!0;Sk.astnodes.MatMult.prototype._astname="MatMult";Sk.astnodes.MatMult.prototype._isenum=!0;Sk.astnodes.Div.prototype._astname="Div";Sk.astnodes.Div.prototype._isenum=!0;Sk.astnodes.Mod.prototype._astname="Mod";Sk.astnodes.Mod.prototype._isenum=
!0;Sk.astnodes.Pow.prototype._astname="Pow";Sk.astnodes.Pow.prototype._isenum=!0;Sk.astnodes.LShift.prototype._astname="LShift";Sk.astnodes.LShift.prototype._isenum=!0;Sk.astnodes.RShift.prototype._astname="RShift";Sk.astnodes.RShift.prototype._isenum=!0;Sk.astnodes.BitOr.prototype._astname="BitOr";Sk.astnodes.BitOr.prototype._isenum=!0;Sk.astnodes.BitXor.prototype._astname="BitXor";Sk.astnodes.BitXor.prototype._isenum=!0;Sk.astnodes.BitAnd.prototype._astname="BitAnd";Sk.astnodes.BitAnd.prototype._isenum=
!0;Sk.astnodes.FloorDiv.prototype._astname="FloorDiv";Sk.astnodes.FloorDiv.prototype._isenum=!0;Sk.astnodes.Invert.prototype._astname="Invert";Sk.astnodes.Invert.prototype._isenum=!0;Sk.astnodes.Not.prototype._astname="Not";Sk.astnodes.Not.prototype._isenum=!0;Sk.astnodes.UAdd.prototype._astname="UAdd";Sk.astnodes.UAdd.prototype._isenum=!0;Sk.astnodes.USub.prototype._astname="USub";Sk.astnodes.USub.prototype._isenum=!0;Sk.astnodes.Eq.prototype._astname="Eq";Sk.astnodes.Eq.prototype._isenum=!0;Sk.astnodes.NotEq.prototype._astname=
"NotEq";Sk.astnodes.NotEq.prototype._isenum=!0;Sk.astnodes.Lt.prototype._astname="Lt";Sk.astnodes.Lt.prototype._isenum=!0;Sk.astnodes.LtE.prototype._astname="LtE";Sk.astnodes.LtE.prototype._isenum=!0;Sk.astnodes.Gt.prototype._astname="Gt";Sk.astnodes.Gt.prototype._isenum=!0;Sk.astnodes.GtE.prototype._astname="GtE";Sk.astnodes.GtE.prototype._isenum=!0;Sk.astnodes.Is.prototype._astname="Is";Sk.astnodes.Is.prototype._isenum=!0;Sk.astnodes.IsNot.prototype._astname="IsNot";Sk.astnodes.IsNot.prototype._isenum=
!0;Sk.astnodes.In.prototype._astname="In";Sk.astnodes.In.prototype._isenum=!0;Sk.astnodes.NotIn.prototype._astname="NotIn";Sk.astnodes.NotIn.prototype._isenum=!0;Sk.astnodes.comprehension.prototype._astname="comprehension";Sk.astnodes.comprehension.prototype._fields=["target",function(a){return a.target},"iter",function(a){return a.iter},"ifs",function(a){return a.ifs},"is_async",function(a){return a.is_async}];Sk.astnodes.ExceptHandler.prototype._astname="ExceptHandler";Sk.astnodes.ExceptHandler.prototype._fields=
["type",function(a){return a.type},"name",function(a){return a.name},"body",function(a){return a.body}];Sk.astnodes.arguments_.prototype._astname="arguments";Sk.astnodes.arguments_.prototype._fields=["args",function(a){return a.args},"vararg",function(a){return a.vararg},"kwonlyargs",function(a){return a.kwonlyargs},"kw_defaults",function(a){return a.kw_defaults},"kwarg",function(a){return a.kwarg},"defaults",function(a){return a.defaults}];Sk.astnodes.arg.prototype._astname="arg";Sk.astnodes.arg.prototype._fields=
["arg",function(a){return a.arg},"annotation",function(a){return a.annotation}];Sk.astnodes.keyword.prototype._astname="keyword";Sk.astnodes.keyword.prototype._fields=["arg",function(a){return a.arg},"value",function(a){return a.value}];Sk.astnodes.alias.prototype._astname="alias";Sk.astnodes.alias.prototype._fields=["name",function(a){return a.name},"asname",function(a){return a.asname}];Sk.astnodes.withitem.prototype._astname="withitem";Sk.astnodes.withitem.prototype._fields=["context_expr",function(a){return a.context_expr},
"optional_vars",function(a){return a.optional_vars}];Sk.exportSymbol("Sk.astnodes",Sk.astnodes)},function(m,p){function a(a,b,c){this.c_encoding=a;this.c_filename=b;this.c_flags=c||0}function b(a){Sk.asserts.assert(void 0!==a,"node must be defined");return null===a.children?0:a.children.length}function c(a,b){Sk.asserts.assert(void 0!==a,"node must be defined");Sk.asserts.assert(void 0!==b,"index of child must be specified");return a.children[b]}function d(a,b){Sk.asserts.assert(a.type===b,"node wasn't expected type")}
function e(a,b,c){throw new Sk.builtin.SyntaxError(c,a.c_filename,b.lineno);}function h(a){Sk.asserts.assert("string"===typeof a,"expecting string, got "+typeof a);return new Sk.builtin.str(a)}function g(a){var d,f;switch(a.type){case r.single_input:if(c(a,0).type===w.T_NEWLINE)break;else return g(c(a,0));case r.file_input:for(d=f=0;d<b(a);++d){var e=c(a,d);e.type===r.stmt&&(f+=g(e))}return f;case r.stmt:return g(c(a,0));case r.compound_stmt:return 1;case r.simple_stmt:return Math.floor(b(a)/2);case r.suite:if(1===
b(a))return g(c(a,0));f=0;for(d=2;d<b(a)-1;++d)f+=g(c(a,d));return f;default:Sk.asserts.fail("Non-statement found")}return 0}function f(a,b,c,d){c instanceof Sk.builtin.str&&(c=c.v);if("None"===c)throw new Sk.builtin.SyntaxError("assignment to None",a.c_filename,d);if("True"===c||"False"===c)throw new Sk.builtin.SyntaxError("assignment to True or False is forbidden",a.c_filename,d);}function k(a,b,c,d){var e;Sk.asserts.assert(c!==Sk.astnodes.AugStore&&c!==Sk.astnodes.AugLoad,"context not AugStore or AugLoad");
var g=e=null;switch(b.constructor){case Sk.astnodes.Attribute:case Sk.astnodes.Name:c===Sk.astnodes.Store&&f(a,d,b.attr,d.lineno);b.ctx=c;break;case Sk.astnodes.Starred:b.ctx=c;k(a,b.value,c,d);break;case Sk.astnodes.Subscript:b.ctx=c;break;case Sk.astnodes.List:b.ctx=c;e=b.elts;break;case Sk.astnodes.Tuple:if(0===b.elts.length)throw new Sk.builtin.SyntaxError("can't assign to ()",a.c_filename,d.lineno);b.ctx=c;e=b.elts;break;case Sk.astnodes.Lambda:g="lambda";break;case Sk.astnodes.Call:g="function call";
break;case Sk.astnodes.BoolOp:case Sk.astnodes.BinOp:case Sk.astnodes.UnaryOp:g="operator";break;case Sk.astnodes.GeneratorExp:g="generator expression";break;case Sk.astnodes.Yield:g="yield expression";break;case Sk.astnodes.ListComp:g="list comprehension";break;case Sk.astnodes.SetComp:g="set comprehension";break;case Sk.astnodes.DictComp:g="dict comprehension";break;case Sk.astnodes.Dict:case Sk.astnodes.Set:case Sk.astnodes.Num:case Sk.astnodes.Str:g="literal";break;case Sk.astnodes.NameConstant:g=
"True, False or None";break;case Sk.astnodes.Compare:g="comparison";break;case Sk.astnodes.Repr:g="repr";break;case Sk.astnodes.IfExp:g="conditional expression";break;default:Sk.asserts.fail("unhandled expression in assignment")}if(g)throw new Sk.builtin.SyntaxError("can't "+(c===Sk.astnodes.Store?"assign to":"delete")+" "+g,a.c_filename,d.lineno);if(e)for(b=0;b<e.length;++b)k(a,e[b],c,d)}function n(a){if(void 0===Q[a.type])throw new Sk.builtin.SyntaxError("invalid syntax",a.type,a.lineno);return Q[a.type]}
function l(a,b){return a.value?new Sk.builtin.str(a.value):new Sk.builtin.str(a)}function q(a,f){d(f,r.comp_op);if(1===b(f))switch(f=c(f,0),f.type){case w.T_LESS:return Sk.astnodes.Lt;case w.T_GREATER:return Sk.astnodes.Gt;case w.T_EQEQUAL:return Sk.astnodes.Eq;case w.T_LESSEQUAL:return Sk.astnodes.LtE;case w.T_GREATEREQUAL:return Sk.astnodes.GtE;case w.T_NOTEQUAL:return Sk.astnodes.NotEq;case w.T_NAME:if("in"===f.value)return Sk.astnodes.In;if("is"===f.value)return Sk.astnodes.Is}else if(2===b(f)&&
c(f,0).type===w.T_NAME){if("in"===c(f,1).value)return Sk.astnodes.NotIn;if("is"===c(f,0).value)return Sk.astnodes.IsNot}Sk.asserts.fail("invalid comp_op")}function u(a,b){a&&(a.lineno=b.lineno,a.col_offset=b.col_offset,a.end_lineno=b.end_lineno,a.end_col_offset=b.end_col_offset);return a}function z(a,d){var f,e=[];Sk.asserts.assert(d.type===r.testlist||d.type===r.testlist_star_expr||d.type===r.listmaker||d.type===r.testlist_comp||d.type===r.testlist_safe||d.type===r.testlist1,"node type must be listlike");
for(f=0;f<b(d);f+=2)Sk.asserts.assert(c(d,f).type===r.test||c(d,f).type===r.old_test||c(d,f).type===r.star_expr),e[f/2]=x(a,c(d,f));return e}function y(a,f){var e;d(f,r.suite);var k=[];var h=0;if(c(f,0).type===r.simple_stmt){f=c(f,0);var v=b(f)-1;c(f,v-1).type===w.T_SEMI&&--v;for(e=0;e<v;e+=2)k[h++]=O(a,c(f,e))}else for(e=2;e<b(f)-1;++e){v=c(f,e);d(v,r.stmt);var l=g(v);if(1===l)k[h++]=O(a,v);else for(v=c(v,0),d(v,r.simple_stmt),l=0;l<b(v);l+=2){if(0===b(c(v,l))){Sk.asserts.assert(l+1===b(v));break}k[h++]=
O(a,c(v,l))}}Sk.asserts.assert(h===g(f));return k}function t(a,f,e){var g;d(f,r.exprlist);var h=[];for(g=0;g<b(f);g+=2){var v=x(a,c(f,g));h[g/2]=v;e&&k(a,v,e,c(f,g))}return h}function H(a,d){a:for(;;)switch(d.type){case r.import_as_name:a=null;var f=h(c(d,0).value);3===b(d)&&(a=c(d,2).value);return new Sk.astnodes.alias(f,null==a?null:h(a));case r.dotted_as_name:if(1===b(d)){d=c(d,0);continue a}else return a=H(a,c(d,0)),Sk.asserts.assert(!a.asname),a.asname=h(c(d,2).value),a;case r.dotted_name:if(1===
b(d))return new Sk.astnodes.alias(h(c(d,0).value),null);a="";for(f=0;f<b(d);f+=2)a+=c(d,f).value+".";return new Sk.astnodes.alias(h(a.substr(0,a.length-1)),null);case w.T_STAR:return new Sk.astnodes.alias(h("*"),null);default:throw new Sk.builtin.SyntaxError("unexpected import name",a.c_filename,d.lineno);}}function K(a,b){Sk.asserts.assert(b.type==r.testlist_comp||b.type==r.argument);return ca(a,b,0)}function C(a,d){if(c(d,0).type===w.T_MINUS&&2===b(d)){var f=c(d,1);if(f.type===r.factor&&1===b(f)&&
(f=c(f,0),f.type===r.power&&1===b(f))){var e=c(f,0);if(e.type===r.atom&&(f=c(e,0),f.type===w.T_NUMBER))return f.value="-"+f.value,G(a,e)}}a=x(a,c(d,1));switch(c(d,0).type){case w.T_PLUS:return new Sk.astnodes.UnaryOp(Sk.astnodes.UAdd,a,d.lineno,d.col_offset);case w.T_MINUS:return new Sk.astnodes.UnaryOp(Sk.astnodes.USub,a,d.lineno,d.col_offset);case w.T_TILDE:return new Sk.astnodes.UnaryOp(Sk.astnodes.Invert,a,d.lineno,d.col_offset)}Sk.asserts.fail("unhandled factor")}function F(a,g,k,h){var v,l,
n;d(g,r.arglist);for(v=n=l=0;v<b(g);v++){var D=c(g,v);D.type==r.argument&&(1==b(D)?l++:c(D,1).type==r.comp_for?(l++,h||e(a,D,"invalid syntax"),1<b(g)&&e(a,D,"Generator expression must be parenthesized")):c(D,0).type==w.T_STAR?l++:n++)}var B=[];var q=[];for(v=h=n=l=0;v<b(g);v++)if(D=c(g,v),D.type==r.argument){var P=c(D,0);if(1==b(D)){n&&(h?e(a,P,"positional argument follows keyword argument unpacking"):e(a,P,"positional argument follows keyword argument"));var m=x(a,P);if(!m)return null;B[l++]=m}else if(P.type==
w.T_STAR){if(h)return e(a,P,"iterable argument unpacking follows keyword argument unpacking"),null;m=x(a,c(D,1));if(!m)return null;D=new Sk.astnodes.Starred(m,Sk.astnodes.Load,P.lineno,P.col_offset);B[l++]=D}else if(P.type==w.T_DOUBLESTAR){v++;m=x(a,c(D,1));if(!m)return null;D=new Sk.astnodes.keyword(null,m);q[n++]=D;h++}else if(c(D,1).type==r.comp_for){m=K(a,D);if(!m)return null;B[l++]=m}else{var p;m=x(a,P);if(!m)return null;if(m.constructor===Sk.astnodes.Lambda)return e(a,P,"lambda cannot contain assignment"),
null;if(m.constructor!==Sk.astnodes.Name)return e(a,P,"keyword can't be an expression"),null;if(f(a,m.id,D,1))return null;var u=m.id;for(p=0;p<n;p++)if((m=q[p].arg)&&m===u)return e(a,P,"keyword argument repeated"),null;m=x(a,c(D,2));if(!m)return null;D=new Sk.astnodes.keyword(u,m);q[n++]=D}}return new Sk.astnodes.Call(k,B,q,k.lineno,k.col_offset)}function L(a,f,e){d(f,r.trailer);if(c(f,0).type==w.T_LPAR)return 2==b(f)?new Sk.astnodes.Call(e,null,null,f.lineno,f.col_offset):F(a,c(f,1),e,!0);if(c(f,
0).type==w.T_DOT){var g=l(c(f,1));return g?new Sk.astnodes.Attribute(e,g,Sk.astnodes.Load,f.lineno,f.col_offset):null}d(c(f,0),w.T_LSQB);d(c(f,2),w.T_RSQB);f=c(f,1);if(1==b(f))return(g=A(a,c(f,0)))?new Sk.astnodes.Subscript(e,g,Sk.astnodes.Load,f.lineno,f.col_offset):null;var k,h=1,v=[];for(k=0;k<b(f);k+=2){g=A(a,c(f,k));if(!g)return null;g.kind!=da.Index_kind&&(h=0);v[k/2]=g}if(!h)return new Sk.astnodes.Subscript(e,new Sk.astnodes.ExtSlice(v),Sk.astnodes.Load,f.lineno,f.col_offset);a=[];for(k=0;k<
v.length;++k)g=v[k],Sk.asserts.assert(g.kind==da.Index_kind&&g.v.Index.value),a[k]=g.v.Index.value;g=new Sk.astnodes.Tuple(a,Sk.astnodes.Load,f.lineno,f.col_offset);return new Sk.astnodes.Subscript(e,new Sk.astnodes.Index(g),Sk.astnodes.Load,f.lineno,f.col_offset)}function W(a,f){d(f,r.flow_stmt);var g=c(f,0);switch(g.type){case r.break_stmt:return new Sk.astnodes.Break(f.lineno,f.col_offset,f.end_lineno,f.end_col_offset);case r.continue_stmt:return new Sk.astnodes.Continue(f.lineno,f.col_offset,
f.end_lineno,f.end_col_offset);case r.yield_stmt:return(a=x(a,c(g,0)))?new Sk.astnodes.Expr(a,f.lineno,f.col_offset,f.end_lineno,f.end_col_offset):null;case r.return_stmt:if(1==b(g))return new Sk.astnodes.Return(null,f.lineno,f.col_offset,f.end_lineno,f.end_col_offset);var k=M(a,c(g,1));return k?new Sk.astnodes.Return(k,f.lineno,f.col_offset,f.end_lineno,f.end_col_offset):null;case r.raise_stmt:if(1==b(g))return new Sk.astnodes.Raise(null,null,null,null,f.lineno,f.col_offset,f.end_lineno,f.end_col_offset);
if(2<=b(g)){var h=null;k=x(a,c(g,1));var l=null,v=null;4==b(g)&&"from"==c(g,2).value?(Sk.__future__.python3||e(a,c(g,2),"raise ... from ... is not available in Python 2"),h=x(a,c(g,3))):4<=b(g)&&","==c(g,2).value&&(Sk.__future__.python3&&e(a,f,"Old raise syntax is not available in Python 3"),l=x(a,c(g,3)),6==b(g)&&(v=x(a,c(g,5))));return new Sk.astnodes.Raise(k,h,l,v,f.lineno,f.col_offset,f.end_lineno,f.end_col_offset)}default:return Sk.asserts.fail("unexpected flow_stmt: ",g.type),null}}function N(a,
d){var e=null;Sk.asserts.assert(d.type===r.tfpdef||d.type===r.vfpdef);var g=c(d,0);f(a,g,g.value,g.lineno);g=h(g.value);3==b(d)&&c(d,1).type===w.T_COLON&&(e=x(a,c(d,2)));return new Sk.astnodes.arg(g,e,d.lineno,d.col_offset)}function E(a,d,g,k,l){var n=g,v=0;k||e(a,c(d,g),"named arguments must follow bare *");for(Sk.asserts.assert(l);n<b(d);){var D=c(d,n);switch(D.type){case r.vfpdef:case r.tfpdef:n+1<b(d)&&c(d,n+1).type==w.T_EQUAL?(l[v]=x(a,c(d,n+2)),n+=2):l[v]=null;var B=3==b(D)?x(a,c(D,2)):null;
D=c(D,0);f(a,D,D.value,D.lineno);g=h(D.value);k[v++]=new Sk.astnodes.arg(g,B,D.lineno,D.col_offset);n+=2;break;case w.T_DOUBLESTAR:return n;default:e(a,D,"unexpected node")}}return n}function X(a,d){var f,e,g,k=[],h=[],l=[],n=[],v=null,q=null;if(d.type===r.parameters){if(2===b(d))return new Sk.astnodes.arguments_([],null,[],[],null,[]);d=c(d,1)}Sk.asserts.assert(d.type===r.varargslist||d.type===r.typedargslist);for(f=e=g=0;g<b(d);){var B=c(d,g);switch(B.type){case r.tfpdef:case r.vfpdef:if(g+1<b(d)&&
c(d,g+1).type==w.T_EQUAL){h[e++]=x(a,c(d,g+2));g+=2;var m=1}else if(m)throw new Sk.builtin.SyntaxError("non-default argument follows default argument",a.c_filename,d.lineno);k[f++]=N(a,B);g+=2;break;case w.T_STAR:if(g+1>=b(d)||g+2==b(d)&&c(d,g+1).type==w.T_COMMA)throw new Sk.builtin.SyntaxError("named arguments must follow bare *",a.c_filename,d.lineno);B=c(d,g+1);B.type==w.T_COMMA?(g+=2,g=E(a,d,g,l,n)):(v=N(a,B),g+=3,g<b(d)&&(c(d,g).type==r.tfpdef||c(d,g).type==r.vfpdef)&&(g=E(a,d,g,l,n)));break;
case w.T_DOUBLESTAR:B=c(d,g+1);Sk.asserts.assert(B.type==r.tfpdef||B.type==r.vfpdef);q=N(a,B);g+=3;break;default:Sk.asserts.fail("unexpected node in varargslist");return}}return new Sk.astnodes.arguments_(k,v,l,n,q,h)}function aa(a,g,k,h){var n=h?c(g,1):g,v=null,D=1,q=null;if(h&&5>a.c_feature_version)return e(a,n,"Async functions are only supported in Python 3.5 and greater"),null;d(n,r.funcdef);var B=l(c(n,D));if(f(a,B,c(n,D),0))return null;var m=X(a,c(n,D+1));if(!m)return null;if(c(n,D+2).type==
w.T_RARROW){v=x(a,c(n,D+3));if(!v)return null;D+=2}if(c(n,D+3).type==w.T_TYPE_COMMENT){q=w.T_NEW_TYPE_COMMENT(c(n,D+3));if(!q)return null;D+=1}var P=y(a,c(n,D+3));if(!P)return null;if(1<b(c(n,D+3))&&(D=c(c(n,D+3),1),D.type==w.T_TYPE_COMMENT)){if(null!=q)return e(a,n,"Cannot have two type comments on def"),null;q=w.T_NEW_TYPE_COMMENT(D);if(!q)return null}return h?new Sk.astnodes.AsyncFunctionDef(B,m,P,k,v,q,g.lineno,g.col_offset,void 0,void 0):new Sk.astnodes.FunctionDef(B,m,P,k,v,q,n.lineno,n.col_offset,
void 0,void 0)}function S(a,e,g){d(e,r.classdef);if(4==b(e)){var k=y(a,c(e,3));var h=l(c(e,1).value);f(a,c(e,3),h,e.lineno);return new Sk.astnodes.ClassDef(h,[],[],k,g,null,e.lineno,e.col_offset)}if(c(e,3).type===w.T_RPAR)return k=y(a,c(e,5)),h=l(c(e,1).value),f(a,c(e,3),h,c(e,3).lineno),new Sk.astnodes.ClassDef(h,[],[],k,g,null,e.lineno,e.col_offset);h=l(c(e,1));h=new Sk.astnodes.Name(h,Sk.astnodes.Load,e.lineno,e.col_offset);var n=F(a,c(e,3),h,!1);k=y(a,c(e,6));h=l(c(e,1).value);f(a,c(e,1),h,c(e,
1).lineno);return new Sk.astnodes.ClassDef(h,n.args,n.keywords,k,g,null,e.lineno,e.col_offset)}function R(a,f){function e(a,f){for(a=0;;){d(f,r.comp_iter);if(c(f,0).type===r.comp_for)return a;f=c(f,0);d(f,r.comp_if);a++;if(2===b(f))return a;f=c(f,2)}}var g;var k=function(a,f){a=0;a:for(;;){a++;d(f,r.comp_for);if(5===b(f))f=c(f,4);else return a;b:for(;;){d(f,r.comp_iter);f=c(f,0);if(f.type===r.comp_for)continue a;else if(f.type===r.comp_if)if(3===b(f)){f=c(f,2);continue b}else return a;break}break}Sk.asserts.fail("logic error in countCompFors")}(a,
f);var h=[];for(g=0;g<k;++g){d(f,r.comp_for);var l=c(f,1);var n=t(a,l,Sk.astnodes.Store);var v=x(a,c(f,3));var q=1===b(l)?new Sk.astnodes.comprehension(n[0],v,[]):new Sk.astnodes.comprehension(new Sk.astnodes.Tuple(n,Sk.astnodes.Store,f.lineno,f.col_offset),v,[]);if(5===b(f)){f=c(f,4);var m=e(a,f);l=[];for(n=0;n<m;++n)d(f,r.comp_iter),f=c(f,0),d(f,r.comp_if),v=x(a,c(f,1)),l[n]=v,3===b(f)&&(f=c(f,2));f.type===r.comp_iter&&(f=c(f,0));q.ifs=l}h[g]=q}return h}function V(a,f){var e=[];a:{var g=f;var k=
0;b:for(;;){var h=0;k++;d(g,r.comp_for);c(g,0).type==w.T_ASYNC&&(h=1);if(b(g)==5+h)g=c(g,4+h);else break a;c:for(;;){d(g,r.comp_iter);g=c(g,0);if(g.type===r.comp_for)continue b;else if(g.type===r.comp_if)if(3===b(g)){g=c(g,2);continue c}else break a;break}break}k=void 0}for(g=0;g<k;g++){var l=0;c(f,0).type==w.T_ASYNC&&(l=1);var n=c(f,1+l);var v=t(a,n,Sk.astnodes.Store);if(!v)return null;h=x(a,c(f,3+l));if(!h)return null;var q=v[0];v=1==b(n)?new Sk.astnodes.comprehension(q,h,null,l):new Sk.astnodes.comprehension(new Sk.astnodes.Tuple(v,
Sk.astnodes.Store,q.lineno,q.col_offset,n.end_lineno,n.end_col_offset),h,null,l);if(b(f)==5+l){q=[];f=c(f,4+l);a:for(h=f,l=0;;){d(h,r.comp_iter);if(c(h,0).type==r.comp_for){n=l;break a}h=c(h,0);d(h,r.comp_if);l++;if(2==b(h)){n=l;break a}h=c(h,2)}if(-1==n)return null;for(l=0;l<n;l++){d(f,r.comp_iter);f=c(f,0);d(f,r.comp_if);h=x(a,c(f,1));if(!h)return null;q[l]=h;3==b(f)&&(f=c(f,2))}f.type==r.comp_iter&&(f=c(f,0));v.ifs=q}e[g]=v}return e}function ca(a,d,f){Sk.asserts.assert(1<b(d));var g=c(d,0);var k=
x(a,g);if(k.constructor===Sk.astnodes.Starred)return e(a,g,"iterable unpacking cannot be used in comprehension"),null;a=V(a,c(d,1));return 0==f?new Sk.astnodes.GeneratorExp(k,a,d.lineno,d.col_offset,d.end_lineno,d.end_col_offset):1==f?new Sk.astnodes.ListComp(k,a,d.lineno,d.col_offset,d.end_lineno,d.end_col_offset):2==f?new Sk.astnodes.SetComp(k,a,d.lineno,d.col_offset,d.end_lineno,d.end_col_offset):null}function ea(a,b){d(b,r.augassign);b=c(b,0);switch(b.value.charAt(0)){case "+":return Sk.astnodes.Add;
case "-":return Sk.astnodes.Sub;case "/":return"/"===b.value.charAt(1)?Sk.astnodes.FloorDiv:Sk.astnodes.Div;case "%":return Sk.astnodes.Mod;case "<":return Sk.astnodes.LShift;case ">":return Sk.astnodes.RShift;case "&":return Sk.astnodes.BitAnd;case "^":return Sk.astnodes.BitXor;case "|":return Sk.astnodes.BitOr;case "*":return"*"===b.value.charAt(1)?Sk.astnodes.Pow:Sk.astnodes.Mult;case "@":if(Sk.__future__.python3)return Sk.astnodes.MatMult;default:Sk.asserts.fail("invalid augassign")}}function M(a,
d){Sk.asserts.assert(0<b(d));d.type===r.testlist_comp?1<b(d)&&Sk.asserts.assert(c(d,1).type!==r.comp_for):Sk.asserts.assert(d.type===r.testlist||d.type===r.testlist_star_expr);return 1===b(d)?x(a,c(d,0)):new Sk.astnodes.Tuple(z(a,d),Sk.astnodes.Load,d.lineno,d.col_offset)}function I(a,e){d(e,r.expr_stmt);if(1===b(e))return new Sk.astnodes.Expr(M(a,c(e,0)),e.lineno,e.col_offset);if(c(e,1).type===r.augassign){var g=c(e,0);var h=M(a,g);k(a,h,Sk.astnodes.Store,g);switch(h.constructor){case Sk.astnodes.Name:var l=
h.id;f(a,g,l,e.lineno);break;case Sk.astnodes.Attribute:case Sk.astnodes.Subscript:break;case Sk.astnodes.GeneratorExp:throw new Sk.builtin.SyntaxError("augmented assignment to generator expression not possible",a.c_filename,e.lineno);case Sk.astnodes.Yield:throw new Sk.builtin.SyntaxError("augmented assignment to yield expression not possible",a.c_filename,e.lineno);default:throw new Sk.builtin.SyntaxError("illegal expression for augmented assignment",a.c_filename,e.lineno);}g=c(e,2);l=g.type===
r.testlist?M(a,g):x(a,g);return new Sk.astnodes.AugAssign(h,ea(a,c(e,1)),l,e.lineno,e.col_offset)}if(c(e,1).type===r.annassign){if(!Sk.__future__.python3)throw new Sk.builtin.SyntaxError("Annotated assignment is not supported in Python 2",a.c_filename,e.lineno);g=c(e,0);var n=c(e,1);var v=1;for(h=g;1==b(h);)h=c(h,0);0<b(h)&&c(h,0).type==w.T_LPAR&&(v=0);h=M(a,g);switch(h.constructor){case Sk.astnodes.Name:l=h.id;f(a,g,l,e.lineno);k(a,h,Sk.astnodes.Store,g);break;case Sk.astnodes.Attribute:l=h.attr;
f(a,g,l,e.lineno);k(a,h,Sk.astnodes.Store,g);break;case Sk.astnodes.Subscript:k(a,h,Sk.astnodes.Store,g);break;case Sk.astnodes.List:throw new Sk.builtin.SyntaxError("only single target (not list) can be annotated",a.c_filename,e.lineno);case Sk.astnodes.Tuple:throw new Sk.builtin.SyntaxError("only single target (not tuple) can be annotated",a.c_filename,e.lineno);default:throw new Sk.builtin.SyntaxError("illegal target for annotation",a.c_filename,e.lineno);}h.constructor!=Sk.astnodes.Name&&(v=0);
g=c(n,1);l=x(a,g);if(2==b(n))return new Sk.astnodes.AnnAssign(h,l,null,v,e.lineno,e.col_offset);g=c(n,3);a=x(a,g);return new Sk.astnodes.AnnAssign(h,l,a,v,e.lineno,e.col_offset)}d(c(e,1),w.T_EQUAL);v=[];for(h=0;h<b(e)-2;h+=2){g=c(e,h);if(g.type===r.yield_expr)throw new Sk.builtin.SyntaxError("assignment to yield expression not possible",a.c_filename,e.lineno);g=M(a,g);k(a,g,Sk.astnodes.Store,c(e,h));v[h/2]=g}g=c(e,b(e)-1);a=g.type===r.testlist_star_expr?M(a,g):x(a,g);return new Sk.astnodes.Assign(v,
a,e.lineno,e.col_offset)}function Z(a,b,c,d,f){Sk.asserts.assert(c>=b);Sk.asserts.assert("{"==a.charAt(b-1));Sk.asserts.assert("}"==a.charAt(c)||"!"==a.charAt(c)||":"==a.charAt(c));a=a.substring(b,c);/^\s*$/.test(a)&&e(d,f,"f-string: empty expression not allowed");try{let b=Sk.parse("<fstring>","("+a+")");var g=Sk.astFromParse(b.cst,"<fstring>",b.flags)}catch(ba){throw ba.traceback&&ba.traceback[0]&&(g=ba.traceback[0],g.lineno=(g.lineno||1)-1+f.lineno,g.filename=d.c_filename),ba;}Sk.asserts.assert(1==
g.body.length&&g.body[0].constructor===Sk.astnodes.Expr);return g.body[0].value}function Y(a,b,c,d,f,g,k){Sk.asserts.assert("{"==a.charAt(b));b++;var h=b;let l=null,n=0,D=0,v,q,m=()=>e(g,k,"f-string: expecting '}'");for(Sk.asserts.assert(b<=c);b<c;b++){let d=a.charAt(b);"\\"==d&&e(g,k,"f-string expression part cannot include a backslash");if(l)d==l&&(3==n?b+2<c&&a.charAt(b+1)==d&&a.charAt(b+2)==d&&(b+=2,l=n=0):n=l=0);else if("'"==d||'"'==d)b+2<c&&a.charAt(b+1)==d&&a.charAt(b+2)==d?(n=3,b+=2):n=1,
l=d;else if("["==d||"{"==d||"("==d)D++;else if(0!=D&&("]"==d||"}"==d||")"==d))D--;else if("#"==d)e(g,k,"f-string expression part cannot include '#'");else if(!(0!=D||"!"!=d&&":"!=d&&"}"!=d||"!"==d&&b+1<c&&"="==a.charAt(b+1)))break}l&&e(g,k,"f-string: unterminated string");D&&e(g,k,"f-string: mismatched '(', '{', or '['");h=Z(a,h,b,g,k);"!"==a.charAt(b)&&(b++,b>=c&&m(),q=a.charAt(b),b++,"s"!=q&&"r"!=q&&"a"!=q&&e(g,k,"f-string: invalid conversion character: expected 's', 'r', or 'a'"));b>=c&&m();":"==
a.charAt(b)&&(b++,b>=c&&m(),[v,b]=J(a,b,c,d,f+1,g,k));(b>=c||"}"!=a.charAt(b))&&m();b++;return[new Sk.astnodes.FormattedValue(h,q,v,k.lineno,k.col_offset),b]}function J(a,b,c,d,f,e,g){let k=[],h=a=>{if(-1!==a.indexOf("}")){if(/(^|[^}])}(}})*($|[^}])/.test(a))throw new SyntaxError("f-string: single '}' is not allowed",g.lineno,g.col_offset);a=a.replace(/}}/g,"}")}k.push(new Sk.astnodes.Str(new Sk.builtin.str(a),g.lineno,g.col_offset,e.end_lineno,g.end_col_offset))};for(;b<c;){let l=a.indexOf("{",b);
if(0!==f){let d=a.indexOf("}",b);-1!==d&&(-1===l?c=d:l>d&&(l=-1,c=d))}if(-1===l){h(a.substring(b,c));b=c;break}else if(l+1<c&&"{"===a.charAt(l+1))h(a.substring(b,l+1)),b=l+2;else{h(a.substring(b,l));b=l;let [n,D]=Y(a,l,c,d,f,e,g);k.push(n);b=D}}return[new Sk.astnodes.JoinedStr(k,g.lineno,g.col_offset),b]}function U(a,b,c){var d=b.charAt(b.length-1);if(-1!==b.indexOf("_")){if(fa.test(b))throw new Sk.builtin.SyntaxError("invalid syntax",a.c_filename,c);if(ha.test(b))throw new Sk.builtin.SyntaxError("invalid decimal literal",
a.c_filename,c);b=b.replace(ia,"")}if("j"===d||"J"===d)return Sk.builtin.complex.complex_subtype_from_string(b);if("l"===d||"L"===d)return Sk.longFromStr(b.substr(0,b.length-1),0);if(-1!==b.indexOf("."))return new Sk.builtin.float_(parseFloat(b));c=b;a=!1;"-"===b.charAt(0)&&(c=b.substr(1),a=!0);if("0"!==c.charAt(0)||"x"!==c.charAt(1)&&"X"!==c.charAt(1)){if(-1!==b.indexOf("e")||-1!==b.indexOf("E"))return new Sk.builtin.float_(parseFloat(b));if("0"!==c.charAt(0)||"b"!==c.charAt(1)&&"B"!==c.charAt(1))if("0"===
c.charAt(0))if("0"===c)c=0;else{c=c.substring(1);if("o"===c.charAt(0)||"O"===c.charAt(0))c=c.substring(1);c=parseInt(c,8)}else c=parseInt(c,10);else c=c.substring(2),c=parseInt(c,2)}else c=c.substring(2),c=parseInt(c,16);return c>Sk.builtin.int_.threshold$&&Math.floor(c)===c&&-1===b.indexOf("e")&&-1===b.indexOf("E")?Sk.longFromStr(b,0):a?new Sk.builtin.int_(-c):new Sk.builtin.int_(c)}function A(a,f){var e,g;d(f,r.subscript);var k=c(f,0);var h=e=g=null;if(k.type===w.T_DOT)return new Sk.astnodes.Ellipsis;
if(1===b(f)&&k.type===r.test)return new Sk.astnodes.Index(x(a,k));k.type===r.test&&(g=x(a,k));k.type===w.T_COLON?1<b(f)&&(k=c(f,1),k.type===r.test&&(e=x(a,k))):2<b(f)&&(k=c(f,2),k.type===r.test&&(e=x(a,k)));k=c(f,b(f)-1);k.type===r.sliceop&&(1===b(k)?(k=c(k,0),h=new Sk.astnodes.NameConstant(Sk.builtin.none.none$,Sk.astnodes.Load,k.lineno,k.col_offset)):(k=c(k,1),k.type===r.test&&(h=x(a,k))));return new Sk.astnodes.Slice(g,e,h)}function G(a,f){var g=c(f,0);switch(g.type){case w.T_NAME:var k=g.value;
if(4<=k.length&&5>=k.length){if("None"===k)return new Sk.astnodes.NameConstant(Sk.builtin.none.none$,f.lineno,f.col_offset);if("True"===k)return new Sk.astnodes.NameConstant(Sk.builtin.bool.true$,f.lineno,f.col_offset);if("False"===k)return new Sk.astnodes.NameConstant(Sk.builtin.bool.false$,f.lineno,f.col_offset)}a=l(k,a);return new Sk.astnodes.Name(a,Sk.astnodes.Load,f.lineno,f.col_offset,f.end_lineno,f.end_col_offset);case w.T_STRING:g=[];for(var n=0;n<b(f);++n){var q=c(f,n).value;var m=a;var v=
c(f,n);var p=q;var B=p.charAt(0);for(var t=!1,y=q=!1;;){if("u"!==B&&"U"!==B)if("r"===B||"R"===B)t=!0;else if("b"===B||"B"===B)y=!0;else if("f"===B||"F"===B)q=!0;else break;p=p.substr(1);B=p.charAt(0)}Sk.asserts.assert("'"===B||'"'===B&&p.charAt(p.length-1)===B);p=p.substr(1,p.length-2);4<=p.length&&p.charAt(0)===B&&p.charAt(1)===B&&(Sk.asserts.assert(p.charAt(p.length-1)===B&&p.charAt(p.length-2)===B),p=p.substr(2,p.length-4));if(t||-1===p.indexOf("\\")){if(y)for(B=0;B<p.length;B++)127<p.charCodeAt(B)&&
e(m,v,"bytes can only contain ASCII literal characters");m=[h(p),q,y]}else{t=p;var F=t.length,C="";for(p=0;p<F;++p)B=t.charAt(p),"\\"===B?(++p,B=t.charAt(p),"n"===B?C+="\n":"\\"===B?C+="\\":"t"===B?C+="\t":"r"===B?C+="\r":"b"===B?C+="\b":"f"===B?C+="\f":"v"===B?C+="\v":"0"===B?C+="\x00":'"'===B?C+='"':"'"===B?C+="'":"\n"!==B&&("x"===B?(p+2>=F&&e(m,v,"Truncated \\xNN escape"),C+=String.fromCharCode(parseInt(t.substr(p+1,2),16)),p+=2):y||"u"!==B?y||"U"!==B?C+="\\"+B:(p+8>=F&&e(m,v,"Truncated \\UXXXXXXXX escape"),
C+=String.fromCodePoint(parseInt(t.substr(p+1,8),16)),p+=8):(p+4>=F&&e(m,v,"Truncated \\uXXXX escape"),C+=String.fromCharCode(parseInt(t.substr(p+1,4),16)),p+=4))):y&&127<B.charCodeAt(0)?e(m,v,"bytes can only contain ASCII literal characters"):C+=B;m=C;m=[h(m),q,y]}q=m;m=q[0];v=q[1];q=q[2];0!=n&&k!==q&&e(a,f,"cannot mix bytes and nonbytes literals");k=q;if(v){if(!Sk.__future__.python3)throw new Sk.builtin.SyntaxError("invalid string (f-strings are not supported in Python 2)",a.c_filename,c(f,n).lineno);
var A=m.$jsstr();[A]=J(A,0,A.length,!1,0,a,c(f,n));g.push.apply(g,A.values);A=null}else A?A.s=A.s.sq$concat(m):(A=new (k?Sk.astnodes.Bytes:Sk.astnodes.Str)(m,f.lineno,f.col_offset,a.end_lineno,f.end_col_offset),g.push(A))}f=1===g.length&&g[0].constructor===Sk.astnodes.Str?g[0]:new Sk.astnodes.JoinedStr(g,f.lineno,f.col_offset,a.end_lineno,f.end_col_offset);return f;case w.T_NUMBER:return new Sk.astnodes.Num(U(a,g.value,f.lineno),f.lineno,f.col_offset);case w.T_ELLIPSIS:return new Sk.astnodes.Ellipsis(f.lineno,
f.col_offset,f.end_lineno,f.end_col_offset);case w.T_LPAR:return g=c(f,1),g.type==w.T_RPAR?new Sk.astnodes.Tuple([],Sk.astnodes.Load,f.lineno,f.col_offset,f.end_lineno,f.end_col_offset):g.type==r.yield_expr?x(a,g):1==b(g)?M(a,g):c(g,1).type==r.comp_for?u(K(a,g),f):u(M(a,g),f);case w.T_LSQB:g=c(f,1);if(g.type==w.T_RSQB)return new Sk.astnodes.List([],Sk.astnodes.Load,f.lineno,f.col_offset,f.end_lineno,f.end_col_offset);d(g,r.testlist_comp);if(1==b(g)||c(g,1).type==w.T_COMMA)return(a=z(a,g))?new Sk.astnodes.List(a,
Sk.astnodes.Load,f.lineno,f.col_offset,f.end_lineno,f.end_col_offset):null;k=g;Sk.asserts.assert(k.type==r.testlist_comp);a=ca(a,k,1);return u(a,f);case w.T_LBRACE:g=c(f,1);if(g.type==w.T_RBRACE)return new Sk.astnodes.Dict(null,null,f.lineno,f.col_offset,f.end_lineno,f.end_col_offset);k=c(g,0).type==w.T_DOUBLESTAR;if(1==b(g)||1<b(g)&&c(g,1).type==w.T_COMMA){k=g;n=[];Sk.asserts.assert(k.type===r.dictorsetmaker);for(g=0;g<b(k);g+=2)A=x(a,c(k,g)),n[g/2]=A;a=new Sk.astnodes.Set(n,k.lineno,k.col_offset)}else if(1<
b(g)&&c(g,1).type==r.comp_for)k=g,Sk.asserts.assert(k.type===r.dictorsetmaker),Sk.asserts.assert(1<b(k)),g=x(a,c(k,0)),a=R(a,c(k,1)),a=new Sk.astnodes.SetComp(g,a,k.lineno,k.col_offset);else if(b(g)>3-k&&c(g,3-k).type==r.comp_for){if(k)return e(a,f,"dict unpacking cannot be used in dict comprehension"),null;k=g;Sk.asserts.assert(3<b(k));d(c(k,1),w.T_COLON);g=x(a,c(k,0));n=x(a,c(k,2));a=R(a,c(k,3));a=new Sk.astnodes.DictComp(g,n,a,k.lineno,k.col_offset)}else{k=g;n=[];A=[];for(q=g=0;q<b(k);q++)m=a,
v=k,y=q,c(v,y).type==w.T_DOUBLESTAR?(Sk.asserts.assert(2<=b(v)-y),q=x(m,c(v,y+1)),m={key:null,value:q,i:y+2}):(Sk.asserts.assert(3<=b(v)-y),(q=x(m,c(v,y)))?(p=q,d(c(v,y+1),w.T_COLON),m=(q=x(m,c(v,y+2)))?{key:p,value:q,i:y+3}:!1):m=0),q=m.i,n[g]=m.key,A[g]=m.value,g++;a=new Sk.astnodes.Dict(n,A,k.lineno,k.col_offset,k.end_lineno,k.end_col_offset)}return u(a,f);default:return Sk.asserts.fail("unhandled atom "+g.type),null}}function T(a,f){var e,g=0;d(f,r.atom_expr);var k=b(f);c(f,0).type===w.T_AWAIT&&
(g=1,Sk.asserts.assert(1<k));var h=G(a,c(f,g));if(!h)return null;if(1===k)return h;if(g&&2===k)return new Sk.astnodes.Await(h,f.lineno,f.col_offset);for(e=g+1;e<k;e++){var l=c(f,e);if(l.type!==r.trailer)break;l=L(a,l,h);if(!l)return null;l.lineno=h.lineno;l.col_offset=h.col_offset;h=l}return g?new Sk.astnodes.Await(h,f.line,f.col_offset):h}function x(a,f){a:for(;;){switch(f.type){case r.test:case r.test_nocond:if(c(f,0).type===r.lambdef||c(f,0).type===r.lambdef_nocond){var e=c(f,0);3===b(e)?(f=new Sk.astnodes.arguments_([],
null,null,[]),a=x(a,c(e,2))):(f=X(a,c(e,1)),a=x(a,c(e,3)));return new Sk.astnodes.Lambda(f,a,e.lineno,e.col_offset)}if(1<b(f))return Sk.asserts.assert(5===b(f)),new Sk.astnodes.IfExp(x(a,c(f,2)),x(a,c(f,0)),x(a,c(f,4)),f.lineno,f.col_offset);case r.or_test:case r.and_test:if(1===b(f)){f=c(f,0);continue a}var g=[];for(e=0;e<b(f);e+=2)g[e/2]=x(a,c(f,e));if("and"===c(f,1).value)return new Sk.astnodes.BoolOp(Sk.astnodes.And,g,f.lineno,f.col_offset);Sk.asserts.assert("or"===c(f,1).value);return new Sk.astnodes.BoolOp(Sk.astnodes.Or,
g,f.lineno,f.col_offset);case r.not_test:if(1===b(f)){f=c(f,0);continue a}else return new Sk.astnodes.UnaryOp(Sk.astnodes.Not,x(a,c(f,1)),f.lineno,f.col_offset);case r.comparison:if(1===b(f)){f=c(f,0);continue a}else{var k=[];g=[];for(e=1;e<b(f);e+=2)k[(e-1)/2]=q(a,c(f,e)),g[(e-1)/2]=x(a,c(f,e+1));return new Sk.astnodes.Compare(x(a,c(f,0)),k,g,f.lineno,f.col_offset)}case r.star_expr:return d(f,r.star_expr),new Sk.astnodes.Starred(x(a,c(f,1)),Sk.astnodes.Load,f.lineno,f.col_offset);case r.expr:case r.xor_expr:case r.and_expr:case r.shift_expr:case r.arith_expr:case r.term:if(1===
b(f)){f=c(f,0);continue a}var h=f,l=new Sk.astnodes.BinOp(x(a,c(h,0)),n(c(h,1)),x(a,c(h,2)),h.lineno,h.col_offset),m=(b(h)-1)/2;for(f=1;f<m;++f)e=c(h,2*f+1),g=n(e),k=x(a,c(h,2*f+2)),l=new Sk.astnodes.BinOp(l,g,k,e.lineno,e.col_offset);return l;case r.yield_expr:return g=!1,k=null,1<b(f)&&(e=c(f,1)),e&&(k=c(e,b(e)-1),2==b(e)?(g=!0,k=x(a,k)):k=M(a,k)),g?new Sk.astnodes.YieldFrom(k,f.lineno,f.col_offset):new Sk.astnodes.Yield(k,f.lineno,f.col_offset);case r.factor:if(1===b(f)){f=c(f,0);continue a}return C(a,
f);case r.power:return e=f,d(e,r.power),f=T(a,c(e,0)),1!==b(e)&&c(e,b(e)-1).type===r.factor&&(a=x(a,c(e,b(e)-1)),f=new Sk.astnodes.BinOp(f,Sk.astnodes.Pow,a,e.lineno,e.col_offset)),f;default:Sk.asserts.fail("unhandled expr","n.type: %d",f.type)}break}}function O(a,f){f.type===r.stmt&&(Sk.asserts.assert(1===b(f)),f=c(f,0));f.type===r.simple_stmt&&(Sk.asserts.assert(1===g(f)),f=c(f,0));if(f.type===r.small_stmt)switch(f=c(f,0),f.type){case r.expr_stmt:return I(a,f);case r.del_stmt:var l=f;d(l,r.del_stmt);
return new Sk.astnodes.Delete(t(a,c(l,1),Sk.astnodes.Del),l.lineno,l.col_offset);case r.pass_stmt:return new Sk.astnodes.Pass(f.lineno,f.col_offset);case r.flow_stmt:return W(a,f);case r.import_stmt:var n=f,q;d(n,r.import_stmt);var m=n.lineno;f=n.col_offset;n=c(n,0);if(n.type===r.import_name){n=c(n,1);d(n,r.dotted_as_names);var p=[];for(q=0;q<b(n);q+=2)p[q/2]=H(a,c(n,q));a=new Sk.astnodes.Import(p,m,f)}else if(n.type===r.import_from){var u=null;l=0;for(p=1;p<b(n);++p)if(c(n,p).type===r.dotted_name){u=
H(a,c(n,p));p++;break}else if(c(n,p).type===w.T_DOT)l++;else if(c(n,p).type===w.T_ELLIPSIS)l+=3;else break;++p;switch(c(n,p).type){case w.T_STAR:n=c(n,p);break;case w.T_LPAR:n=c(n,p+1);b(n);break;case r.import_as_names:n=c(n,p);p=b(n);if(0===p%2)throw new Sk.builtin.SyntaxError("trailing comma not allowed without surrounding parentheses",a.c_filename,n.lineno);break;default:throw new Sk.builtin.SyntaxError("Unexpected node-type in from-import",a.c_filename,n.lineno);}p=[];if(n.type===w.T_STAR)p[0]=
H(a,n);else for(q=0;q<b(n);q+=2)p[q/2]=H(a,c(n,q));a=u?u.name.v:"";a=new Sk.astnodes.ImportFrom(h(a),p,l,m,f)}else throw new Sk.builtin.SyntaxError("unknown import statement",a.c_filename,n.lineno);return a;case r.global_stmt:a=f;f=[];d(a,r.global_stmt);for(l=1;l<b(a);l+=2)f[(l-1)/2]=h(c(a,l).value);return new Sk.astnodes.Global(f,a.lineno,a.col_offset);case r.nonlocal_stmt:e(a,f,"Not implemented: nonlocal");break;case r.assert_stmt:return l=f,d(l,r.assert_stmt),2===b(l)?a=new Sk.astnodes.Assert(x(a,
c(l,1)),null,l.lineno,l.col_offset):4===b(l)?a=new Sk.astnodes.Assert(x(a,c(l,1)),x(a,c(l,3)),l.lineno,l.col_offset):(Sk.asserts.fail("improper number of parts to assert stmt"),a=void 0),a;case r.print_stmt:l=f;Sk.__future__.print_function&&e(a,l,"Missing parentheses in call to 'print'");n=1;m=null;d(l,r.print_stmt);2<=b(l)&&c(l,1).type===w.T_RIGHTSHIFT&&(m=x(a,c(l,2)),n=4);f=[];for(u=0;n<b(l);n+=2,++u)f[u]=x(a,c(l,n));a=c(l,b(l)-1).type===w.T_COMMA?!1:!0;return new Sk.astnodes.Print(m,f,a,l.lineno,
l.col_offset);case r.debugger_stmt:return new Sk.astnodes.Debugger(f.lineno,f.col_offset);default:Sk.asserts.fail("unhandled small_stmt")}else switch(l=c(f,0),d(f,r.compound_stmt),l.type){case r.if_stmt:d(l,r.if_stmt);if(4===b(l))a=new Sk.astnodes.If(x(a,c(l,1)),y(a,c(l,3)),[],l.lineno,l.col_offset);else if(f=c(l,4).value.charAt(2),"s"===f)a=new Sk.astnodes.If(x(a,c(l,1)),y(a,c(l,3)),y(a,c(l,6)),l.lineno,l.col_offset);else if("i"===f){m=b(l)-4;n=!1;f=[];c(l,m+1).type===w.T_NAME&&"s"===c(l,m+1).value.charAt(2)&&
(n=!0,m-=3);m/=4;n&&(f=[new Sk.astnodes.If(x(a,c(l,b(l)-6)),y(a,c(l,b(l)-4)),y(a,c(l,b(l)-1)),c(l,b(l)-6).lineno,c(l,b(l)-6).col_offset)],m--);for(u=0;u<m;++u)n=5+4*(m-u-1),f=[new Sk.astnodes.If(x(a,c(l,n)),y(a,c(l,n+2)),f,c(l,n).lineno,c(l,n).col_offset)];a=new Sk.astnodes.If(x(a,c(l,1)),y(a,c(l,3)),f,l.lineno,l.col_offset)}else Sk.asserts.fail("unexpected token in 'if' statement"),a=void 0;return a;case r.while_stmt:return d(l,r.while_stmt),4===b(l)?a=new Sk.astnodes.While(x(a,c(l,1)),y(a,c(l,3)),
[],l.lineno,l.col_offset):7===b(l)?a=new Sk.astnodes.While(x(a,c(l,1)),y(a,c(l,3)),y(a,c(l,6)),l.lineno,l.col_offset):(Sk.asserts.fail("wrong number of tokens for 'while' stmt"),a=void 0),a;case r.for_stmt:return f=[],d(l,r.for_stmt),9===b(l)&&(f=y(a,c(l,8))),n=c(l,1),m=t(a,n,Sk.astnodes.Store),m=1===b(n)?m[0]:new Sk.astnodes.Tuple(m,Sk.astnodes.Store,l.lineno,l.col_offset),new Sk.astnodes.For(m,M(a,c(l,3)),y(a,c(l,5)),f,l.lineno,l.col_offset);case r.try_stmt:f=[];q=b(l);m=(q-3)/3;u=[];p=null;d(l,
r.try_stmt);n=y(a,c(l,2));if(c(l,q-3).type===w.T_NAME)"finally"===c(l,q-3).value?(9<=q&&c(l,q-6).type===w.T_NAME&&(u=y(a,c(l,q-4)),m--),p=y(a,c(l,q-1))):u=y(a,c(l,q-1)),m--;else if(c(l,q-3).type!==r.except_clause)throw new Sk.builtin.SyntaxError("malformed 'try' statement",a.c_filename,l.lineno);if(0<m)for(q=0;q<m;q++){var z=q;var v=a,C=c(l,3+3*q),A=c(l,5+3*q);d(C,r.except_clause);d(A,r.suite);if(1===b(C))var B=new Sk.astnodes.ExceptHandler(null,null,y(v,A),C.lineno,C.col_offset);else 2===b(C)?B=
new Sk.astnodes.ExceptHandler(x(v,c(C,1)),null,y(v,A),C.lineno,C.col_offset):4===b(C)?(Sk.__future__.python3&&","==c(C,2).value&&e(v,C,"Old-style 'except' clauses are not supported in Python 3"),x(v,c(C,1)),B=x(v,c(C,3)),k(v,B,Sk.astnodes.Store,c(C,3)),B=new Sk.astnodes.ExceptHandler(x(v,c(C,1)),B,y(v,A),C.lineno,C.col_offset)):(Sk.asserts.fail("wrong number of children for except clause"),B=void 0);f[z]=B}Sk.asserts.assert(!!p||0!=f.length);return new Sk.astnodes.Try(n,f,u,p,l.lineno,l.col_offset);
case r.with_stmt:f=[];d(l,r.with_stmt);for(m=1;m<b(l)-2;m+=2)n=void 0,p=a,q=c(l,m),d(q,r.with_item),u=x(p,c(q,0)),3==b(q)&&(n=x(p,c(q,2)),k(p,n,Sk.astnodes.Store,q)),n=new Sk.astnodes.withitem(u,n),f[(m-1)/2]=n;a=y(a,c(l,b(l)-1));a=new Sk.astnodes.With(f,a,l.lineno,l.col_offset);return a;case r.funcdef:return aa(a,l,[],!1);case r.classdef:return S(a,l,[]);case r.decorated:m=null;d(l,r.decorated);n=c(l,0);d(n,r.decorators);f=[];for(u=0;u<b(n);++u){p=f;q=u;z=a;B=c(n,u);d(B,r.decorator);d(c(B,0),w.T_AT);
d(c(B,b(B)-1),w.T_NEWLINE);var K,E=c(B,1);d(E,r.dotted_name);v=E.lineno;C=E.col_offset;A=h(c(E,0).value);var L=new Sk.astnodes.Name(A,Sk.astnodes.Load,v,C);for(K=2;K<b(E);K+=2)A=h(c(E,K).value),L=new Sk.astnodes.Attribute(L,A,Sk.astnodes.Load,v,C);v=L;z=3===b(B)?v:5===b(B)?new Sk.astnodes.Call(v,[],[],null,null,B.lineno,B.col_offset):F(z,c(B,3),v);p[q]=z}Sk.asserts.assert(c(l,1).type==r.funcdef||c(l,1).type==r.async_funcdef||c(l,1).type==r.classdef);c(l,1).type==r.funcdef?(m=c(l,1),m=aa(a,m,f,!1)):
c(l,1).type==r.classdef?m=S(a,c(l,1),f):c(l,1).type==r.async_funcdef&&(m=c(l,1),d(m,r.async_funcdef),d(c(m,0),w.T_NAME),Sk.asserts.assert(("async"===c(m,0)).value),d(c(m,1),r.funcdef),m=aa(a,m,f,!0));m&&(m.lineno=l.lineno,m.col_offset=l.col_offset);return m;case r.async_stmt:e(a,l,"Not implemented: async");break;default:Sk.asserts.assert("unhandled compound_stmt")}}var r=Sk.ParseTables.sym,w=Sk.token.tokens,da={Slice_kind:1,ExtSlice_kind:2,Index_kind:3},Q={};Q[w.T_VBAR]=Sk.astnodes.BitOr;Q[w.T_CIRCUMFLEX]=
Sk.astnodes.BitXor;Q[w.T_AMPER]=Sk.astnodes.BitAnd;Q[w.T_LEFTSHIFT]=Sk.astnodes.LShift;Q[w.T_RIGHTSHIFT]=Sk.astnodes.RShift;Q[w.T_PLUS]=Sk.astnodes.Add;Q[w.T_MINUS]=Sk.astnodes.Sub;Q[w.T_STAR]=Sk.astnodes.Mult;Q[w.T_SLASH]=Sk.astnodes.Div;Q[w.T_DOUBLESLASH]=Sk.astnodes.FloorDiv;Q[w.T_PERCENT]=Sk.astnodes.Mod;Sk.setupOperators=function(a){a?Q[w.T_AT]=Sk.astnodes.MatMult:Q[w.T_AT]&&delete Q[w.T_AT]};Sk.exportSymbol("Sk.setupOperators",Sk.setupOperators);const fa=/_[eE]|[eE]_|\._|j_/,ha=/_\.|[+-]_|^0_\D|_j/,
ia=/_(?=[^_])/g;Sk.astFromParse=function(f,e,k){var h,l=new a("utf-8",e,k),n=[],q=0;switch(f.type){case r.file_input:for(h=0;h<b(f)-1;++h){var m=c(f,h);if(f.type!==w.T_NEWLINE)if(d(m,r.stmt),k=g(m),1===k)n[q++]=O(l,m);else for(m=c(m,0),d(m,r.simple_stmt),e=0;e<k;++e)n[q++]=O(l,c(m,2*e))}return new Sk.astnodes.Module(n);case r.eval_input:Sk.asserts.fail("todo;");case r.single_input:Sk.asserts.fail("todo;");default:Sk.asserts.fail("todo;")}};Sk.astDump=function(a){var b=function(a){var b,c="";for(b=
0;b<a;++b)c+=" ";return c},c=function(a,f){var d;if(null===a)return f+"None";if(a.prototype&&void 0!==a.prototype._astname&&a.prototype._isenum)return f+a.prototype._astname+"()";if(void 0!==a._astname){var e=b(a._astname.length+1);var g=[];for(d=0;d<a._fields.length;d+=2){var k=a._fields[d];var h=a._fields[d+1](a);var l=b(k.length+1);g.push([k,c(h,f+e+l)])}h=[];for(d=0;d<g.length;++d)l=g[d],h.push(l[0]+"="+l[1].replace(/^\s+/,""));d=h.join(",\n"+f+e);return f+a._astname+"("+d+")"}if(Sk.isArrayLike(a)){e=
[];for(d=0;d<a.length;++d)g=a[d],e.push(c(g,f+" "));a=e.join(",\n");return f+"["+a.replace(/^\s+/,"")+"]"}a=!0===a?"True":!1===a?"False":a instanceof Sk.builtin.lng?a.tp$str().v:a instanceof Sk.builtin.str?a.$r().v:""+a;return f+a};return c(a,"")};Sk.exportSymbol("Sk.astFromParse",Sk.astFromParse);Sk.exportSymbol("Sk.astDump",Sk.astDump)},function(m,p){function a(a,b,c){this.__name=a;this.__flags=b;this.__scope=b>>11&7;this.__namespaces=c||[]}function b(a,b,c,d,e){this.symFlags={};this.name=b;this.varnames=
[];this.children=[];this.blockType=c;this.returnsValue=this.varkeywords=this.varargs=this.generator=this.childHasFree=this.hasFree=this.isNested=!1;this.lineno=e;this.table=a;a.cur&&(a.cur.nested||"function"===a.cur.blockType)&&(this.isNested=!0);d.scopeId=h++;a.stss[d.scopeId]=this;this.symbols={}}function c(a){this.filename=a;this.top=this.cur=null;this.stack=[];this.curClass=this.global=null;this.tmpname=0;this.stss={}}function d(a,b){var c;for(c=0;c<b.length;c++)a(b[c])}function e(a,b){for(var c in b)a[c]=
b[c]}Sk.exportSymbol("Sk.SYMTAB_CONSTS",{DEF_GLOBAL:1,DEF_LOCAL:2,DEF_PARAM:4,USE:8,DEF_STAR:16,DEF_DOUBLESTAR:32,DEF_INTUPLE:64,DEF_FREE:128,DEF_FREE_GLOBAL:256,DEF_FREE_CLASS:512,DEF_IMPORT:1024,DEF_BOUND:1030,SCOPE_OFF:11,SCOPE_MASK:7,LOCAL:1,GLOBAL_EXPLICIT:2,GLOBAL_IMPLICIT:3,FREE:4,CELL:5,OPT_IMPORT_STAR:1,OPT_EXEC:2,OPT_BARE_EXEC:4,OPT_TOPLEVEL:8,GENERATOR:2,GENERATOR_EXPRESSION:2,ModuleBlock:"module",FunctionBlock:"function",ClassBlock:"class"});a.prototype.get_name=function(){return this.__name};
a.prototype.is_referenced=function(){return!!(this.__flags&8)};a.prototype.is_parameter=function(){return!!(this.__flags&4)};a.prototype.is_global=function(){return 3===this.__scope||2==this.__scope};a.prototype.is_declared_global=function(){return 2==this.__scope};a.prototype.is_local=function(){return!!(this.__flags&1030)};a.prototype.is_free=function(){return 4==this.__scope};a.prototype.is_imported=function(){return!!(this.__flags&1024)};a.prototype.is_assigned=function(){return!!(this.__flags&
2)};a.prototype.is_namespace=function(){return this.__namespaces&&0<this.__namespaces.length};a.prototype.get_namespaces=function(){return this.__namespaces};var h=0;b.prototype.get_type=function(){return this.blockType};b.prototype.get_name=function(){return this.name};b.prototype.get_lineno=function(){return this.lineno};b.prototype.is_nested=function(){return this.isNested};b.prototype.has_children=function(){return 0<this.children.length};b.prototype.get_identifiers=function(){return this._identsMatching(function(){return!0})};
b.prototype.lookup=function(b){if(this.symbols.hasOwnProperty(b))b=this.symbols[b];else{var c=this.symFlags[b];var d=this.__check_children(b);b=this.symbols[b]=new a(b,c,d)}return b};b.prototype.__check_children=function(a){var b,c=[];for(b=0;b<this.children.length;++b){var d=this.children[b];d.name===a&&c.push(d)}return c};b.prototype._identsMatching=function(a){var b,c=[];for(b in this.symFlags)this.symFlags.hasOwnProperty(b)&&a(this.symFlags[b])&&c.push(b);c.sort();return c};b.prototype.get_parameters=
function(){Sk.asserts.assert("function"==this.get_type(),"get_parameters only valid for function scopes");this._funcParams||(this._funcParams=this._identsMatching(function(a){return a&4}));return this._funcParams};b.prototype.get_locals=function(){Sk.asserts.assert("function"==this.get_type(),"get_locals only valid for function scopes");this._funcLocals||(this._funcLocals=this._identsMatching(function(a){return a&1030}));return this._funcLocals};b.prototype.get_globals=function(){Sk.asserts.assert("function"==
this.get_type(),"get_globals only valid for function scopes");this._funcGlobals||(this._funcGlobals=this._identsMatching(function(a){a=a>>11&7;return 3==a||2==a}));return this._funcGlobals};b.prototype.get_frees=function(){Sk.asserts.assert("function"==this.get_type(),"get_frees only valid for function scopes");this._funcFrees||(this._funcFrees=this._identsMatching(function(a){return 4==(a>>11&7)}));return this._funcFrees};b.prototype.get_methods=function(){var a;Sk.asserts.assert("class"==this.get_type(),
"get_methods only valid for class scopes");if(!this._classMethods){var b=[];for(a=0;a<this.children.length;++a)b.push(this.children[a].name);b.sort();this._classMethods=b}return this._classMethods};b.prototype.getScope=function(a){a=this.symFlags[a];return void 0===a?0:a>>11&7};c.prototype.getStsForAst=function(a){Sk.asserts.assert(void 0!==a.scopeId,"ast wasn't added to st?");a=this.stss[a.scopeId];Sk.asserts.assert(void 0!==a,"unknown sym tab entry");return a};c.prototype.SEQStmt=function(a){var b,
c;if(null!==a){Sk.asserts.assert(Sk.isArrayLike(a),"SEQ: nodes isn't array? got "+a.toString());var d=a.length;for(c=0;c<d;++c)(b=a[c])&&this.visitStmt(b)}};c.prototype.SEQExpr=function(a){var b,c;if(null!==a){Sk.asserts.assert(Sk.isArrayLike(a),"SEQ: nodes isn't array? got "+a.toString());var d=a.length;for(c=0;c<d;++c)(b=a[c])&&this.visitExpr(b)}};c.prototype.enterBlock=function(a,c,d,e){a=Sk.fixReserved(a);var f=null;this.cur&&(f=this.cur,this.stack.push(this.cur));this.cur=new b(this,a,c,d,e);
"top"===a&&(this.global=this.cur.symFlags);f&&f.children.push(this.cur)};c.prototype.exitBlock=function(){this.cur=null;0<this.stack.length&&(this.cur=this.stack.pop())};c.prototype.visitParams=function(a,b){var c;for(c=0;c<a.length;++c)if(b=a[c],b.constructor===Sk.astnodes.arg)this.addDef(b.arg,4,b.lineno);else throw new Sk.builtin.SyntaxError("invalid expression in parameter list",this.filename);};c.prototype.visitArguments=function(a,b){a.args&&this.visitParams(a.args,!0);a.kwonlyargs&&this.visitParams(a.kwonlyargs,
!0);a.vararg&&(this.addDef(a.vararg.arg,4,b),this.cur.varargs=!0);a.kwarg&&(this.addDef(a.kwarg.arg,4,b),this.cur.varkeywords=!0)};c.prototype.newTmpname=function(a){this.addDef(new Sk.builtin.str("_["+ ++this.tmpname+"]"),2,a)};c.prototype.addDef=function(a,b,c){var d=Sk.mangleName(this.curClass,a).v;d=Sk.fixReserved(d);var f=this.cur.symFlags[d];if(void 0!==f){if(b&4&&f&4)throw new Sk.builtin.SyntaxError("duplicate argument '"+a.v+"' in function definition",this.filename,c);f|=b}else f=b;this.cur.symFlags[d]=
f;b&4?this.cur.varnames.push(d):b&1&&(f=b,a=this.global[d],void 0!==a&&(f|=a),this.global[d]=f)};c.prototype.visitSlice=function(a){var b;switch(a.constructor){case Sk.astnodes.Slice:a.lower&&this.visitExpr(a.lower);a.upper&&this.visitExpr(a.upper);a.step&&this.visitExpr(a.step);break;case Sk.astnodes.ExtSlice:for(b=0;b<a.dims.length;++b)this.visitSlice(a.dims[b]);break;case Sk.astnodes.Index:this.visitExpr(a.value)}};c.prototype.visitStmt=function(a){var b;Sk.asserts.assert(void 0!==a,"visitStmt called with undefined");
switch(a.constructor){case Sk.astnodes.FunctionDef:this.addDef(a.name,2,a.lineno);a.args.defaults&&this.SEQExpr(a.args.defaults);a.decorator_list&&this.SEQExpr(a.decorator_list);this.enterBlock(a.name.v,"function",a,a.lineno);this.visitArguments(a.args,a.lineno);this.SEQStmt(a.body);this.exitBlock();break;case Sk.astnodes.ClassDef:this.addDef(a.name,2,a.lineno);this.SEQExpr(a.bases);a.decorator_list&&this.SEQExpr(a.decorator_list);this.enterBlock(a.name.v,"class",a,a.lineno);this.curClass=a.name;
this.SEQStmt(a.body);this.exitBlock();break;case Sk.astnodes.Return:if(a.value&&(this.visitExpr(a.value),this.cur.returnsValue=!0,this.cur.generator))throw new Sk.builtin.SyntaxError("'return' with argument inside generator",this.filename);break;case Sk.astnodes.Delete:this.SEQExpr(a.targets);break;case Sk.astnodes.Assign:this.SEQExpr(a.targets);this.visitExpr(a.value);break;case Sk.astnodes.AnnAssign:if(a.target.constructor==Sk.astnodes.Name){var c=a.target;var e=Sk.mangleName(this.curClass,c.id).v;
e=Sk.fixReserved(e);c=this.cur.symFlags[e];if(c&2049&&this.global!=this.cur.symFlags&&a.simple)throw new Sk.builtin.SyntaxError("annotated name '"+e+"' can't be global",this.filename,a.lineno);a.simple?this.addDef(new Sk.builtin.str(e),4098,a.lineno):a.value&&this.addDef(new Sk.builtin.str(e),2,a.lineno)}else this.visitExpr(a.target);this.visitExpr(a.annotation);a.value&&this.visitExpr(a.value);break;case Sk.astnodes.AugAssign:this.visitExpr(a.target);this.visitExpr(a.value);break;case Sk.astnodes.Print:a.dest&&
this.visitExpr(a.dest);this.SEQExpr(a.values);break;case Sk.astnodes.For:this.visitExpr(a.target);this.visitExpr(a.iter);this.SEQStmt(a.body);a.orelse&&this.SEQStmt(a.orelse);break;case Sk.astnodes.While:this.visitExpr(a.test);this.SEQStmt(a.body);a.orelse&&this.SEQStmt(a.orelse);break;case Sk.astnodes.If:this.visitExpr(a.test);this.SEQStmt(a.body);a.orelse&&this.SEQStmt(a.orelse);break;case Sk.astnodes.Raise:a.exc&&(this.visitExpr(a.exc),a.inst&&(this.visitExpr(a.inst),a.tback&&this.visitExpr(a.tback)),
a.cause&&this.visitExpr(a.cause));break;case Sk.astnodes.Assert:this.visitExpr(a.test);a.msg&&this.visitExpr(a.msg);break;case Sk.astnodes.Import:case Sk.astnodes.ImportFrom:this.visitAlias(a.names,a.lineno);break;case Sk.astnodes.Global:var g=a.names.length;for(b=0;b<g;++b){e=Sk.mangleName(this.curClass,a.names[b]).v;e=Sk.fixReserved(e);c=this.cur.symFlags[e];if(c&10){if(c&2)throw new Sk.builtin.SyntaxError("name '"+e+"' is assigned to before global declaration",this.filename,a.lineno);throw new Sk.builtin.SyntaxError("name '"+
e+"' is used prior to global declaration",this.filename,a.lineno);}this.addDef(new Sk.builtin.str(e),1,a.lineno)}break;case Sk.astnodes.Expr:this.visitExpr(a.value);break;case Sk.astnodes.Pass:case Sk.astnodes.Break:case Sk.astnodes.Continue:case Sk.astnodes.Debugger:break;case Sk.astnodes.With:d(this.visit_withitem.bind(this),a.items);d(this.visitStmt.bind(this),a.body);break;case Sk.astnodes.Try:this.SEQStmt(a.body);this.visitExcepthandlers(a.handlers);this.SEQStmt(a.orelse);this.SEQStmt(a.finalbody);
break;default:Sk.asserts.fail("Unhandled type "+a.constructor.name+" in visitStmt")}};c.prototype.visit_withitem=function(a){this.visitExpr(a.context_expr);a.optional_vars&&this.visitExpr(a.optional_vars)};c.prototype.visitExpr=function(a){Sk.asserts.assert(void 0!==a,"visitExpr called with undefined");switch(a.constructor){case Sk.astnodes.BoolOp:this.SEQExpr(a.values);break;case Sk.astnodes.BinOp:this.visitExpr(a.left);this.visitExpr(a.right);break;case Sk.astnodes.UnaryOp:this.visitExpr(a.operand);
break;case Sk.astnodes.Lambda:this.addDef(new Sk.builtin.str("lambda"),2,a.lineno);a.args.defaults&&this.SEQExpr(a.args.defaults);this.enterBlock("lambda","function",a,a.lineno);this.visitArguments(a.args,a.lineno);this.visitExpr(a.body);this.exitBlock();break;case Sk.astnodes.IfExp:this.visitExpr(a.test);this.visitExpr(a.body);this.visitExpr(a.orelse);break;case Sk.astnodes.Dict:this.SEQExpr(a.keys);this.SEQExpr(a.values);break;case Sk.astnodes.DictComp:case Sk.astnodes.SetComp:this.visitComprehension(a.generators,
0);break;case Sk.astnodes.ListComp:this.newTmpname(a.lineno);this.visitExpr(a.elt);this.visitComprehension(a.generators,0);break;case Sk.astnodes.GeneratorExp:this.visitGenexp(a);break;case Sk.astnodes.Yield:a.value&&this.visitExpr(a.value);this.cur.generator=!0;if(this.cur.returnsValue)throw new Sk.builtin.SyntaxError("'return' with argument inside generator",this.filename);break;case Sk.astnodes.Compare:this.visitExpr(a.left);this.SEQExpr(a.comparators);break;case Sk.astnodes.Call:this.visitExpr(a.func);
if(a.args)for(let b of a.args)b.constructor===Sk.astnodes.Starred?this.visitExpr(b.value):this.visitExpr(b);if(a.keywords)for(let b of a.keywords)this.visitExpr(b.value);break;case Sk.astnodes.Num:case Sk.astnodes.Str:case Sk.astnodes.Bytes:break;case Sk.astnodes.JoinedStr:for(let b of a.values)this.visitExpr(b);break;case Sk.astnodes.FormattedValue:this.visitExpr(a.value);a.format_spec&&this.visitExpr(a.format_spec);break;case Sk.astnodes.Attribute:this.visitExpr(a.value);break;case Sk.astnodes.Subscript:this.visitExpr(a.value);
this.visitSlice(a.slice);break;case Sk.astnodes.Name:this.addDef(a.id,a.ctx===Sk.astnodes.Load?8:2,a.lineno);break;case Sk.astnodes.NameConstant:break;case Sk.astnodes.List:case Sk.astnodes.Tuple:case Sk.astnodes.Set:this.SEQExpr(a.elts);break;case Sk.astnodes.Starred:this.visitExpr(a.value);break;default:Sk.asserts.fail("Unhandled type "+a.constructor.name+" in visitExpr")}};c.prototype.visitComprehension=function(a,b){var c,d=a.length;for(c=b;c<d;++c)b=a[c],this.visitExpr(b.target),this.visitExpr(b.iter),
this.SEQExpr(b.ifs)};c.prototype.visitAlias=function(a,b){var c,d;for(d=0;d<a.length;++d){var f=a[d];var e=c=null===f.asname?f.name.v:f.asname.v;f=c.indexOf(".");-1!==f&&(e=c.substr(0,f));if("*"!==c)this.addDef(new Sk.builtin.str(e),1024,b);else if("module"!==this.cur.blockType)throw new Sk.builtin.SyntaxError("import * only allowed at module level",this.filename);}};c.prototype.visitGenexp=function(a){var b=a.generators[0];this.visitExpr(b.iter);this.enterBlock("genexpr","function",a,a.lineno);this.cur.generator=
!0;this.addDef(new Sk.builtin.str(".0"),4,a.lineno);this.visitExpr(b.target);this.SEQExpr(b.ifs);this.visitComprehension(a.generators,1);this.visitExpr(a.elt);this.exitBlock()};c.prototype.visitExcepthandlers=function(a){var b,c;for(b=0;c=a[b];++b)c.type&&this.visitExpr(c.type),c.name&&this.visitExpr(c.name),this.SEQStmt(c.body)};c.prototype.analyzeBlock=function(a,b,c,d){var f={};var g={},k={},h={},n={};"class"==a.blockType&&(e(k,d),b&&e(h,b));for(p in a.symFlags){var m=a.symFlags[p];this.analyzeName(a,
g,p,m,b,f,c,d)}"class"!==a.blockType&&("function"===a.blockType&&e(h,f),b&&e(h,b),e(k,d));f={};var p=a.children.length;for(m=0;m<p;++m)if(d=a.children[m],this.analyzeChildBlock(d,h,n,k,f),d.hasFree||d.childHasFree)a.childHasFree=!0;e(n,f);"function"===a.blockType&&this.analyzeCells(g,n);b=this.updateSymbols(a.symFlags,g,b,n,"class"===a.blockType);a.hasFree=a.hasFree||b;e(c,n)};c.prototype.analyzeChildBlock=function(a,b,c,d,h){var f={};e(f,b);b={};e(b,c);c={};e(c,d);this.analyzeBlock(a,f,b,c);e(h,
b)};c.prototype.analyzeCells=function(a,b){var c;for(c in a){var d=a[c];1===d&&void 0!==b[c]&&(a[c]=5,delete b[c])}};c.prototype.updateSymbols=function(a,b,c,d,e){var f,g=!1;for(f in a){var k=a[f];var h=b[f];k|=h<<11;a[f]=k}for(f in d)b=a[f],void 0!==b?e&&b&1031&&(b|=512,a[f]=b):void 0!==c[f]&&(a[f]=8192,g=!0);return g};c.prototype.analyzeName=function(a,b,c,d,e,h,m,p){if(d&1){if(d&4)throw new Sk.builtin.SyntaxError("name '"+c+"' is local and global",this.filename,a.lineno);b[c]=2;p[c]=null;e&&void 0!==
e[c]&&delete e[c]}else d&1030?(b[c]=1,h[c]=null,delete p[c]):e&&void 0!==e[c]?(b[c]=4,a.hasFree=!0,m[c]=null):(p&&void 0!==p[c]||!a.isNested||(a.hasFree=!0),b[c]=3)};c.prototype.analyze=function(){this.analyzeBlock(this.top,null,{},{})};Sk.symboltable=function(a,b){var d=new c(b);d.enterBlock("top","module",a,0);d.top=d.cur;for(b=0;b<a.body.length;++b)d.visitStmt(a.body[b]);d.exitBlock();d.analyze();return d};Sk.dumpSymtab=function(a){var b=function(a){return a?"True":"False"},c=function(a){var b,
c=[];for(b=0;b<a.length;++b)c.push((new Sk.builtin.str(a[b])).$r().v);return"["+c.join(", ")+"]"},d=function(a,f){var e,g;void 0===f&&(f="");var k=f+"Sym_type: "+a.get_type()+"\n";k+=f+"Sym_name: "+a.get_name()+"\n";k+=f+"Sym_lineno: "+a.get_lineno()+"\n";k+=f+"Sym_nested: "+b(a.is_nested())+"\n";k+=f+"Sym_haschildren: "+b(a.has_children())+"\n";"class"===a.get_type()?k+=f+"Class_methods: "+c(a.get_methods())+"\n":"function"===a.get_type()&&(k+=f+"Func_params: "+c(a.get_parameters())+"\n",k+=f+"Func_locals: "+
c(a.get_locals())+"\n",k+=f+"Func_globals: "+c(a.get_globals())+"\n",k+=f+"Func_frees: "+c(a.get_frees())+"\n");k+=f+"-- Identifiers --\n";var h=a.get_identifiers();var l=h.length;for(g=0;g<l;++g){var n=a.lookup(h[g]);k+=f+"name: "+n.get_name()+"\n";k+=f+"  is_referenced: "+b(n.is_referenced())+"\n";k+=f+"  is_imported: "+b(n.is_imported())+"\n";k+=f+"  is_parameter: "+b(n.is_parameter())+"\n";k+=f+"  is_global: "+b(n.is_global())+"\n";k+=f+"  is_declared_global: "+b(n.is_declared_global())+"\n";
k+=f+"  is_local: "+b(n.is_local())+"\n";k+=f+"  is_free: "+b(n.is_free())+"\n";k+=f+"  is_assigned: "+b(n.is_assigned())+"\n";k+=f+"  is_namespace: "+b(n.is_namespace())+"\n";var m=n.get_namespaces();var q=m.length;k+=f+"  namespaces: [\n";var p=[];for(e=0;e<q;++e)n=m[e],p.push(d(n,f+"    "));k+=p.join("\n");k+=f+"  ]\n"}return k};return d(a.top,"")};Sk.exportSymbol("Sk.symboltable",Sk.symboltable);Sk.exportSymbol("Sk.dumpSymtab",Sk.dumpSymtab)},function(m,p){function a(a,b,c,d,e){this.filename=
a;this.st=b;this.flags=c;this.canSuspend=d;this.interactive=!1;this.nestlevel=0;this.u=null;this.stack=[];this.result=[];this.allUnits=[];this.source=e?e.split("\n"):!1}function b(){this.name=this.ste=null;this.doesSuspend=this.canSuspend=!1;this.private_=null;this.lineno=this.firstlineno=0;this.linenoSet=!1;this.localnames=[];this.localtemps=[];this.tempsToSave=[];this.blocknum=0;this.blocks=[];this.curblock=0;this.consts={};this.scopename=null;this.suffixCode=this.switchCode=this.varDeclsCode=this.prefixCode=
"";this.breakBlocks=[];this.continueBlocks=[];this.exceptBlocks=[];this.finallyBlocks=[]}function c(a){return void 0===g[a]?a:a+"_$rw$"}function d(a,b){var c=b.v;if(null===a||null===c||"_"!==c.charAt(0)||"_"!==c.charAt(1)||"_"===c.charAt(c.length-1)&&"_"===c.charAt(c.length-2))return b;var d=a.v;d.replace(/_/g,"");if(""===d)return b;d=a.v;d.replace(/^_*/,"");return d=new Sk.builtin.str("_"+d+c)}function e(a){let b='"';for(let c=0;c<a.length;c++){let d=a.charCodeAt(c);b=10==d?b+"\\n":92==d?b+"\\\\":
34==d||32>d||127<=d&&256>d?b+("\\x"+("0"+d.toString(16)).substr(-2)):256<=d?b+("\\u"+("000"+d.toString(16)).substr(-4)):b+a.charAt(c)}return b+'"'}var h;Sk.gensymcount=0;b.prototype.activateScope=function(){var a=this;h=function(){var b,c=a.blocks[a.curblock];if(null===c._next)for(b=0;b<arguments.length;++b)c.push(arguments[b])}};a.prototype.getSourceLine=function(a){Sk.asserts.assert(this.source);return this.source[a-1]};a.prototype.annotateSource=function(a){var b;if(this.source){var c=a.lineno;
var d=a.col_offset;h("\n//\n// line ",c,":\n// ",this.getSourceLine(c),"\n// ");for(b=0;b<d;++b)h(" ");h("^\n//\n");Sk.asserts.assert(void 0!==a.lineno&&void 0!==a.col_offset);h("$currLineNo = ",c,";\n$currColNo = ",d,";\n\n")}};a.prototype.gensym=function(a){return a="$"+(a||"")+Sk.gensymcount++};a.prototype.niceName=function(a){return this.gensym(a.replace("<","").replace(">","").replace(" ","_"))};var g=Sk.builtin.str.reservedWords_;a.prototype.makeConstant=function(a){var b,c="";for(b=0;b<arguments.length;++b)c+=
arguments[b];for(d in this.u.consts)if(this.u.consts.hasOwnProperty(d)&&(b=this.u.consts[d],b==c))return d;var d=this.u.scopename+"."+this.gensym("const");this.u.consts[d]=c;return d};a.prototype._gr=function(a,b){var c,d=this.gensym(a);this.u.localtemps.push(d);h("var ",d,"=");for(c=1;c<arguments.length;++c)h(arguments[c]);h(";");return d};a.prototype.outputInterruptTest=function(){var a="";if(null!==Sk.execLimit||null!==Sk.yieldLimit&&this.u.canSuspend)a+="var $dateNow = Date.now();",null!==Sk.execLimit&&
(a+="if ($dateNow - Sk.execStart > Sk.execLimit) {throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg())}"),null!==Sk.yieldLimit&&this.u.canSuspend&&(a=a+"if ($dateNow - Sk.lastYield > Sk.yieldLimit) {"+("var $susp = $saveSuspension({data: {type: 'Sk.yield'}, resume: function() {}}, '"+this.filename+"',$currLineNo,$currColNo);"),a+="$susp.$blk = $blk;$susp.optional = true;return $susp;}",this.u.doesSuspend=!0);return a};a.prototype._jumpfalse=function(a,b){a=this._gr("jfalse","(",a,"===false||!Sk.misceval.isTrue(",
a,"))");h("if(",a,"){/*test failed */$blk=",b,";continue;}")};a.prototype._jumpundef=function(a,b){h("if(",a,"===undefined){$blk=",b,";continue;}")};a.prototype._jumpnotundef=function(a,b){h("if(",a,"!==undefined){$blk=",b,";continue;}")};a.prototype._jumptrue=function(a,b){a=this._gr("jtrue","(",a,"===true||Sk.misceval.isTrue(",a,"))");h("if(",a,"){/*test passed */$blk=",b,";continue;}")};a.prototype._jump=function(a){null===this.u.blocks[this.u.curblock]._next&&(h("$blk=",a,";"),this.u.blocks[this.u.curblock]._next=
a)};a.prototype._checkSuspension=function(a){if(this.u.canSuspend){var b=this.newBlock("function return or resume suspension");this._jump(b);this.setBlock(b);a=a||{lineno:"$currLineNo",col_offset:"$currColNo"};h("if ($ret && $ret.$isSuspension) { return $saveSuspension($ret,'"+this.filename+"',"+a.lineno+","+a.col_offset+"); }");this.u.doesSuspend=!0;this.u.tempsToSave=this.u.tempsToSave.concat(this.u.localtemps)}else h("if ($ret && $ret.$isSuspension) { $ret = Sk.misceval.retryOptionalSuspensionOrThrow($ret); }")};
a.prototype.cunpackstarstoarray=function(a,b){if(!a||0==a.length)return"[]";let c=!1;for(let d of a){if(b&&c)throw new Sk.builtin.SyntaxError("Extended argument unpacking is not permitted in Python 2");d.constructor===Sk.astnodes.Starred&&(c=!0)}if(c){b=this._gr("unpack","[]");for(let c of a)c.constructor!==Sk.astnodes.Starred?h(b,".push(",this.vexpr(c),");"):(h("$ret = Sk.misceval.iterFor(Sk.abstr.iter(",this.vexpr(c.value),"), function(e) { ",b,".push(e); });"),this._checkSuspension());return b}return"["+
a.map(a=>this.vexpr(a)).join(",")+"]"};a.prototype.ctuplelistorset=function(a,b,c){var d;Sk.asserts.assert("tuple"===c||"list"===c||"set"===c);var f=!1;for(d=0;d<a.elts.length;d++)if(a.elts[d].constructor===Sk.astnodes.Starred){f=!0;var e=d;break}if(a.ctx===Sk.astnodes.Store){if(f){if(!Sk.__future__.python3)throw new Sk.builtin.SyntaxError("assignment unpacking with stars is not supported in Python 2",this.filename,a.lineno);for(d=e+1;d<a.elts.length;d++)if(a.elts[d].constructor===Sk.astnodes.Starred)throw new Sk.builtin.SyntaxError("multiple starred expressions in assignment",
this.filename,a.lineno);}c=f?e:a.elts.length;h("$ret = Sk.abstr.sequenceUnpack("+b+","+c+","+(f?a.elts.length-1:c)+", "+f+");");this._checkSuspension();b=this._gr("items","$ret");for(d=0;d<a.elts.length;++d)d===e?this.vexpr(a.elts[d].value,b+"["+d+"]"):this.vexpr(a.elts[d],b+"["+d+"]")}else if(a.ctx===Sk.astnodes.Load||"set"===c){if(f){if(!Sk.__future__.python3)throw new Sk.builtin.SyntaxError("List packing with stars is not supported in Python 2");return this._gr("load"+c,"new Sk.builtins['",c,"'](",
this.cunpackstarstoarray(a.elts),")")}if("tuple"===c){f=!0;b=[];for(d=0;d<a.elts.length;++d)e=this.vexpr(a.elts[d]),f&&-1==e.indexOf("$const")&&(f=!1),b.push(e);if(f)return this.makeConstant("new Sk.builtin.tuple(["+b+"])");for(d=0;d<b.length;++d)b[d]=this._gr("elem",b[d]);return this._gr("load"+c,"new Sk.builtins['",c,"']([",b,"])")}b=[];for(d=0;d<a.elts.length;++d)b.push(this._gr("elem",this.vexpr(a.elts[d])));return this._gr("load"+c,"new Sk.builtins['",c,"']([",b,"])")}};a.prototype.cdict=function(a){var b;
var c=[];if(null!==a.keys)for(Sk.asserts.assert(a.values.length===a.keys.length),b=0;b<a.values.length;++b){var d=this.vexpr(a.values[b]);c.push(this.vexpr(a.keys[b]));c.push(d)}return this._gr("loaddict","new Sk.builtins['dict']([",c,"])")};a.prototype.clistcomp=function(a){Sk.asserts.assert(a instanceof Sk.astnodes.ListComp);var b=this._gr("_compr","new Sk.builtins['list']([])");return this.ccompgen("list",b,a.generators,0,a.elt,null,a)};a.prototype.cdictcomp=function(a){Sk.asserts.assert(a instanceof
Sk.astnodes.DictComp);var b=this._gr("_dcompr","new Sk.builtins.dict([])");return this.ccompgen("dict",b,a.generators,0,a.value,a.key,a)};a.prototype.csetcomp=function(a){Sk.asserts.assert(a instanceof Sk.astnodes.SetComp);var b=this._gr("_setcompr","new Sk.builtins.set([])");return this.ccompgen("set",b,a.generators,0,a.elt,null,a)};a.prototype.ccompgen=function(a,b,c,d,e,g,m){var f=this.newBlock(a+" comp start"),k=this.newBlock(a+" comp skip"),l=this.newBlock(a+" comp anchor"),n=c[d],q=this.vexpr(n.iter);
q=this._gr("iter","Sk.abstr.iter(",q,")");var p;this._jump(f);this.setBlock(f);h("$ret = Sk.abstr.iternext(",q,", true);");this._checkSuspension(m);q=this._gr("next","$ret");this._jumpundef(q,l);this.vexpr(n.target,q);var u=n.ifs?n.ifs.length:0;for(p=0;p<u;++p)q=this.vexpr(n.ifs[p]),this._jumpfalse(q,f);++d<c.length&&this.ccompgen(a,b,c,d,e,g,m);d>=c.length&&(c=this.vexpr(e),"dict"===a?(a=this.vexpr(g),h(b,".mp$ass_subscript(",a,",",c,");")):"list"===a?h(b,".v.push(",c,");"):"set"===a&&h(b,".v.mp$ass_subscript(",
c,", true);"),this._jump(k),this.setBlock(k));this._jump(f);this.setBlock(l);return b};a.prototype.cyield=function(a){if(this.u.ste.blockType!==Sk.SYMTAB_CONSTS.FunctionBlock)throw new Sk.builtin.SyntaxError("'yield' outside function",this.filename,a.lineno);var b="Sk.builtin.none.none$";a.value&&(b=this.vexpr(a.value));a=this.newBlock("after yield");h("return [/*resume*/",a,",/*ret*/",b,"];");this.setBlock(a);return"$gen.gi$sentvalue"};a.prototype.ccompare=function(a){var b;Sk.asserts.assert(a.ops.length===
a.comparators.length);var c=this.vexpr(a.left);var d=a.ops.length;var f=this.newBlock("done");var e=this._gr("compareres","null");for(b=0;b<d;++b){var g=this.vexpr(a.comparators[b]);h("$ret = Sk.builtin.bool(Sk.misceval.richCompareBool(",c,",",g,",'",a.ops[b].prototype._astname,"', true));");this._checkSuspension(a);h(e,"=$ret;");this._jumpfalse("$ret",f);c=g}this._jump(f);this.setBlock(f);return e};a.prototype.ccall=function(a){var b=this.vexpr(a.func);let c=this.cunpackstarstoarray(a.args,!Sk.__future__.python3);
var d="undefined";if(a.keywords&&0<a.keywords.length){let c=!1;d=[];for(let b of a.keywords){if(c&&!Sk.__future__.python3)throw new SyntaxError("Advanced unpacking of function arguments is not supported in Python 2");b.arg?(d.push("'"+b.arg.v+"'"),d.push(this.vexpr(b.value))):c=!0}d="["+d.join(",")+"]";if(c){d=this._gr("keywordArgs",d);for(let c of a.keywords)c.arg||(h("$ret = Sk.abstr.mappingUnpackIntoKeywordArray(",d,",",this.vexpr(c.value),",",b,");"),this._checkSuspension())}}Sk.__future__.super_args&&
a.func.id&&"super"===a.func.id.v&&"[]"===c&&(h('if (typeof self === "undefined" || self.toString().indexOf("Window") > 0) { throw new Sk.builtin.RuntimeError("super(): no arguments") };'),c="[$gbl.__class__,self]");h("$ret = (",b,".tp$call)?",b,".tp$call(",c,",",d,") : Sk.misceval.applyOrSuspend(",b,",undefined,undefined,",d,",",c,");");this._checkSuspension(a);return this._gr("call","$ret")};a.prototype.cslice=function(a){Sk.asserts.assert(a instanceof Sk.astnodes.Slice);if(Sk.__future__.python3){var b=
a.lower?this.vexpr(a.lower):"Sk.builtin.none.none$";var c=a.upper?this.vexpr(a.upper):"Sk.builtin.none.none$"}else b=a.lower?this.vexpr(a.lower):a.step?"Sk.builtin.none.none$":"new Sk.builtin.int_(0)",c=a.upper?this.vexpr(a.upper):a.step?"Sk.builtin.none.none$":"new Sk.builtin.int_(2147483647)";a=a.step?this.vexpr(a.step):"Sk.builtin.none.none$";return this._gr("slice","new Sk.builtins['slice'](",b,",",c,",",a,")")};a.prototype.eslice=function(a){var b;Sk.asserts.assert(a instanceof Array);var c=
[];for(b=0;b<a.length;b++)c.push(this.vslicesub(a[b]));return this._gr("extslice","new Sk.builtins['tuple']([",c,"])")};a.prototype.vslicesub=function(a){switch(a.constructor){case Sk.astnodes.Index:var b=this.vexpr(a.value);break;case Sk.astnodes.Slice:b=this.cslice(a);break;case Sk.astnodes.Ellipsis:Sk.asserts.fail("todo compile.js Ellipsis;");break;case Sk.astnodes.ExtSlice:b=this.eslice(a.dims);break;default:Sk.asserts.fail("invalid subscript kind")}return b};a.prototype.vslice=function(a,b,c,
d){a=this.vslicesub(a);return this.chandlesubscr(b,c,a,d)};a.prototype.chandlesubscr=function(a,b,c,d){if(a===Sk.astnodes.Load||a===Sk.astnodes.AugLoad)return h("$ret = Sk.abstr.objectGetItem(",b,",",c,", true);"),this._checkSuspension(),this._gr("lsubscr","$ret");a===Sk.astnodes.Store||a===Sk.astnodes.AugStore?(h("$ret = Sk.abstr.objectSetItem(",b,",",c,",",d,", true);"),this._checkSuspension()):a===Sk.astnodes.Del?h("Sk.abstr.objectDelItem(",b,",",c,");"):Sk.asserts.fail("handlesubscr fail")};a.prototype.cboolop=
function(a){var b,c;Sk.asserts.assert(a instanceof Sk.astnodes.BoolOp);var d=a.op===Sk.astnodes.And?this._jumpfalse:this._jumptrue;var f=this.newBlock("end of boolop");var e=a.values;var g=e.length;for(b=0;b<g;++b)a=this.vexpr(e[b]),0===b&&(c=this._gr("boolopsucc",a)),h(c,"=",a,";"),d.call(this,a,f);this._jump(f);this.setBlock(f);return c};a.prototype.cjoinedstr=function(a){let b;Sk.asserts.assert(a instanceof Sk.astnodes.JoinedStr);for(let c of a.values)a=this.vexpr(c),b?h(b,"=",b,".sq$concat(",
a,");"):b=this._gr("joinedstr",a);b||(b="Sk.builtin.str.$emptystr");return b};a.prototype.cformattedvalue=function(a){let b=this.vexpr(a.value);switch(a.conversion){case "s":b=this._gr("value","new Sk.builtin.str(",b,")");break;case "a":b=this._gr("value","Sk.builtin.ascii(",b,")");break;case "r":b=this._gr("value","Sk.builtin.repr(",b,")")}a=a.format_spec?this.vexpr(a.format_spec):"Sk.builtin.str.$emptystr";return this._gr("formatted","Sk.abstr.objectFormat("+b+","+a+")")};a.prototype.vexpr=function(a,
b,c,g){var f;a.lineno>this.u.lineno&&(this.u.lineno=a.lineno,this.u.linenoSet=!1);switch(a.constructor){case Sk.astnodes.BoolOp:return this.cboolop(a);case Sk.astnodes.BinOp:return this._gr("binop","Sk.abstr.numberBinOp(",this.vexpr(a.left),",",this.vexpr(a.right),",'",a.op.prototype._astname,"')");case Sk.astnodes.UnaryOp:return this._gr("unaryop","Sk.abstr.numberUnaryOp(",this.vexpr(a.operand),",'",a.op.prototype._astname,"')");case Sk.astnodes.Lambda:return this.clambda(a);case Sk.astnodes.IfExp:return this.cifexp(a);
case Sk.astnodes.Dict:return this.cdict(a);case Sk.astnodes.ListComp:return this.clistcomp(a);case Sk.astnodes.DictComp:return this.cdictcomp(a);case Sk.astnodes.SetComp:return this.csetcomp(a);case Sk.astnodes.GeneratorExp:return this.cgenexp(a);case Sk.astnodes.Yield:return this.cyield(a);case Sk.astnodes.Compare:return this.ccompare(a);case Sk.astnodes.Call:return b=this.ccall(a),this.annotateSource(a),b;case Sk.astnodes.Num:if("number"===typeof a.n)return a.n;if(a.n instanceof Sk.builtin.int_)return this.makeConstant("new Sk.builtin.int_("+
a.n.v+")");if(a.n instanceof Sk.builtin.float_)return a=0===a.n.v&&-Infinity===1/a.n.v?"-0":a.n.v,this.makeConstant("new Sk.builtin.float_("+a+")");if(a.n instanceof Sk.builtin.lng)return this.makeConstant("Sk.longFromStr('"+a.n.tp$str().v+"')");if(a.n instanceof Sk.builtin.complex)return this.makeConstant("new Sk.builtin.complex("+(0===a.n.real&&-Infinity===1/a.n.real?"-0":a.n.real)+", "+(0===a.n.imag&&-Infinity===1/a.n.imag?"-0":a.n.imag)+")");Sk.asserts.fail("unhandled Num type");case Sk.astnodes.Bytes:if(Sk.__future__.python3){b=
[];a=a.s.$jsstr();for(c=0;c<a.length;c++)b.push(a.charCodeAt(c));return this.makeConstant("new Sk.builtin.bytes([",b.join(", "),"])")}case Sk.astnodes.Str:return this.makeConstant("new Sk.builtin.str(",e(a.s.$jsstr()),")");case Sk.astnodes.Attribute:a.ctx!==Sk.astnodes.AugLoad&&a.ctx!==Sk.astnodes.AugStore&&(f=this.vexpr(a.value));g=a.attr.$r().v;g=g.substring(1,g.length-1);g=d(this.u.private_,new Sk.builtin.str(g)).v;g=this.makeConstant("new Sk.builtin.str('"+g+"')");switch(a.ctx){case Sk.astnodes.AugLoad:return h("$ret = Sk.abstr.gattr(",
c,",",g,", true);"),this._checkSuspension(a),this._gr("lattr","$ret");case Sk.astnodes.Load:return h("$ret = Sk.abstr.gattr(",f,",",g,", true);"),this._checkSuspension(a),this._gr("lattr","$ret");case Sk.astnodes.AugStore:h("$ret = undefined;");h("if(",b,"!==undefined){");h("$ret = Sk.abstr.sattr(",c,",",g,",",b,", true);");h("}");this._checkSuspension(a);break;case Sk.astnodes.Store:h("$ret = Sk.abstr.sattr(",f,",",g,",",b,", true);");this._checkSuspension(a);break;case Sk.astnodes.Del:Sk.asserts.fail("todo Del;");
break;default:Sk.asserts.fail("invalid attribute expression")}break;case Sk.astnodes.Subscript:switch(a.ctx){case Sk.astnodes.AugLoad:return h("$ret = Sk.abstr.objectGetItem(",c,",",g,", true);"),this._checkSuspension(a),this._gr("gitem","$ret");case Sk.astnodes.Load:case Sk.astnodes.Store:case Sk.astnodes.Del:return this.vslice(a.slice,a.ctx,this.vexpr(a.value),b);case Sk.astnodes.AugStore:h("$ret=undefined;");h("if(",b,"!==undefined){");h("$ret=Sk.abstr.objectSetItem(",c,",",g,",",b,", true)");
h("}");this._checkSuspension(a);break;default:Sk.asserts.fail("invalid subscript expression")}break;case Sk.astnodes.Name:return this.nameop(a.id,a.ctx,b);case Sk.astnodes.NameConstant:if(a.ctx===Sk.astnodes.Store||a.ctx===Sk.astnodes.AugStore||a.ctx===Sk.astnodes.Del)throw new Sk.builtin.SyntaxError("can not assign to a constant name");switch(a.value){case Sk.builtin.none.none$:return"Sk.builtin.none.none$";case Sk.builtin.bool.true$:return"Sk.builtin.bool.true$";case Sk.builtin.bool.false$:return"Sk.builtin.bool.false$";
default:Sk.asserts.fail("invalid named constant")}break;case Sk.astnodes.List:return this.ctuplelistorset(a,b,"list");case Sk.astnodes.Tuple:return this.ctuplelistorset(a,b,"tuple");case Sk.astnodes.Set:return this.ctuplelistorset(a,b,"set");case Sk.astnodes.Starred:switch(a.ctx){case Sk.astnodes.Store:throw new Sk.builtin.SyntaxError("starred assignment target must be in a list or tuple",this.filename,a.lineno);default:throw new Sk.builtin.SyntaxError("can't use starred expression here",this.filename,
a.lineno);}case Sk.astnodes.JoinedStr:return this.cjoinedstr(a);case Sk.astnodes.FormattedValue:return this.cformattedvalue(a);default:Sk.asserts.fail("unhandled case "+a.constructor.name+" vexpr")}};a.prototype.vseqexpr=function(a,b){var c;Sk.asserts.assert(void 0===b||a.length===b.length);var d=[];for(c=0;c<a.length;++c)d.push(this.vexpr(a[c],void 0===b?void 0:b[c]));return d};a.prototype.caugassign=function(a){Sk.asserts.assert(a instanceof Sk.astnodes.AugAssign);var b=a.target;switch(b.constructor){case Sk.astnodes.Attribute:var c=
this.vexpr(b.value);b=new Sk.astnodes.Attribute(b.value,b.attr,Sk.astnodes.AugLoad,b.lineno,b.col_offset);var d=this.vexpr(b,void 0,c);var f=this.vexpr(a.value);a=this._gr("inplbinopattr","Sk.abstr.numberInplaceBinOp(",d,",",f,",'",a.op.prototype._astname,"')");b.ctx=Sk.astnodes.AugStore;return this.vexpr(b,a,c);case Sk.astnodes.Subscript:c=this.vexpr(b.value);var e=this.vslicesub(b.slice);b=new Sk.astnodes.Subscript(b.value,e,Sk.astnodes.AugLoad,b.lineno,b.col_offset);d=this.vexpr(b,void 0,c,e);
f=this.vexpr(a.value);a=this._gr("inplbinopsubscr","Sk.abstr.numberInplaceBinOp(",d,",",f,",'",a.op.prototype._astname,"')");b.ctx=Sk.astnodes.AugStore;return this.vexpr(b,a,c,e);case Sk.astnodes.Name:return c=this.nameop(b.id,Sk.astnodes.Load),f=this.vexpr(a.value),a=this._gr("inplbinop","Sk.abstr.numberInplaceBinOp(",c,",",f,",'",a.op.prototype._astname,"')"),this.nameop(b.id,Sk.astnodes.Store,a);default:Sk.asserts.fail("unhandled case in augassign")}};a.prototype.exprConstant=function(a){switch(a.constructor){case Sk.astnodes.Num:return Sk.misceval.isTrue(a.n)?
1:0;case Sk.astnodes.Str:return Sk.misceval.isTrue(a.s)?1:0;default:return-1}};a.prototype.newBlock=function(a){var b=this.u.blocknum++;this.u.blocks[b]=[];this.u.blocks[b]._name=a||"<unnamed>";this.u.blocks[b]._next=null;return b};a.prototype.setBlock=function(a){Sk.asserts.assert(0<=a&&a<this.u.blocknum);this.u.curblock=a};a.prototype.pushBreakBlock=function(a){Sk.asserts.assert(0<=a&&a<this.u.blocknum);this.u.breakBlocks.push(a)};a.prototype.popBreakBlock=function(){this.u.breakBlocks.pop()};a.prototype.pushContinueBlock=
function(a){Sk.asserts.assert(0<=a&&a<this.u.blocknum);this.u.continueBlocks.push(a)};a.prototype.popContinueBlock=function(){this.u.continueBlocks.pop()};a.prototype.pushExceptBlock=function(a){Sk.asserts.assert(0<=a&&a<this.u.blocknum);this.u.exceptBlocks.push(a)};a.prototype.popExceptBlock=function(){this.u.exceptBlocks.pop()};a.prototype.pushFinallyBlock=function(a){Sk.asserts.assert(0<=a&&a<this.u.blocknum);Sk.asserts.assert(this.u.breakBlocks.length===this.u.continueBlocks.length);this.u.finallyBlocks.push({blk:a,
breakDepth:this.u.breakBlocks.length})};a.prototype.popFinallyBlock=function(){this.u.finallyBlocks.pop()};a.prototype.peekFinallyBlock=function(){return 0<this.u.finallyBlocks.length?this.u.finallyBlocks[this.u.finallyBlocks.length-1]:void 0};a.prototype.setupExcept=function(a){h("$exc.push(",a,");")};a.prototype.endExcept=function(){h("$exc.pop();")};a.prototype.outputLocals=function(a){var b,c={};for(b=0;a.argnames&&b<a.argnames.length;++b)c[a.argnames[b]]=!0;a.localnames.sort();var d=[];for(b=
0;b<a.localnames.length;++b){var f=a.localnames[b];void 0===c[f]&&(d.push(f),c[f]=!0)}return 0<d.length?"var "+d.join(",")+"; /* locals */":""};a.prototype.outputSuspensionHelpers=function(a){var b,c=[],d=a.localnames.concat(a.tempsToSave),f={},e=a.ste.blockType===Sk.SYMTAB_CONSTS.FunctionBlock&&a.ste.childHasFree,g=(0<d.length?"var "+d.join(",")+";":"")+"var $wakeFromSuspension = function() {var susp = "+a.scopename+".$wakingSuspension; "+a.scopename+".$wakingSuspension = undefined;$blk=susp.$blk; $loc=susp.$loc; $gbl=susp.$gbl; $exc=susp.$exc; $err=susp.$err; $postfinally=susp.$postfinally;$currLineNo=susp.$lineno; $currColNo=susp.$colno; Sk.lastYield=Date.now();"+
(e?"$cell=susp.$cell;":"");for(b=0;b<d.length;b++){var h=d[b];void 0===f[h]&&(g+=h+"=susp.$tmps."+h+";",f[h]=!0)}g+="try { $ret=susp.child.resume(); } catch(err) { if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if($exc.length>0) { $err=err; $blk=$exc.pop(); } else { throw err; } }};";g+="var $saveSuspension = function($child, $filename, $lineno, $colno) {var susp = new Sk.misceval.Suspension(); susp.child=$child;susp.resume=function(){"+
a.scopename+".$wakingSuspension=susp; return "+a.scopename+"("+(a.ste.generator?"$gen":"")+"); };susp.data=susp.child.data;susp.$blk=$blk;susp.$loc=$loc;susp.$gbl=$gbl;susp.$exc=$exc;susp.$err=$err;susp.$postfinally=$postfinally;susp.$filename=$filename;susp.$lineno=$lineno;susp.$colno=$colno;susp.optional=susp.child.optional;"+(e?"susp.$cell=$cell;":"");f={};for(b=0;b<d.length;b++)h=d[b],void 0===f[h]&&(c.push('"'+h+'":'+h),f[h]=!0);return g+="susp.$tmps={"+c.join(",")+"};return susp;};"};a.prototype.outputAllUnits=
function(){var a,b,c="";for(b=0;b<this.allUnits.length;++b){var d=this.allUnits[b];c+=d.prefixCode;c+=this.outputLocals(d);d.doesSuspend&&(c+=this.outputSuspensionHelpers(d));c+=d.varDeclsCode;c+=d.switchCode;var e=d.blocks;var g=Object.create(null);for(a=0;a<e.length;++a){var h=a;if(!(h in g))for(;;)if(g[h]=!0,c+="case "+h+": /* --- "+e[h]._name+" --- */",c+=e[h].join(""),null!==e[h]._next)if(e[h]._next in g){c+="/* jump */ continue;";break}else c+="/* allowing case fallthrough */",h=e[h]._next;
else{c+="throw new Sk.builtin.SystemError('internal error: unterminated block');";break}}c+=d.suffixCode}return c};a.prototype.cif=function(a){var b;Sk.asserts.assert(a instanceof Sk.astnodes.If);var c=this.exprConstant(a.test);if(0===c)a.orelse&&0<a.orelse.length&&this.vseqstmt(a.orelse);else if(1===c)this.vseqstmt(a.body);else{var d=this.newBlock("end of if");a.orelse&&0<a.orelse.length&&(b=this.newBlock("next branch of if"));c=this.vexpr(a.test);a.orelse&&0<a.orelse.length?(this._jumpfalse(c,b),
this.vseqstmt(a.body),this._jump(d),this.setBlock(b),this.vseqstmt(a.orelse)):(this._jumpfalse(c,d),this.vseqstmt(a.body));this._jump(d);this.setBlock(d)}};a.prototype.cwhile=function(a){if(0===this.exprConstant(a.test))a.orelse&&this.vseqstmt(a.orelse);else{var b=this.newBlock("while test");this._jump(b);this.setBlock(b);var c=this.newBlock("after while");var d=0<a.orelse.length?this.newBlock("while orelse"):null;var f=this.newBlock("while body");this.annotateSource(a);this._jumpfalse(this.vexpr(a.test),
d?d:c);this._jump(f);this.pushBreakBlock(c);this.pushContinueBlock(b);this.setBlock(f);(Sk.debugging||Sk.killableWhile)&&this.u.canSuspend&&(f=this.newBlock("debug breakpoint for line "+a.lineno),h("if (Sk.breakpoints('"+this.filename+"',"+a.lineno+","+a.col_offset+")) {","var $susp = $saveSuspension({data: {type: 'Sk.delay'}, resume: function() {}}, '"+this.filename+"',"+a.lineno+","+a.col_offset+");","$susp.$blk = "+f+";","$susp.optional = true;","return $susp;","}"),this._jump(f),this.setBlock(f),
this.u.doesSuspend=!0);this.vseqstmt(a.body);this._jump(b);this.popContinueBlock();this.popBreakBlock();0<a.orelse.length&&(this.setBlock(d),this.vseqstmt(a.orelse),this._jump(c));this.setBlock(c)}};a.prototype.cfor=function(a){var b=this.newBlock("for start"),c=this.newBlock("for cleanup"),d=this.newBlock("for end");this.pushBreakBlock(d);this.pushContinueBlock(b);var f=this.vexpr(a.iter);if(this.u.ste.generator){var e="$loc."+this.gensym("iter");h(e,"=Sk.abstr.iter(",f,");")}else e=this._gr("iter",
"Sk.abstr.iter(",f,")"),this.u.tempsToSave.push(e);this._jump(b);this.setBlock(b);h("$ret = Sk.abstr.iternext(",e,this.u.canSuspend?", true":", false",");");this._checkSuspension(a);e=this._gr("next","$ret");this._jumpundef(e,c);this.vexpr(a.target,e);(Sk.debugging||Sk.killableFor)&&this.u.canSuspend&&(e=this.newBlock("debug breakpoint for line "+a.lineno),h("if (Sk.breakpoints('"+this.filename+"',"+a.lineno+","+a.col_offset+")) {","var $susp = $saveSuspension({data: {type: 'Sk.delay'}, resume: function() {}}, '"+
this.filename+"',"+a.lineno+","+a.col_offset+");","$susp.$blk = "+e+";","$susp.optional = true;","return $susp;","}"),this._jump(e),this.setBlock(e),this.u.doesSuspend=!0);this.vseqstmt(a.body);this._jump(b);this.setBlock(c);this.popContinueBlock();this.popBreakBlock();this.vseqstmt(a.orelse);this._jump(d);this.setBlock(d)};a.prototype.craise=function(a){if(a.exc){var b=this._gr("exc",this.vexpr(a.exc)),c=this.newBlock("exception now instantiated"),d=this._gr("isclass",b+".prototype instanceof Sk.builtin.BaseException");
this._jumpfalse(d,c);a.inst?(d=this._gr("inst",this.vexpr(a.inst)),h("if(!(",d," instanceof Sk.builtin.tuple)) {",d,"= new Sk.builtin.tuple([",d,"]);","}"),h("$ret = Sk.misceval.callsimOrSuspendArray(",b,",",d,".v);")):h("$ret = Sk.misceval.callsimOrSuspend(",b,");");this._checkSuspension(a);h(b,"=$ret;");this._jump(c);this.setBlock(c);h("if (",b," instanceof Sk.builtin.BaseException) {throw ",b,";} else {throw new Sk.builtin.TypeError('exceptions must derive from BaseException');};")}else h("throw $err;")};
a.prototype.outputFinallyCascade=function(a){if(0==this.u.finallyBlocks.length)h("if($postfinally!==undefined) { if ($postfinally.returning) { return $postfinally.returning; } else { $blk=$postfinally.gotoBlock; $postfinally=undefined; continue; } }");else{var b=this.peekFinallyBlock();h("if($postfinally!==undefined) {","if ($postfinally.returning",b.breakDepth==a.breakDepth?"|| $postfinally.isBreak":"",") {","$blk=",b.blk,";continue;","} else {","$blk=$postfinally.gotoBlock;$postfinally=undefined;continue;",
"}","}")}};a.prototype.ctry=function(a){var b,c=a.handlers.length;if(a.finalbody){var d=this.newBlock("finalbody");var f=this.newBlock("finalexh");var e=this._gr("finally_reraise","undefined");this.u.tempsToSave.push(e);this.pushFinallyBlock(d);var g=this.peekFinallyBlock();this.setupExcept(f)}var m=[];for(b=0;b<c;++b)m.push(this.newBlock("except_"+b+"_"));var p=this.newBlock("unhandled");var H=this.newBlock("orelse");var K=this.newBlock("end");0!=m.length&&this.setupExcept(m[0]);this.vseqstmt(a.body);
0!=m.length&&this.endExcept();this._jump(H);for(b=0;b<c;++b){this.setBlock(m[b]);var C=a.handlers[b];if(!C.type&&b<c-1)throw new Sk.builtin.SyntaxError("default 'except:' must be last",this.filename,C.lineno);if(C.type){var F=this.vexpr(C.type);var L=b==c-1?p:m[b+1];F=this._gr("instance","Sk.misceval.isTrue(Sk.builtin.isinstance($err, ",F,"))");this._jumpfalse(F,L)}C.name&&this.vexpr(C.name,"$err");this.vseqstmt(C.body);this._jump(K)}this.setBlock(p);h("throw $err;");this.setBlock(H);this.vseqstmt(a.orelse);
this._jump(K);this.setBlock(K);a.finalbody&&(this.endExcept(),this._jump(d),this.setBlock(f),h(e,"=$err;"),this._jump(d),this.setBlock(d),this.popFinallyBlock(),this.vseqstmt(a.finalbody),h("if(",e,"!==undefined) { throw ",e,";}"),this.outputFinallyCascade(g))};a.prototype.cwith=function(a,b){var c=this.newBlock("withexh"),d=this.newBlock("withtidyup"),f=this.newBlock("withcarryon");var e=this._gr("mgr",this.vexpr(a.items[b].context_expr));h("$ret = Sk.abstr.gattr(",e,",Sk.builtin.str.$exit, true);");
this._checkSuspension(a);var g=this._gr("exit","$ret");this.u.tempsToSave.push(g);h("$ret = Sk.abstr.gattr(",e,",Sk.builtin.str.$enter, true);");this._checkSuspension(a);h("$ret = Sk.misceval.callsimOrSuspendArray($ret);");this._checkSuspension(a);e=this._gr("value","$ret");this.pushFinallyBlock(d);var k=this.u.finallyBlocks[this.u.finallyBlocks.length-1];this.setupExcept(c);a.items[b].optional_vars&&this.nameop(a.items[b].optional_vars.id,Sk.astnodes.Store,e);b+1<a.items.length?this.cwith(a,b+1):
this.vseqstmt(a.body);this.endExcept();this._jump(d);this.setBlock(c);h("$ret = Sk.misceval.applyOrSuspend(",g,",undefined,Sk.builtin.getExcInfo($err),undefined,[]);");this._checkSuspension(a);this._jumptrue("$ret",f);h("throw $err;");this.setBlock(d);this.popFinallyBlock();h("$ret = Sk.misceval.callsimOrSuspendArray(",g,",[Sk.builtin.none.none$,Sk.builtin.none.none$,Sk.builtin.none.none$]);");this._checkSuspension(a);this.outputFinallyCascade(k);this._jump(f);this.setBlock(f)};a.prototype.cassert=
function(a){var b=this.vexpr(a.test),c=this.newBlock("end");this._jumptrue(b,c);h("throw new Sk.builtin.AssertionError(",a.msg?this.vexpr(a.msg):"",");");this.setBlock(c)};a.prototype.cimportas=function(a,b,c){a=a.v;var d=a.indexOf("."),e=c;if(-1!==d)for(a=a.substr(d+1);-1!==d;)d=a.indexOf("."),c=-1!==d?a.substr(0,d):a,e=this._gr("lattr","Sk.abstr.gattr(",e,", new Sk.builtin.str('",c,"'))"),a=a.substr(d+1);return this.nameop(b,Sk.astnodes.Store,e)};a.prototype.cimport=function(a){var b,c=a.names.length;
for(b=0;b<c;++b){var d=a.names[b];h("$ret = Sk.builtin.__import__(",d.name.$r().v,",$gbl,$loc,[],",Sk.__future__.absolute_import?0:-1,");");this._checkSuspension(a);var e=this._gr("module","$ret");if(d.asname)this.cimportas(d.name,d.asname,e);else{var f=d.name;d=f.v.indexOf(".");-1!==d&&(f=new Sk.builtin.str(f.v.substr(0,d)));this.nameop(f,Sk.astnodes.Store,e)}}};a.prototype.cfromimport=function(a){var b,d=a.names.length;var e=[];var f=a.level;0!=f||Sk.__future__.absolute_import||(f=-1);for(b=0;b<
d;++b)e[b]="'"+c(a.names[b].name.v)+"'";h("$ret = Sk.builtin.__import__(",a.module.$r().v,",$gbl,$loc,[",e,"],",f,");");this._checkSuspension(a);f=this._gr("module","$ret");for(b=0;b<d;++b){e=a.names[b];var g="'"+e.name.v+"'";if(0===b&&"*"===e.name.v){Sk.asserts.assert(1===d);h("Sk.importStar(",f,",$loc, $gbl);");break}var m=this._gr("item","Sk.abstr.gattr(",f,", new Sk.builtin.str(",g,"))");g=e.name;e.asname&&(g=e.asname);this.nameop(g,Sk.astnodes.Store,m)}};a.prototype.buildcodeobj=function(a,b,
e,g,m,p){var f=[],k=[],l=[],n=[],q=null,u=null;e&&(k=this.vseqexpr(e));g&&g.defaults&&(l=this.vseqexpr(g.defaults));g&&g.kw_defaults&&(n=g.kw_defaults.map(a=>a?this.vexpr(a):"undefined"));g&&g.vararg&&(q=g.vararg);g&&g.kwarg&&(u=g.kwarg);if(!Sk.__future__.python3&&g&&g.kwonlyargs&&0!=g.kwonlyargs.length)throw new Sk.builtin.SyntaxError("Keyword-only arguments are not supported in Python 2");var F=this.enterScope(b,a,a.lineno,this.canSuspend);e=this.u.ste.generator;var L=this.u.ste.hasFree;var W=this.u.ste.childHasFree;
var N=this.newBlock("codeobj entry");this.u.prefixCode="var "+F+"=(function "+this.niceName(b.v)+"$(";var E=[];if(e){if(u)throw new Sk.builtin.SyntaxError(b.v+"(): keyword arguments in generators not supported",this.filename,a.lineno);if(q)throw new Sk.builtin.SyntaxError(b.v+"(): variable number of arguments in generators not supported",this.filename,a.lineno);E.push("$gen")}else{u&&(E.push("$kwa"),this.u.tempsToSave.push("$kwa"));for(a=0;g&&a<g.args.length;++a)E.push(this.nameop(g.args[a].arg,Sk.astnodes.Param));
for(a=0;g&&g.kwonlyargs&&a<g.kwonlyargs.length;++a)E.push(this.nameop(g.kwonlyargs[a].arg,Sk.astnodes.Param));q&&E.push(this.nameop(g.vararg.arg,Sk.astnodes.Param))}let X=!e;L&&(X||E.push("$free"),this.u.tempsToSave.push("$free"));this.u.prefixCode=X?this.u.prefixCode+"$posargs,$kwargs":this.u.prefixCode+E.join(",");this.u.prefixCode+="){";e&&(this.u.prefixCode+="\n// generator\n");L&&(this.u.prefixCode+="\n// has free\n");W&&(this.u.prefixCode+="\n// has cell\n");X&&(this.u.prefixCode+="\n// fast call\n");
var aa="{}";e&&(N="$gen.gi$resumeat",aa="$gen.gi$locals");a=",$cell={}";W&&e&&(a=",$cell=$gen.gi$cells");this.u.varDeclsCode+="var $blk="+N+",$exc=[],$loc="+aa+a+",$gbl="+(X?"this.func_globals":"this")+(X&&L?",$free=this.func_closure":"")+",$err=undefined,$ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;";null!==Sk.execLimit&&(this.u.varDeclsCode+="if (typeof Sk.execStart === 'undefined') {Sk.execStart = Date.now()}");null!==Sk.yieldLimit&&this.u.canSuspend&&(this.u.varDeclsCode+=
"if (typeof Sk.lastYield === 'undefined') {Sk.lastYield = Date.now()}");this.u.varDeclsCode+="if ("+F+".$wakingSuspension!==undefined) { $wakeFromSuspension(); } else {";if(X){this.u.varDeclsCode=u||q||g&&g.kwonlyargs&&0!==g.kwonlyargs.length?this.u.varDeclsCode+"\nvar $args = this.$resolveArgs($posargs,$kwargs)\n":this.u.varDeclsCode+("var $args = ((!$kwargs || $kwargs.length===0) && $posargs.length==="+E.length+") ? $posargs : this.$resolveArgs($posargs,$kwargs)");for(a=0;a<E.length;a++)this.u.varDeclsCode+=
","+E[a]+"=$args["+a+"]";this.u.varDeclsCode+=";\n"}if(e&&0<l.length)for(N=g.args.length-l.length,a=0;a<l.length;++a)E=this.nameop(g.args[a+N].arg,Sk.astnodes.Param),this.u.varDeclsCode+="if("+E+"===undefined)"+E+"="+F+".$defaults["+a+"];";for(a=0;g&&a<g.args.length;++a)E=g.args[a].arg,this.isCell(E)&&(E=c(d(this.u.private_,E).v),this.u.varDeclsCode+="$cell."+E+"="+E+";");for(a=0;g&&g.kwonlyargs&&a<g.kwonlyargs.length;++a)E=g.kwonlyargs[a].arg,this.isCell(E)&&(E=c(d(this.u.private_,E).v),this.u.varDeclsCode+=
"$cell."+E+"="+E+";");q&&this.isCell(q.arg)&&(a=c(d(this.u.private_,q.arg).v),this.u.varDeclsCode+="$cell."+a+"="+a+";");u&&(this.u.localnames.push(u.arg.v),this.u.varDeclsCode+=u.arg.v+"=new Sk.builtins['dict']($kwa);",this.isCell(u.arg)&&(a=c(d(this.u.private_,u.arg).v),this.u.varDeclsCode+="$cell."+a+"="+a+";"));this.u.varDeclsCode+="}";Sk.__future__.python3&&p&&(this.u.varDeclsCode+="$gbl.__class__=$gbl."+p.v+";");this.u.switchCode="while(true){try{";this.u.switchCode+=this.outputInterruptTest();
this.u.switchCode+="switch($blk){";this.u.suffixCode="} }catch(err){ if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }} }});";m.call(this,F);if(g){for(let a of g.args)f.push(a.arg.v);for(let a of g.kwonlyargs||[])f.push(a.arg.v);this.u.argnames=f}this.exitScope();0<l.length&&h(F,".$defaults=[",
l.join(","),"];");g&&g.kwonlyargs&&0<g.kwonlyargs.length&&(h(F,".co_argcount=",g.args.length,";"),h(F,".co_kwonlyargcount=",g.kwonlyargs.length,";"),h(F,".$kwdefs=[",n.join(","),"];"));0<f.length?h(F,".co_varnames=['",f.join("','"),"'];"):h(F,".co_varnames=[];");u&&h(F,".co_kwargs=1;");q&&h(F,".co_varargs=1;");e||h(F,".co_fastcall=1;");m="";L&&(m=",$cell",(p=this.u.ste.hasFree)&&(m+=",$free"));if(e)return g&&0<g.args.length?this._gr("gener","new Sk.builtins['function']((function(){var $origargs=Array.prototype.slice.call(arguments);Sk.builtin.pyCheckArgsLen(\"",
b.v,'",arguments.length,',g.args.length-l.length,",",g.args.length,");return new Sk.builtins['generator'](",F,",$gbl,$origargs",m,");}))"):this._gr("gener","new Sk.builtins['function']((function(){Sk.builtin.pyCheckArgsLen(\"",b.v,"\",arguments.length,0,0);return new Sk.builtins['generator'](",F,",$gbl,[]",m,");}))");if(0<k.length){h("$ret = new Sk.builtins['function'](",F,",$gbl",m,");");for(let a of k.reverse())h("$ret = Sk.misceval.callsimOrSuspendArray(",a,",[$ret]);"),this._checkSuspension();
return this._gr("funcobj","$ret")}return this._gr("funcobj","new Sk.builtins['function'](",F,",$gbl",m,")")};a.prototype.cfunction=function(a,b){Sk.asserts.assert(a instanceof Sk.astnodes.FunctionDef);b=this.buildcodeobj(a,a.name,a.decorator_list,a.args,function(b){this.vseqstmt(a.body);h("return Sk.builtin.none.none$;")},b);this.nameop(a.name,Sk.astnodes.Store,b)};a.prototype.clambda=function(a){Sk.asserts.assert(a instanceof Sk.astnodes.Lambda);return this.buildcodeobj(a,new Sk.builtin.str("<lambda>"),
null,a.args,function(b){b=this.vexpr(a.body);h("return ",b,";")})};a.prototype.cifexp=function(a){var b=this.newBlock("next of ifexp"),c=this.newBlock("end of ifexp"),d=this._gr("res","null"),e=this.vexpr(a.test);this._jumpfalse(e,b);h(d,"=",this.vexpr(a.body),";");this._jump(c);this.setBlock(b);h(d,"=",this.vexpr(a.orelse),";");this._jump(c);this.setBlock(c);return d};a.prototype.cgenexpgen=function(a,b,c){var d=this.newBlock("start for "+b),e=this.newBlock("skip for "+b);this.newBlock("if cleanup for "+
b);var f=this.newBlock("end for "+b),g=a[b];if(0===b)var k="$loc.$iter0";else{var n=this.vexpr(g.iter);k="$loc."+this.gensym("iter");h(k,"=","Sk.abstr.iter(",n,");")}this._jump(d);this.setBlock(d);this.annotateSource(c);h("$ret = Sk.abstr.iternext(",k,this.u.canSuspend?", true":", false",");");this._checkSuspension(c);n=this._gr("next","$ret");this._jumpundef(n,f);this.vexpr(g.target,n);var m=g.ifs?g.ifs.length:0;for(k=0;k<m;++k)this.annotateSource(g.ifs[k]),n=this.vexpr(g.ifs[k]),this._jumpfalse(n,
d);++b<a.length&&this.cgenexpgen(a,b,c);b>=a.length&&(this.annotateSource(c),a=this.vexpr(c),h("return [",e,"/*resume*/,",a,"/*ret*/];"),this.setBlock(e));this._jump(d);this.setBlock(f);1===b&&h("return Sk.builtin.none.none$;")};a.prototype.cgenexp=function(a){var b=this.buildcodeobj(a,new Sk.builtin.str("<genexpr>"),null,null,function(b){this.cgenexpgen(a.generators,0,a.elt)});b=this._gr("gener","Sk.misceval.callsimArray(",b,");");h(b,".gi$locals.$iter0=Sk.abstr.iter(",this.vexpr(a.generators[0].iter),
");");return b};a.prototype.cclass=function(a){Sk.asserts.assert(a instanceof Sk.astnodes.ClassDef);var b=this.vseqexpr(a.decorator_list);var c=this.vseqexpr(a.bases);var d=this.enterScope(a.name,a,a.lineno);var e=this.newBlock("class entry");this.u.prefixCode="var "+d+"=(function $"+a.name.v+"$class_outer($globals,$locals,$cell){var $gbl=$globals,$loc=$locals;$free=$globals;";this.u.switchCode+="(function $"+a.name.v+"$_closure($cell){";this.u.switchCode+="var $blk="+e+",$exc=[],$ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;";
null!==Sk.execLimit&&(this.u.switchCode+="if (typeof Sk.execStart === 'undefined') {Sk.execStart = Date.now()}");null!==Sk.yieldLimit&&this.u.canSuspend&&(this.u.switchCode+="if (typeof Sk.lastYield === 'undefined') {Sk.lastYield = Date.now()}");this.u.switchCode+="while(true){try{";this.u.switchCode+=this.outputInterruptTest();this.u.switchCode+="switch($blk){";this.u.suffixCode="}}catch(err){ if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+
this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }}}";this.u.suffixCode+="}).call(null, $cell);});";this.u.private_=a.name;this.cbody(a.body,a.name);h("return;");this.exitScope();h("$ret = Sk.misceval.buildClass($gbl,",d,",",a.name.$r().v,",[",c,"], $cell);");for(let a of b)h("$ret = Sk.misceval.callsimOrSuspendArray(",a,", [$ret]);"),this._checkSuspension();this.nameop(a.name,Sk.astnodes.Store,"$ret")};a.prototype.ccontinue=function(a){var b=this.peekFinallyBlock();
if(0==this.u.continueBlocks.length)throw new Sk.builtin.SyntaxError("'continue' outside loop",this.filename,a.lineno);a=this.u.continueBlocks[this.u.continueBlocks.length-1];Sk.asserts.assert(this.u.breakBlocks.length===this.u.continueBlocks.length);b&&b.breakDepth==this.u.continueBlocks.length?h("$postfinally={isBreak:true,gotoBlock:",a,"};"):this._jump(a)};a.prototype.cbreak=function(a){var b=this.peekFinallyBlock();if(0===this.u.breakBlocks.length)throw new Sk.builtin.SyntaxError("'break' outside loop",
this.filename,a.lineno);a=this.u.breakBlocks[this.u.breakBlocks.length-1];b&&b.breakDepth==this.u.breakBlocks.length?h("$postfinally={isBreak:true,gotoBlock:",a,"};"):this._jump(a)};a.prototype.vstmt=function(a,b){this.u.lineno=a.lineno;this.u.linenoSet=!1;this.u.localtemps=[];if(Sk.debugging&&this.u.canSuspend){var c=this.newBlock("debug breakpoint for line "+a.lineno);h("if (Sk.breakpoints('"+this.filename+"',"+a.lineno+","+a.col_offset+")) {","var $susp = $saveSuspension({data: {type: 'Sk.debug'}, resume: function() {}}, '"+
this.filename+"',"+a.lineno+","+a.col_offset+");","$susp.$blk = "+c+";","$susp.optional = true;","return $susp;","}");this._jump(c);this.setBlock(c);this.u.doesSuspend=!0}this.annotateSource(a);switch(a.constructor){case Sk.astnodes.FunctionDef:this.cfunction(a,b);break;case Sk.astnodes.ClassDef:this.cclass(a);break;case Sk.astnodes.Return:if(this.u.ste.blockType!==Sk.SYMTAB_CONSTS.FunctionBlock)throw new Sk.builtin.SyntaxError("'return' outside function",this.filename,a.lineno);c=a.value?this.vexpr(a.value):
"Sk.builtin.none.none$";0==this.u.finallyBlocks.length?h("return ",c,";"):(h("$postfinally={returning:",c,"};"),this._jump(this.peekFinallyBlock().blk));break;case Sk.astnodes.Delete:this.vseqexpr(a.targets);break;case Sk.astnodes.Assign:var d=a.targets.length;c=this.vexpr(a.value);for(b=0;b<d;++b)this.vexpr(a.targets[b],c);break;case Sk.astnodes.AnnAssign:c=this.vexpr(a.value);this.vexpr(a.target,c);this.vexpr(a.annotation);break;case Sk.astnodes.AugAssign:return this.caugassign(a);case Sk.astnodes.Print:this.cprint(a);
break;case Sk.astnodes.For:return this.cfor(a);case Sk.astnodes.While:return this.cwhile(a);case Sk.astnodes.If:return this.cif(a);case Sk.astnodes.Raise:return this.craise(a);case Sk.astnodes.Try:return this.ctry(a);case Sk.astnodes.With:return this.cwith(a,0);case Sk.astnodes.Assert:return this.cassert(a);case Sk.astnodes.Import:return this.cimport(a);case Sk.astnodes.ImportFrom:return this.cfromimport(a);case Sk.astnodes.Global:break;case Sk.astnodes.Expr:this.vexpr(a.value);break;case Sk.astnodes.Pass:break;
case Sk.astnodes.Break:this.cbreak(a);break;case Sk.astnodes.Continue:this.ccontinue(a);break;case Sk.astnodes.Debugger:h("debugger;");break;default:Sk.asserts.fail("unhandled case in vstmt: "+JSON.stringify(a))}};a.prototype.vseqstmt=function(a){var b;for(b=0;b<a.length;++b)this.vstmt(a[b])};a.prototype.isCell=function(a){a=c(d(this.u.private_,a).v);return this.u.ste.getScope(a)===Sk.SYMTAB_CONSTS.CELL};a.prototype.nameop=function(a,b,e){if((b===Sk.astnodes.Store||b===Sk.astnodes.AugStore||b===Sk.astnodes.Del)&&
"__debug__"===a.v)throw new Sk.builtin.SyntaxError("can not assign to __debug__",this.filename,this.u.lineno);Sk.asserts.assert("None"!==a.v);if("NotImplemented"===a.v)return"Sk.builtin.NotImplemented.NotImplemented$";var f=d(this.u.private_,a).v;f=c(f);var g=3;var k=this.u.ste.getScope(f);var n=null;switch(k){case Sk.SYMTAB_CONSTS.FREE:n="$free";g=2;break;case Sk.SYMTAB_CONSTS.CELL:n="$cell";g=2;break;case Sk.SYMTAB_CONSTS.LOCAL:this.u.ste.blockType!==Sk.SYMTAB_CONSTS.FunctionBlock||this.u.ste.generator||
(g=0);break;case Sk.SYMTAB_CONSTS.GLOBAL_IMPLICIT:this.u.ste.blockType===Sk.SYMTAB_CONSTS.FunctionBlock&&(g=1);break;case Sk.SYMTAB_CONSTS.GLOBAL_EXPLICIT:g=1}Sk.asserts.assert(k||"_"===a.v.charAt(1));a=f;this.u.ste.generator||this.u.ste.blockType!==Sk.SYMTAB_CONSTS.FunctionBlock?f="$loc."+f:(0===g||3===g)&&this.u.localnames.push(f);switch(g){case 0:switch(b){case Sk.astnodes.Load:case Sk.astnodes.Param:return h("if (",f," === undefined) { throw new Sk.builtin.UnboundLocalError('local variable \\'",
f,"\\' referenced before assignment'); }\n"),f;case Sk.astnodes.Store:h(f,"=",e,";");break;case Sk.astnodes.Del:h("delete ",f,";");break;default:Sk.asserts.fail("unhandled")}break;case 3:switch(b){case Sk.astnodes.Load:return this._gr("loadname",f,"!==undefined?",f,":Sk.misceval.loadname('",a,"',$gbl);");case Sk.astnodes.Store:h(f,"=",e,";");break;case Sk.astnodes.Del:h("delete ",f,";");break;case Sk.astnodes.Param:return f;default:Sk.asserts.fail("unhandled")}break;case 1:switch(b){case Sk.astnodes.Load:return this._gr("loadgbl",
"Sk.misceval.loadname('",a,"',$gbl)");case Sk.astnodes.Store:h("$gbl.",a,"=",e,";");break;case Sk.astnodes.Del:h("delete $gbl.",a);break;default:Sk.asserts.fail("unhandled case in name op_global")}break;case 2:switch(b){case Sk.astnodes.Load:return n+"."+a;case Sk.astnodes.Store:h(n,".",a,"=",e,";");break;case Sk.astnodes.Param:return a;default:Sk.asserts.fail("unhandled case in name op_deref")}break;default:Sk.asserts.fail("unhandled case")}};a.prototype.enterScope=function(a,c,d,e){var f=new b;
f.ste=this.st.getStsForAst(c);f.name=a;f.firstlineno=d;f.canSuspend=e||!1;this.u&&this.u.private_&&(f.private_=this.u.private_);this.stack.push(this.u);this.allUnits.push(f);a=this.gensym("scope");f.scopename=a;this.u=f;this.u.activateScope();this.nestlevel++;return a};a.prototype.exitScope=function(){var a=this.u;this.nestlevel--;(this.u=0<=this.stack.length-1?this.stack.pop():null)&&this.u.activateScope();if("<module>"!==a.name.v){var b=a.name.$r().v;b=b.substring(1,b.length-1);h(a.scopename,".co_name=new Sk.builtins['str']('",
b,"');")}for(var c in a.consts)a.consts.hasOwnProperty(c)&&(a.suffixCode+=c+" = "+a.consts[c]+";")};a.prototype.cbody=function(a,b){var c;for(c=0;c<a.length;++c)this.vstmt(a[c],b)};a.prototype.cprint=function(a){var b;Sk.asserts.assert(a instanceof Sk.astnodes.Print);a.dest&&this.vexpr(a.dest);var c=a.values.length;for(b=0;b<c;++b)h("$ret = Sk.misceval.print_(","new Sk.builtins['str'](",this.vexpr(a.values[b]),").v);"),this._checkSuspension(a);a.nl&&(h("$ret = Sk.misceval.print_(",'"\\n");'),this._checkSuspension(a))};
a.prototype.cmod=function(a){var b=this.enterScope(new Sk.builtin.str("<module>"),a,0,this.canSuspend),c=this.newBlock("module entry");this.u.prefixCode="var "+b+"=(function($forcegbl){";this.u.varDeclsCode="var $gbl = $forcegbl || {}, $blk="+c+",$exc=[],$loc=$gbl,$cell={},$err=undefined;$loc.__file__=new Sk.builtins.str('"+this.filename+"');var $ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;";null!==Sk.execLimit&&(this.u.varDeclsCode+="if (typeof Sk.execStart === 'undefined') {Sk.execStart = Date.now()}");
null!==Sk.yieldLimit&&this.u.canSuspend&&(this.u.varDeclsCode+="if (typeof Sk.lastYield === 'undefined') {Sk.lastYield = Date.now()}");this.u.varDeclsCode+="if ("+b+".$wakingSuspension!==undefined) { $wakeFromSuspension(); }if (Sk.retainGlobals) {    if (Sk.globals) { $gbl = Sk.globals; Sk.globals = $gbl; $loc = $gbl; }    if (Sk.globals) { $gbl = Sk.globals; Sk.globals = $gbl; $loc = $gbl; $loc.__file__=new Sk.builtins.str('"+this.filename+"');}    else { Sk.globals = $gbl; }} else { Sk.globals = $gbl; }";
this.u.switchCode="while(true){try{";this.u.switchCode+=this.outputInterruptTest();this.u.switchCode+="switch($blk){";this.u.suffixCode="}";this.u.suffixCode+="}catch(err){ if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }} } });";switch(a.constructor){case Sk.astnodes.Module:this.cbody(a.body);
h("return $loc;");break;default:Sk.asserts.fail("todo; unhandled case in compilerMod")}this.exitScope();this.result.push(this.outputAllUnits());return b};Sk.compile=function(b,c,d,e){d=Sk.__future__;Sk.__future__=Object.create(Sk.__future__);var f=Sk.parse(c,b),g=Sk.astFromParse(f.cst,c,f.flags);f=f.flags;var h=Sk.symboltable(g,c);b=new a(c,h,f,e,b);c=b.cmod(g);Sk.__future__=d;return{funcname:"$compiledmod",code:"$compiledmod = function() {"+b.result.join("")+"\nreturn "+c+";}();"}};Sk.exportSymbol("Sk.compile",
Sk.compile);Sk.resetCompiler=function(){Sk.gensymcount=0};Sk.exportSymbol("Sk.resetCompiler",Sk.resetCompiler);Sk.fixReserved=c;Sk.exportSymbol("Sk.fixReserved",Sk.fixReserved);Sk.unfixReserved=function(a){return a.replace(/_\$rw\$$/,"")};Sk.exportSymbol("Sk.unfixReserved",Sk.unfixReserved);Sk.mangleName=d;Sk.exportSymbol("Sk.mangleName",Sk.mangleName)},function(m,p){Sk.sysmodules=new Sk.builtin.dict([]);Sk.realsyspath=void 0;Sk.importSearchPathForName=function(a,b,c){var d=a.replace(/\./g,"/"),e=
function(a,b){return Sk.misceval.chain(Sk.misceval.tryCatch(function(){return Sk.read(a)},function(a){}),function(c){if(void 0!==c)return new Sk.misceval.Break({filename:a,code:c,packagePath:b})})};void 0===c&&(c=Sk.realsyspath);return Sk.misceval.iterFor(c.tp$iter(),function(a){return Sk.misceval.chain(e(a.v+"/"+d+b,!1),function(c){return c?c:e(a.v+"/"+d+"/__init__"+b,a.v+"/"+d)})})};Sk.doOneTimeInitialization=function(a){Sk.builtin.type.basesStr_=new Sk.builtin.str("__bases__");Sk.builtin.type.mroStr_=
new Sk.builtin.str("__mro__");for(var b in Sk.builtin)if(a=Sk.builtin[b],a instanceof Sk.builtin.type&&void 0===a.sk$abstract){for(var c=a.prototype.tp$base,d=[];void 0!==c;c=c.prototype.tp$base)!c.sk$abstract&&Sk.builtins[c.prototype.tp$name]&&d.push(c);a.tp$mro=new Sk.builtin.tuple([a].concat(d));a.hasOwnProperty("tp$base")||(a.tp$base=d[0]);a.$d=new Sk.builtin.dict([]);a.$d.mp$ass_subscript(Sk.builtin.type.basesStr_,a.tp$base?new Sk.builtin.tuple([a.tp$base]):new Sk.builtin.tuple([]));a.$d.mp$ass_subscript(Sk.builtin.type.mroStr_,
a.tp$mro);a.$d.mp$ass_subscript(new Sk.builtin.str("__name__"),new Sk.builtin.str(a.prototype.tp$name))}b=[Sk.builtin.object,Sk.builtin.type,Sk.builtin.func,Sk.builtin.method];for(a=0;a<b.length;a++){d=b[a];c=d.prototype;for(let a=0;a<d.pythonFunctions.length;a++){const b=d.pythonFunctions[a];if(c[b]instanceof Sk.builtin.func)break;c[b].co_kwargs=null;c[b]=new Sk.builtin.func(c[b])}}for(var e in Sk.internalPy.files)b=e.split(".")[0].split("/")[1],a=Sk.importBuiltinWithBody(b,!1,Sk.internalPy.files[e],
!0),a=Sk.misceval.retryOptionalSuspensionOrThrow(a),Sk.asserts.assert(void 0!==a.$d[b],"Should have imported name "+b),Sk.builtins[b]=a.$d[b],delete Sk.builtins[b].__module__,delete Sk.globals[b]};Sk.importSetUpPath=function(a){var b;if(!Sk.realsyspath){var c=[new Sk.builtin.str("src/builtin"),new Sk.builtin.str("src/lib"),new Sk.builtin.str(".")];for(b=0;b<Sk.syspath.length;++b)c.push(new Sk.builtin.str(Sk.syspath[b]));Sk.realsyspath=new Sk.builtin.list(c);Sk.doOneTimeInitialization(a)}};Sk.importModuleInternal_=
function(a,b,c,d,e,h,g){var f,k,m,l,p=null,u=void 0!==e?e.tp$getattr(Sk.builtin.str.$name):void 0,z=void 0!==u?u.v+".":"",y=void 0!==e?e.tp$getattr(Sk.builtin.str.$path):void 0;Sk.importSetUpPath(g);if(e&&!u){if(h)return;throw new Sk.builtin.ValueError("Attempted to import relative to invalid package (no name)");}void 0===c&&(c=z+a);var t=a.split(".");if(1<t.length){var H=t.slice(0,t.length-1).join(".");p=Sk.importModuleInternal_(H,b,void 0,void 0,e,h,g)}var K=Sk.misceval.chain(p,function(n){p=n;
k=Sk.sysmodules.mp$lookup(c);return void 0!==k?p||k:Sk.misceval.chain(void 0,function(){var b=a;if(1<t.length){if(!p)return;m=Sk.sysmodules.mp$subscript(z+H);b=t[t.length-1];y=m.tp$getattr(Sk.builtin.str.$path)}l=new Sk.builtin.module;if("string"===typeof d){f=a+".py";var c=Sk.compile(d,f,"exec",g)}else c=Sk.misceval.chain(void 0,function(){if(Sk.onBeforeImport&&"function"===typeof Sk.onBeforeImport)return Sk.onBeforeImport(a)},function(c){if(!1===c)throw new Sk.builtin.ImportError("Importing "+a+
" is not allowed");if("string"===typeof c)throw new Sk.builtin.ImportError(c);return Sk.importSearchPathForName(b,".js",y)},function(a){return a?{funcname:"$builtinmodule",code:a.code,filename:a.filename,packagePath:a.packagePath}:Sk.misceval.chain(Sk.importSearchPathForName(b,".py",y),function(b){if(a=b)return Sk.compile(a.code,a.filename,"exec",g)},function(b){if(b)return b.packagePath=a.packagePath,b})});return c},function(a){if(a){Sk.sysmodules.mp$ass_subscript(c,l);var d=l.$js=a.code;null==f&&
(f=a.filename);null!=Sk.dateSet&&Sk.dateSet||(d="Sk.execStart = Sk.lastYield = new Date();\n"+a.code,Sk.dateSet=!0);if(b){var e=function(a){var b,c=Sk.js_beautify(a).split("\n");for(b=1;b<=c.length;++b){var d=(""+b).length;for(a="";5>d;++d)a+=" ";c[b-1]="/* "+a+b+" */ "+c[b-1]}return c.join("\n")};d=e(d);Sk.debugout(d)}d+="\n"+a.funcname+";";d=Sk.global.eval(d);l.$d={__name__:new Sk.builtin.str(c),__doc__:Sk.builtin.none.none$,__package__:a.packagePath?new Sk.builtin.str(c):H?new Sk.builtin.str(z+
H):u?u:Sk.builtin.none.none$};a.packagePath&&(l.$d.__path__=new Sk.builtin.tuple([new Sk.builtin.str(a.packagePath)]));return d(l.$d)}},function(b){var c;if(void 0===b){if(h&&!p)return;throw new Sk.builtin.ImportError("No module named "+a);}if(b!==l.$d){for(c in l.$d)b[c]||(b[c]=l.$d[c]);l.$d=b}if(Sk.onAfterImport&&"function"===typeof Sk.onAfterImport)try{Sk.onAfterImport(a)}catch(W){}if(p)return m.tp$setattr(new Sk.builtin.str(t[t.length-1]),l),p;e&&e.tp$setattr(new Sk.builtin.str(a),l);return l})});
return g?K:Sk.misceval.retryOptionalSuspensionOrThrow(K)};Sk.importModule=function(a,b,c){return Sk.importModuleInternal_(a,b,void 0,void 0,void 0,!1,c)};Sk.importMain=function(a,b,c){Sk.dateSet=!1;Sk.filesLoaded=!1;Sk.sysmodules=new Sk.builtin.dict([]);Sk.realsyspath=void 0;Sk.resetCompiler();return Sk.importModuleInternal_(a,b,"__main__",void 0,void 0,!1,c)};Sk.importMainWithBody=function(a,b,c,d){Sk.dateSet=!1;Sk.filesLoaded=!1;Sk.sysmodules=new Sk.builtin.dict([]);Sk.realsyspath=void 0;Sk.resetCompiler();
return Sk.importModuleInternal_(a,b,"__main__",c,void 0,!1,d)};Sk.importBuiltinWithBody=function(a,b,c,d){return Sk.importModuleInternal_(a,b,"__builtin__."+a,c,void 0,!1,d)};Sk.builtin.__import__=function(a,b,c,d,e){var h=Sk.globals,g;void 0===e&&(e=Sk.__future__.absolute_import?0:-1);if(0!==e&&b.__package__&&b.__package__!==Sk.builtin.none.none$){if((g=b.__package__.v)&&0<e){b=g.split(".");if(e-1>=b.length)throw new Sk.builtin.ValueError("Attempted relative import beyond toplevel package");b.length-=
e-1;g=b.join(".")}var f=Sk.sysmodules.mp$lookup(g)}if(0<e&&void 0===f)throw new Sk.builtin.ValueError("Attempted relative import in non-package");a.split(".");return Sk.misceval.chain(void 0,function(){if(0!==e&&void 0!==f)return""===a?f:Sk.importModuleInternal_(a,void 0,g+"."+a,void 0,f,-1==e,!0)},function(b){return void 0===b?(g=f=void 0,Sk.importModuleInternal_(a,void 0,void 0,void 0,void 0,!1,!0)):b},function(b){if(d&&0!==d.length){var c;var e=Sk.sysmodules.mp$subscript((g||"")+(g&&a?".":"")+
a);for(b=0;b<d.length;b++){var f=d[b];"*"!=f&&void 0===e.tp$getattr(new Sk.builtin.str(f))&&(c=Sk.misceval.chain(c,Sk.importModuleInternal_.bind(null,f,void 0,void 0,void 0,e,!0,!0)))}return Sk.misceval.chain(c,function(){Sk.asserts.assert(e);return e})}return b},function(a){h!==Sk.globals&&(Sk.globals=h);return a})};Sk.importStar=function(a,b,c){if(c=a.tp$getattr(new Sk.builtin.str("__all__")))for(let d=Sk.abstr.iter(c),e=d.tp$iternext();void 0!==e;e=d.tp$iternext())b[e.v]=Sk.abstr.gattr(a,e);else{c=
Object.getOwnPropertyNames(a.$d);for(let d in c)"_"!=c[d].charAt(0)&&(b[c[d]]=a.$d[c[d]])}};Sk.exportSymbol("Sk.importMain",Sk.importMain);Sk.exportSymbol("Sk.importMainWithBody",Sk.importMainWithBody);Sk.exportSymbol("Sk.importBuiltinWithBody",Sk.importBuiltinWithBody);Sk.exportSymbol("Sk.builtin.__import__",Sk.builtin.__import__);Sk.exportSymbol("Sk.importStar",Sk.importStar)},function(m,p){Sk.builtin.timSort=function(a,b){this.list=new Sk.builtin.list(a.v);this.MIN_GALLOP=7;this.listlength=b?b:
a.sq$length()};Sk.builtin.timSort.prototype.lt=function(a,b){return Sk.misceval.richCompareBool(a,b,"Lt")};Sk.builtin.timSort.prototype.le=function(a,b){return!this.lt(b,a)};Sk.builtin.timSort.prototype.setitem=function(a,b){this.list.v[a]=b};Sk.builtin.timSort.prototype.binary_sort=function(a,b){var c;for(c=a.base+b;c<a.base+a.len;c++){var d=a.base;var e=c;for(b=a.getitem(e);d<e;){var h=d+(e-d>>1);this.lt(b,a.getitem(h))?e=h:d=h+1}Sk.asserts.assert(d===e);for(h=c;h>d;h--)a.setitem(h,a.getitem(h-
1));a.setitem(d,b)}};Sk.builtin.timSort.prototype.count_run=function(a){var b;if(1>=a.len){var c=a.len;var d=!1}else if(c=2,this.lt(a.getitem(a.base+1),a.getitem(a.base)))for(d=!0,b=a.base+2;b<a.base+a.len;b++)if(this.lt(a.getitem(b),a.getitem(b-1)))c++;else break;else for(d=!1,b=a.base+2;b<a.base+a.len&&!this.lt(a.getitem(b),a.getitem(b-1));b++)c++;return{run:new Sk.builtin.listSlice(a.list,a.base,c),descending:d}};Sk.builtin.timSort.prototype.sort=function(){var a,b=new Sk.builtin.listSlice(this.list,
0,this.listlength);if(!(2>b.len)){this.merge_init();for(a=this.merge_compute_minrun(b.len);0<b.len;){var c=this.count_run(b);c.descending&&c.run.reverse();if(c.run.len<a){var d=c.run.len;c.run.len=a<b.len?a:b.len;this.binary_sort(c.run,d)}b.advance(c.run.len);this.pending.push(c.run);this.merge_collapse()}Sk.asserts.assert(b.base==this.listlength);this.merge_force_collapse();Sk.asserts.assert(1==this.pending.length);Sk.asserts.assert(0===this.pending[0].base);Sk.asserts.assert(this.pending[0].len==
this.listlength)}};Sk.builtin.timSort.prototype.gallop=function(a,b,c,d){var e;Sk.asserts.assert(0<=c&&c<b.len);var h=this;d=d?function(a,b){return h.le(a,b)}:function(a,b){return h.lt(a,b)};var g=b.base+c;var f=0;var k=1;if(d(b.getitem(g),a)){for(e=b.len-c;k<e;)if(d(b.getitem(g+k),a)){f=k;try{k=(k<<1)+1}catch(n){k=e}}else break;k>e&&(k=e);f+=c;k+=c}else{for(e=c+1;k<e&&!d(b.getitem(g-k),a);){f=k;try{k=(k<<1)+1}catch(n){k=e}}k>e&&(k=e);g=c-f;f=c-k;k=g}Sk.asserts.assert(-1<=f<k<=b.len);for(f+=1;f<k;)c=
f+(k-f>>1),d(b.getitem(b.base+c),a)?f=c+1:k=c;Sk.asserts.assert(f==k);return k};Sk.builtin.timSort.prototype.merge_init=function(){this.min_gallop=this.MIN_GALLOP;this.pending=[]};Sk.builtin.timSort.prototype.merge_lo=function(a,b){var c,d,e;Sk.asserts.assert(0<a.len&&0<b.len&&a.base+a.len==b.base);var h=this.min_gallop;var g=a.base;a=a.copyitems();try{if(this.setitem(g,b.popleft()),g++,1!=a.len&&0!==b.len)for(;;){for(d=c=0;;)if(this.lt(b.getitem(b.base),a.getitem(a.base))){this.setitem(g,b.popleft());
g++;if(0===b.len)return;d++;c=0;if(d>=h)break}else{this.setitem(g,a.popleft());g++;if(1==a.len)return;c++;d=0;if(c>=h)break}for(h+=1;;){this.min_gallop=h-=1<h;c=this.gallop(b.getitem(b.base),a,0,!0);for(e=a.base;e<a.base+c;e++)this.setitem(g,a.getitem(e)),g++;a.advance(c);if(1>=a.len)return;this.setitem(g,b.popleft());g++;if(0===b.len)return;d=this.gallop(a.getitem(a.base),b,0,!1);for(e=b.base;e<b.base+d;e++)this.setitem(g,b.getitem(e)),g++;b.advance(d);if(0===b.len)return;this.setitem(g,a.popleft());
g++;if(1==a.len)return;if(c<this.MIN_GALLOP&&d<this.MIN_GALLOP)break;h++;this.min_gallop=h}}}finally{Sk.asserts.assert(0<=a.len&&0<=b.len);for(e=b.base;e<b.base+b.len;e++)this.setitem(g,b.getitem(e)),g++;for(e=a.base;e<a.base+a.len;e++)this.setitem(g,a.getitem(e)),g++}};Sk.builtin.timSort.prototype.merge_hi=function(a,b){var c,d,e;Sk.asserts.assert(0<a.len&&0<b.len&&a.base+a.len==b.base);var h=this.min_gallop;var g=b.base+b.len;b=b.copyitems();try{if(g--,this.setitem(g,a.popright()),0!==a.len&&1!=
b.len)for(;;){for(d=c=0;;){var f=a.getitem(a.base+a.len-1);var k=b.getitem(b.base+b.len-1);if(this.lt(k,f)){g--;this.setitem(g,f);a.len--;if(0===a.len)return;c++;d=0;if(c>=h)break}else{g--;this.setitem(g,k);b.len--;if(1==b.len)return;d++;c=0;if(d>=h)break}}for(h+=1;;){this.min_gallop=h-=1<h;k=b.getitem(b.base+b.len-1);var m=this.gallop(k,a,a.len-1,!0);c=a.len-m;for(e=a.base+a.len-1;e>a.base+m-1;e--)g--,this.setitem(g,a.getitem(e));a.len-=c;if(0===a.len)return;g--;this.setitem(g,b.popright());if(1==
b.len)return;f=a.getitem(a.base+a.len-1);m=this.gallop(f,b,b.len-1,!1);d=b.len-m;for(e=b.base+b.len-1;e>b.base+m-1;e--)g--,this.setitem(g,b.getitem(e));b.len-=d;if(1>=b.len)return;g--;this.setitem(g,a.popright());if(0===a.len)return;if(c<this.MIN_GALLOP&&d<this.MIN_GALLOP)break;h++;this.min_gallop=h}}}finally{Sk.asserts.assert(0<=a.len&&0<=b.len);for(e=a.base+a.len-1;e>a.base-1;e--)g--,this.setitem(g,a.getitem(e));for(e=b.base+b.len-1;e>b.base-1;e--)g--,this.setitem(g,b.getitem(e))}};Sk.builtin.timSort.prototype.merge_at=
function(a){0>a&&(a=this.pending.length+a);var b=this.pending[a];var c=this.pending[a+1];Sk.asserts.assert(0<b.len&&0<c.len);Sk.asserts.assert(b.base+b.len==c.base);this.pending[a]=new Sk.builtin.listSlice(this.list,b.base,b.len+c.len);this.pending.splice(a+1,1);a=this.gallop(c.getitem(c.base),b,0,!0);b.advance(a);0!==b.len&&(c.len=this.gallop(b.getitem(b.base+b.len-1),c,c.len-1,!1),0!==c.len&&(b.len<=c.len?this.merge_lo(b,c):this.merge_hi(b,c)))};Sk.builtin.timSort.prototype.merge_collapse=function(){for(var a=
this.pending;1<a.length;)if(3<=a.length&&a[a.length-3].len<=a[a.length-2].len+a[a.length-1].len)a[a.length-3].len<a[a.length-1].len?this.merge_at(-3):this.merge_at(-2);else if(a[a.length-2].len<=a[a.length-1].len)this.merge_at(-2);else break};Sk.builtin.timSort.prototype.merge_force_collapse=function(){for(var a=this.pending;1<a.length;)3<=a.length&&a[a.length-3].len<a[a.length-1].len?this.merge_at(-3):this.merge_at(-2)};Sk.builtin.timSort.prototype.merge_compute_minrun=function(a){for(var b=0;64<=
a;)b|=a&1,a>>=1;return a+b};Sk.builtin.listSlice=function(a,b,c){this.list=a;this.base=b;this.len=c};Sk.builtin.listSlice.prototype.copyitems=function(){var a=this.base,b=this.base+this.len;Sk.asserts.assert(0<=a<=b);return new Sk.builtin.listSlice(new Sk.builtin.list(this.list.v.slice(a,b)),0,this.len)};Sk.builtin.listSlice.prototype.advance=function(a){this.base+=a;this.len-=a;Sk.asserts.assert(this.base<=this.list.sq$length())};Sk.builtin.listSlice.prototype.getitem=function(a){return this.list.v[a]};
Sk.builtin.listSlice.prototype.setitem=function(a,b){this.list.v[a]=b};Sk.builtin.listSlice.prototype.popleft=function(){var a=this.list.v[this.base];this.base++;this.len--;return a};Sk.builtin.listSlice.prototype.popright=function(){this.len--;return this.list.v[this.base+this.len]};Sk.builtin.listSlice.prototype.reverse=function(){for(var a,b,c=this.list,d=this.base,e=d+this.len-1;d<e;)a=c.v[e],b=c.v[d],c.v[d]=a,c.v[e]=b,d++,e--};Sk.exportSymbol("Sk.builtin.listSlice",Sk.builtin.listSlice);Sk.exportSymbol("Sk.builtin.timSort",
Sk.builtin.timSort)},function(m,p){Sk.builtin.sorted=function(a,b,c,d){if(void 0===d)d=!1;else{if(d instanceof Sk.builtin.float_)throw new Sk.builtin.TypeError("an integer is required, got float");if(d instanceof Sk.builtin.int_||d.prototype instanceof Sk.builtin.int_)d=Sk.misceval.isTrue(d);else throw new Sk.builtin.TypeError("an integer is required");}if(void 0!==c&&c!==Sk.builtin.none.none$){var e=b===Sk.builtin.none.none$||void 0===b?function(a,b){return Sk.misceval.richCompareBool(a[0],b[0],
"Lt")?new Sk.builtin.int_(-1):new Sk.builtin.int_(0)}:function(a,c){return Sk.misceval.callsimArray(b,[a[0],c[0]])};var h=a.tp$iter();var g=h.tp$iternext();for(a=[];void 0!==g;)a.push([Sk.misceval.callsimArray(c,[g]),g]),g=h.tp$iternext();a=new Sk.builtin.list(a)}else b!==Sk.builtin.none.none$&&void 0!==b&&(e=b),a=new Sk.builtin.list(a);void 0!==e?a.list_sort_(a,e):a.list_sort_(a);d&&a.list_reverse_(a);if(void 0!==c&&c!==Sk.builtin.none.none$){h=a.tp$iter();g=h.tp$iternext();for(a=[];void 0!==g;)a.push(g[1]),
g=h.tp$iternext();a=new Sk.builtin.list(a)}return a}},function(m,p){Sk.builtin.type_is_subtype_base_chain=function(a,b){do{if(a==b)return!0;a=a.tp$base}while(void 0!==a);return b==Sk.builtin.object};Sk.builtin.PyType_IsSubtype=function(a,b){var c=a.tp$mro;if(c){Sk.asserts.assert(c instanceof Sk.builtin.tuple);for(a=0;a<c.v.length;a++)if(c.v[a]==b)return!0;return!1}return Sk.builtin.type_is_subtype_base_chain(a,b)};Sk.builtin.super_=function(a,b){Sk.builtin.pyCheckArgsLen("super",arguments.length,
1);if(!(this instanceof Sk.builtin.super_))return new Sk.builtin.super_(a,b);Sk.misceval.callsimArray(Sk.builtin.super_.__init__,[this,a,b]);return this};Sk.builtin.super_.__init__=new Sk.builtin.func(function(a,b,c){a.obj=c;a.type=b;if(!b.tp$mro)throw new Sk.builtin.TypeError("must be type, not "+Sk.abstr.typeName(b));a.obj_type=b.tp$mro.v[1];if(!c)throw new Sk.builtin.NotImplementedError("unbound super not supported because skulpts implementation of type descriptors aren't brilliant yet, see this question for more information https://stackoverflow.com/a/30190341/117242");
if(!Sk.builtin.PyType_IsSubtype(a.obj.ob$type,a.type))throw new Sk.builtin.TypeError("super(type, obj): obj must be an instance of subtype of type");return Sk.builtin.none.none$});Sk.abstr.setUpInheritance("super",Sk.builtin.super_,Sk.builtin.object);Sk.builtin.super_.prototype.tp$getattr=function(a,b){var c,d,e=a.$jsstr();var h=this.obj_type;Sk.asserts.assert(void 0!==h,"object has no ob$type!");if(d=this.obj.$d||this.obj.constructor.$d)if(d.mp$lookup?c=d.mp$lookup(a):d.mp$subscript?c=Sk.builtin._tryGetSubscript(d,
a):"object"===typeof d&&(c=d[e]),void 0!==c)return c;c=Sk.builtin.type.typeLookup(h,a);if(void 0!==c&&null!==c&&(a=c.tp$descr_get))return a.call(c,this.obj,this.obj_type,b);if(void 0!==c)return c};Sk.builtin.super_.prototype.$r=function(a){return this.obj?new Sk.builtin.str("<super: <class '"+(this.type?this.type.prototype.tp$name:"NULL")+"'>, <"+Sk.abstr.typeName(this.obj)+" object>>"):new Sk.builtin.str("<super: <class '"+(this.type?this.type.prototype.tp$name:"NULL")+"'>, NULL>")};Sk.builtin.super_.__doc__=
new Sk.builtin.str("super(type, obj) -> bound super object; requires isinstance(obj, type)\nsuper(type) -> unbound super object\nsuper(type, type2) -> bound super object; requires issubclass(type2, type)\nTypical use to call a cooperative superclass method:\nclass C(B):\n    def meth(self, arg):\n        super(C, self).meth(arg)")},function(m,p){Sk.builtins={range:new Sk.builtin.func(Sk.builtin.range),round:new Sk.builtin.func(Sk.builtin.round),len:new Sk.builtin.func(Sk.builtin.len),min:new Sk.builtin.func(Sk.builtin.min),
max:new Sk.builtin.func(Sk.builtin.max),sum:new Sk.builtin.func(Sk.builtin.sum),abs:new Sk.builtin.func(Sk.builtin.abs),fabs:new Sk.builtin.func(Sk.builtin.fabs),ord:new Sk.builtin.func(Sk.builtin.ord),chr:new Sk.builtin.func(Sk.builtin.chr),hex:new Sk.builtin.func(Sk.builtin.hex),oct:new Sk.builtin.func(Sk.builtin.oct),bin:new Sk.builtin.func(Sk.builtin.bin),dir:new Sk.builtin.func(Sk.builtin.dir),repr:new Sk.builtin.func(Sk.builtin.repr),open:new Sk.builtin.func(Sk.builtin.open),isinstance:new Sk.builtin.func(Sk.builtin.isinstance),
hash:new Sk.builtin.func(Sk.builtin.hash),getattr:new Sk.builtin.func(Sk.builtin.getattr),hasattr:new Sk.builtin.func(Sk.builtin.hasattr),id:new Sk.builtin.func(Sk.builtin.id),reduce:new Sk.builtin.func(Sk.builtin.reduce),sorted:new Sk.builtin.func(Sk.builtin.sorted),any:new Sk.builtin.func(Sk.builtin.any),all:new Sk.builtin.func(Sk.builtin.all),BaseException:Sk.builtin.BaseException,AttributeError:Sk.builtin.AttributeError,ValueError:Sk.builtin.ValueError,Exception:Sk.builtin.Exception,ZeroDivisionError:Sk.builtin.ZeroDivisionError,
AssertionError:Sk.builtin.AssertionError,ImportError:Sk.builtin.ImportError,IndentationError:Sk.builtin.IndentationError,IndexError:Sk.builtin.IndexError,KeyError:Sk.builtin.KeyError,TypeError:Sk.builtin.TypeError,LookupError:Sk.builtin.LookupError,UnicodeDecodeError:Sk.builtin.UnicodeDecodeError,UnicodeEncodeError:Sk.builtin.UnicodeEncodeError,NameError:Sk.builtin.NameError,IOError:Sk.builtin.IOError,NotImplementedError:Sk.builtin.NotImplementedError,StandardError:Sk.builtin.StandardError,SystemExit:Sk.builtin.SystemExit,
OverflowError:Sk.builtin.OverflowError,OperationError:Sk.builtin.OperationError,NegativePowerError:Sk.builtin.NegativePowerError,RuntimeError:Sk.builtin.RuntimeError,StopIteration:Sk.builtin.StopIteration,SyntaxError:Sk.builtin.SyntaxError,float_$rw$:Sk.builtin.float_,int_$rw$:Sk.builtin.int_,bool:Sk.builtin.bool,complex:Sk.builtin.complex,enumerate:Sk.builtin.enumerate,dict:Sk.builtin.dict,file:Sk.builtin.file,"function":Sk.builtin.func,generator:Sk.builtin.generator,list:Sk.builtin.list,long_$rw$:Sk.builtin.lng,
method:Sk.builtin.method,object:Sk.builtin.object,slice:Sk.builtin.slice,str:Sk.builtin.str,set:Sk.builtin.set,tuple:Sk.builtin.tuple,type:Sk.builtin.type,input:new Sk.builtin.func(Sk.builtin.input),raw_input:new Sk.builtin.func(Sk.builtin.raw_input),setattr:new Sk.builtin.func(Sk.builtin.setattr),jseval:Sk.builtin.jseval,jsmillis:Sk.builtin.jsmillis,quit:new Sk.builtin.func(Sk.builtin.quit),exit:new Sk.builtin.func(Sk.builtin.quit),print:Sk.builtin.print,divmod:new Sk.builtin.func(Sk.builtin.divmod),
format:new Sk.builtin.func(Sk.builtin.format),globals:new Sk.builtin.func(Sk.builtin.globals),issubclass:new Sk.builtin.func(Sk.builtin.issubclass),iter:Sk.builtin.iter,bytearray:Sk.builtin.bytearray,callable:Sk.builtin.callable,delattr:Sk.builtin.delattr,eval_$rw$:Sk.builtin.eval_,execfile:Sk.builtin.execfile,frozenset:Sk.builtin.frozenset,help:Sk.builtin.help,locals:Sk.builtin.locals,memoryview:Sk.builtin.memoryview,next:Sk.builtin.next_,pow:Sk.builtin.pow,reload:Sk.builtin.reload,reversed:Sk.builtin.reversed,
"super":Sk.builtin.super_,unichr:Sk.builtin.unichr,vars:Sk.builtin.vars,xrange:Sk.builtin.xrange,apply_$rw$:Sk.builtin.apply_,buffer:Sk.builtin.buffer,coerce:Sk.builtin.coerce,intern:Sk.builtin.intern};Sk.setupObjects=function(a){a?(Sk.builtins.filter=Sk.builtin.filter_,Sk.builtins.map=Sk.builtin.map_,Sk.builtins.zip=Sk.builtin.zip_,Sk.builtins.bytes=Sk.builtin.bytes,Sk.builtins.range=new Sk.builtin.func(Sk.builtin.xrange),delete Sk.builtins.xrange,delete Sk.builtins.StandardError,delete Sk.builtins.unicode,
delete Sk.builtins.basestring,delete Sk.builtin.str.prototype.decode,Sk.builtins.bytes=Sk.builtin.bytes,Sk.builtins.ascii=new Sk.builtin.func(Sk.builtin.ascii)):(Sk.builtins.filter=new Sk.builtin.func(Sk.builtin.filter),Sk.builtins.map=new Sk.builtin.func(Sk.builtin.map),Sk.builtins.zip=new Sk.builtin.func(Sk.builtin.zip),Sk.builtins.range=new Sk.builtin.func(Sk.builtin.range),Sk.builtins.xrange=new Sk.builtin.func(Sk.builtin.xrange),Sk.builtins.StandardError=Sk.builtin.Exception,Sk.builtins.unicode=
Sk.builtin.str,Sk.builtins.basestring=Sk.builtin.str,Sk.builtin.str.prototype.decode=Sk.builtin.str.$py2decode,delete Sk.builtins.bytes,delete Sk.builtins.ascii)};Sk.exportSymbol("Sk.setupObjects",Sk.setupObjects);Sk.exportSymbol("Sk.builtins",Sk.builtins)},function(m,p){Sk.builtin.str.$emptystr=new Sk.builtin.str("");Sk.builtin.int_.co_varnames=["number","base"];Sk.builtin.int_.$defaults=[0,Sk.builtin.none.none$];Sk.builtin.lng.co_varnames=["number","base"];Sk.builtin.lng.$defaults=[0,Sk.builtin.none.none$];
Sk.builtin.sorted.co_varnames=["list","cmp","key","reverse"];Sk.builtin.sorted.$defaults=[Sk.builtin.none.none$,Sk.builtin.none.none$,Sk.builtin.bool.false$];Sk.builtin.dict.$fromkeys.co_name=new Sk.builtin.str("fromkeys");Sk.builtin.dict.prototype.fromkeys=new Sk.builtin.func(Sk.builtin.dict.$fromkeys);Sk.builtin.str.$empty=new Sk.builtin.str("");Sk.builtin.str.$utf8=new Sk.builtin.str("utf-8");Sk.builtin.str.$ascii=new Sk.builtin.str("ascii");Sk.builtin.str.$default_factory=new Sk.builtin.str("default_factory");
Sk.builtin.str.$imag=new Sk.builtin.str("imag");Sk.builtin.str.$real=new Sk.builtin.str("real");Sk.builtin.str.$abs=new Sk.builtin.str("__abs__");Sk.builtin.str.$bytes=new Sk.builtin.str("__bytes__");Sk.builtin.str.$call=new Sk.builtin.str("__call__");Sk.builtin.str.$cmp=new Sk.builtin.str("__cmp__");Sk.builtin.str.$complex=new Sk.builtin.str("__complex__");Sk.builtin.str.$contains=new Sk.builtin.str("__contains__");Sk.builtin.str.$copy=new Sk.builtin.str("__copy__");Sk.builtin.str.$dict=new Sk.builtin.str("__dict__");
Sk.builtin.str.$dir=new Sk.builtin.str("__dir__");Sk.builtin.str.$enter=new Sk.builtin.str("__enter__");Sk.builtin.str.$eq=new Sk.builtin.str("__eq__");Sk.builtin.str.$exit=new Sk.builtin.str("__exit__");Sk.builtin.str.$index=new Sk.builtin.str("__index__");Sk.builtin.str.$init=new Sk.builtin.str("__init__");Sk.builtin.str.$int_=new Sk.builtin.str("__int__");Sk.builtin.str.$iter=new Sk.builtin.str("__iter__");Sk.builtin.str.$float_=new Sk.builtin.str("__float__");Sk.builtin.str.$format=new Sk.builtin.str("__format__");
Sk.builtin.str.$ge=new Sk.builtin.str("__ge__");Sk.builtin.str.$getattr=new Sk.builtin.str("__getattr__");Sk.builtin.str.$getattribute=new Sk.builtin.str("__getattribute__");Sk.builtin.str.$getitem=new Sk.builtin.str("__getitem__");Sk.builtin.str.$gt=new Sk.builtin.str("__gt__");Sk.builtin.str.$le=new Sk.builtin.str("__le__");Sk.builtin.str.$len=new Sk.builtin.str("__len__");Sk.builtin.str.$lt=new Sk.builtin.str("__lt__");Sk.builtin.str.$module=new Sk.builtin.str("__module__");Sk.builtin.str.$name=
new Sk.builtin.str("__name__");Sk.builtin.str.$ne=new Sk.builtin.str("__ne__");Sk.builtin.str.$new=new Sk.builtin.str("__new__");Sk.builtin.str.$next=new Sk.builtin.str("__next__");Sk.builtin.str.$path=new Sk.builtin.str("__path__");Sk.builtin.str.$repr=new Sk.builtin.str("__repr__");Sk.builtin.str.$reversed=new Sk.builtin.str("__reversed__");Sk.builtin.str.$round=new Sk.builtin.str("__round__");Sk.builtin.str.$setattr=new Sk.builtin.str("__setattr__");Sk.builtin.str.$setitem=new Sk.builtin.str("__setitem__");
Sk.builtin.str.$str=new Sk.builtin.str("__str__");Sk.builtin.str.$trunc=new Sk.builtin.str("__trunc__");Sk.builtin.str.$write=new Sk.builtin.str("write");Sk.misceval.op2method_={Eq:Sk.builtin.str.$eq,NotEq:Sk.builtin.str.$ne,Gt:Sk.builtin.str.$gt,GtE:Sk.builtin.str.$ge,Lt:Sk.builtin.str.$lt,LtE:Sk.builtin.str.$le};m="int_ lng sorted range round len min max sum zip abs fabs ord chr hex oct bin dir repr open isinstance hash getattr hasattr id map filter reduce sorted any all input raw_input setattr quit quit divmod format globals issubclass".split(" ");
for(p=0;p<m.length;p++)Sk.builtin[m[p]].co_name=new Sk.builtin.str(m[p])},function(m,p){Sk.internalPy={files:{"src/classmethod.py":'class classmethod(object):\n    "Emulate PyClassMethod_Type() in Objects/funcobject.c"\n\n    def __init__(self, f):\n        self.f = f\n\n    def __get__(self, obj, klass=None):\n        if klass is None:\n            klass = type(obj)\n        def newfunc(*args):\n            return self.f(klass, *args)\n        return newfunc\n',"src/property.py":'class property(object):\n    "Emulate PyProperty_Type() in Objects/descrobject.c"\n\n    def __init__(self, fget=None, fset=None, fdel=None, doc=None):\n        self.fget = fget\n        self.fset = fset\n        self.fdel = fdel\n        if doc is None and fget is not None:\n            if hasattr(fget, \'__doc__\'):\n                doc = fget.__doc__\n            else:\n                doc = None\n        self.__doc__ = doc\n\n    def __get__(self, obj, objtype=None):\n        if obj is None:\n            return self\n        if self.fget is None:\n            raise AttributeError("unreadable attribute")\n        return self.fget(obj)\n\n    def __set__(self, obj, value):\n        if self.fset is None:\n            raise AttributeError("can\'t set attribute")\n        self.fset(obj, value)\n\n    def __delete__(self, obj):\n        if self.fdel is None:\n            raise AttributeError("can\'t delete attribute")\n        self.fdel(obj)\n\n    def getter(self, fget):\n        return type(self)(fget, self.fset, self.fdel, self.__doc__)\n\n    def setter(self, fset):\n        return type(self)(self.fget, fset, self.fdel, self.__doc__)\n\n    def deleter(self, fdel):\n        return type(self)(self.fget, self.fset, fdel, self.__doc__)\n',
"src/staticmethod.py":'class staticmethod(object):\n    "Emulate PyStaticMethod_Type() in Objects/funcobject.c"\n\n    def __init__(self, f):\n        self.f = f\n\n    def __get__(self, obj, objtype=None):\n        return self.f\n'}}}]);}).call(this || window)

//# sourceMappingURL=skulpt.min.js.map
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../../node_modules/webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))

/***/ }),

/***/ "./runestone/activecode/js/timed_activecode.js":
/*!*****************************************************!*\
  !*** ./runestone/activecode/js/timed_activecode.js ***!
  \*****************************************************/
/*! exports provided: TimedLiveCode, TimedActiveCode, TimedJSActiveCode, TimedHTMLActiveCode, TimedSQLActiveCode */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TimedLiveCode", function() { return TimedLiveCode; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TimedActiveCode", function() { return TimedActiveCode; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TimedJSActiveCode", function() { return TimedJSActiveCode; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TimedHTMLActiveCode", function() { return TimedHTMLActiveCode; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TimedSQLActiveCode", function() { return TimedSQLActiveCode; });
/* harmony import */ var _livecode__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./livecode */ "./runestone/activecode/js/livecode.js");
/* harmony import */ var _activecode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./activecode */ "./runestone/activecode/js/activecode.js");
/* harmony import */ var _activecode_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./activecode_js */ "./runestone/activecode/js/activecode_js.js");
/* harmony import */ var _activecode_html__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./activecode_html */ "./runestone/activecode/js/activecode_html.js");
/* harmony import */ var _activecode_sql__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./activecode_sql */ "./runestone/activecode/js/activecode_sql.js");
/*
The TimedActivecode classes are a great example of where multiple inheritance would be useful
But since Javascript does not suppport multiple inheritance we use the mixin pattern.

*/






var TimedActiveCodeMixin = {
    timedInit: async function (opts) {
        this.isTimed = true;
        this.hideButtons();
        await this.addHistoryScrubber(true); // position last
        this.needsReinitialization = true; // the run button click listener needs to be reinitialized
        this.containerDiv.classList.add("timedComponent");
        window.edList[this.divid] = this;
        return true;
    },

    hideButtons: function () {
        var buttonList = [
            this.saveButton,
            this.loadButton,
            this.gradeButton,
            this.showHideButt,
            this.coachButton,
            this.atButton,
        ];
        for (var i = 0; i < buttonList.length; i++) {
            if (buttonList[i] !== undefined && buttonList[i] !== null)
                $(buttonList[i]).hide();
        }
    },

    // bje - not needed anymore
    renderTimedIcon: function (component) {
        // renders the clock icon on timed components.    The component parameter
        // is the element that the icon should be appended to.
        var timeIconDiv = document.createElement("div");
        var timeIcon = document.createElement("img");
        $(timeIcon).attr({
            src: "../_static/clock.png",
            style: "width:15px;height:15px",
        });
        timeIconDiv.className = "timeTip";
        timeIconDiv.title = "";
        timeIconDiv.appendChild(timeIcon);
        $(component).prepend(timeIconDiv);
    },

    checkCorrectTimed: function () {
        // pct_correct is set by the unittest/gui.py module in skulpt.
        // it relies on finding this object in the edList
        if (this.isAnswered) {
            if (this.pct_correct >= 100.0) {
                return "T";
            } else {
                return "F";
            }
        } else {
            return "I"; // we ignore this in the grading if no unittests
        }
    },

    hideFeedback: function () {
        $(this.output).css("visibility", "hidden");
    },

    reinitializeListeners: function (taken) {
        if (!this.runButton.onclick) {
            console.log("reattaching runbuttonhandler");
            this.runButton.onclick = this.runButtonHander.bind(this);
        }
        $(this.codeDiv).show();
        this.runButton.disabled = false;
        $(this.codeDiv).removeClass("ac-disabled");
        this.editor.refresh();
        $(this.histButton).click(this.addHistoryScrubber.bind(this));
        if (this.historyScrubber !== null) {
            $(this.historyScrubber).slider({
                max: this.history.length - 1,
                value: this.history.length - 1,
                slide: this.slideit.bind(this),
                change: this.slideit.bind(this),
            });
        }
        if (taken) {
            $(`#${this.divid}_unit_results`).show();
        }
    },
};

class TimedLiveCode extends _livecode__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(opts) {
        super(opts);
        this.timedInit(opts);
    }
}

Object.assign(TimedLiveCode.prototype, TimedActiveCodeMixin);

class TimedActiveCode extends _activecode__WEBPACK_IMPORTED_MODULE_1__["ActiveCode"] {
    constructor(opts) {
        super(opts);
        this.timedInitComplete = this.timedInit(opts);
    }

    // for timed exams we need to call runProg and tell it that there is
    // no GUI for sliders or other things.
    // the answers.
    async checkCurrentAnswer() {
        let noUI = true;
        const result = await this.timedInitComplete;
        if (this.isAnswered) {
            await this.runProg(noUI, false);
        }
    }
}

Object.assign(TimedActiveCode.prototype, TimedActiveCodeMixin);

class TimedJSActiveCode extends _activecode_js__WEBPACK_IMPORTED_MODULE_2__["default"] {
    constructor(opts) {
        super(opts);
        this.timedInit(opts);
    }
}
Object.assign(TimedJSActiveCode.prototype, TimedActiveCodeMixin);

class TimedHTMLActiveCode extends _activecode_html__WEBPACK_IMPORTED_MODULE_3__["default"] {
    constructor(opts) {
        super(opts);
        this.timedInit(opts);
    }
}
Object.assign(TimedHTMLActiveCode.prototype, TimedActiveCodeMixin);

class TimedSQLActiveCode extends _activecode_sql__WEBPACK_IMPORTED_MODULE_4__["default"] {
    constructor(opts) {
        super(opts);
        this.timedInit(opts);
    }
}
Object.assign(TimedSQLActiveCode.prototype, TimedActiveCodeMixin);


/***/ }),

/***/ "./runestone/clickableArea/css/clickable.css":
/*!***************************************************!*\
  !*** ./runestone/clickableArea/css/clickable.css ***!
  \***************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var api = __webpack_require__(/*! ../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
            var content = __webpack_require__(/*! !../../../node_modules/css-loader/dist/cjs.js!./clickable.css */ "./node_modules/css-loader/dist/cjs.js!./runestone/clickableArea/css/clickable.css");

            content = content.__esModule ? content.default : content;

            if (typeof content === 'string') {
              content = [[module.i, content, '']];
            }

var options = {};

options.insert = "head";
options.singleton = false;

var update = api(content, options);



module.exports = content.locals || {};

/***/ }),

/***/ "./runestone/clickableArea/js/clickable.js":
/*!*************************************************!*\
  !*** ./runestone/clickableArea/js/clickable.js ***!
  \*************************************************/
/*! exports provided: CAList, default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CAList", function() { return CAList; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return ClickableArea; });
/* harmony import */ var _common_js_runestonebase_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/js/runestonebase.js */ "./runestone/common/js/runestonebase.js");
/* harmony import */ var _css_clickable_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../css/clickable.css */ "./runestone/clickableArea/css/clickable.css");
/* harmony import */ var _css_clickable_css__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_clickable_css__WEBPACK_IMPORTED_MODULE_1__);
/*==========================================
=======     Master clickable.js     ========
============================================
===   This file contains the JS for the  ===
===  Runestone clickable area component. ===
============================================
===              Created by              ===
===           Isaiah Mayerchak           ===
===                7/1/15                ===
==========================================*/





var CAList = {}; // Object that contains all instances of ClickableArea objects

class ClickableArea extends _common_js_runestonebase_js__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(opts) {
        super(opts);
        var orig = opts.orig; // entire <div> element that will be replaced by new HTML
        this.origElem = orig;
        this.divid = orig.id;
        this.useRunestoneServices = opts.useRunestoneServices;
        this.clickableArray = []; // holds all clickable elements
        this.correctArray = []; // holds the IDs of all correct clickable span elements, used for eval
        this.incorrectArray = []; // holds IDs of all incorrect clickable span elements, used for eval
        //For use with Sphinx-rendered html
        this.isTable = false;
        if ($(this.origElem).data("cc") !== undefined) {
            if ($(this.origElem).is("[data-table]")) {
                this.isTable = true;
                this.ccArray = $(this.origElem).data("cc").split(";");
                this.ciArray = $(this.origElem).data("ci").split(";");
            } else {
                this.ccArray = $(this.origElem).data("cc").split(",");
                this.ciArray = $(this.origElem).data("ci").split(",");
            }
        }
        // For use in the recursive replace function
        this.clickIndex = 0; // Index of this.clickedIndexArray that we're checking against
        this.clickableCounter = 0; // Index of the current clickable element
        this.getQuestion();
        this.getFeedback();
        this.renderNewElements();
        this.caption = "Clickable";
        this.addCaption("runestone");
    }
    /*===========================
    == Update basic attributes ==
    ===========================*/
    getQuestion() {
        for (var i = 0; i < this.origElem.childNodes.length; i++) {
            if ($(this.origElem.childNodes[i]).is("[data-question]")) {
                this.question = this.origElem.childNodes[i];
                break;
            }
        }
    }
    getFeedback() {
        this.feedback = "";
        for (var i = 0; i < this.origElem.childNodes.length; i++) {
            if ($(this.origElem.childNodes[i]).is("[data-feedback]")) {
                this.feedback = this.origElem.childNodes[i];
            }
        }
        if (this.feedback !== "") {
            // Get the feedback element out of the container if the user has defined feedback
            $(this.feedback).remove();
            this.feedback = this.feedback.innerHTML;
        }
    }
    /*===========================================
    ====   Functions generating final HTML   ====
    ===========================================*/
    renderNewElements() {
        // wrapper function for generating everything
        this.containerDiv = document.createElement("div");
        this.containerDiv.appendChild(this.question);
        $(this.containerDiv).addClass(this.origElem.getAttribute("class"));
        this.newDiv = document.createElement("div");
        var newContent = $(this.origElem).html();
        while (newContent[0] === "\n") {
            newContent = newContent.slice(1);
        }
        this.newDiv.innerHTML = newContent;
        this.containerDiv.appendChild(this.newDiv);
        this.checkServer("clickableArea");
        this.createButtons();
        this.createFeedbackDiv();
        $(this.origElem).replaceWith(this.containerDiv);
    }
    createButtons() {
        this.submitButton = document.createElement("button"); // Check me button
        this.submitButton.textContent = "Check Me";
        $(this.submitButton).attr({
            class: "btn btn-success",
            name: "do answer",
            type: "button",
        });
        this.submitButton.onclick = function () {
            this.checkCurrentAnswer();
            this.logCurrentAnswer();
            this.renderFeedback();
        }.bind(this);
        this.containerDiv.appendChild(this.submitButton);
    }
    createFeedbackDiv() {
        this.feedBackDiv = document.createElement("div");
        this.containerDiv.appendChild(document.createElement("br"));
        this.containerDiv.appendChild(this.feedBackDiv);
    }
    /*===================================
    === Checking/restoring from storage ===
    ===================================*/
    restoreAnswers(data) {
        // Restore answers from storage retrieval done in RunestoneBase or from local storage
        if (data.answer !== undefined) {
            // if we got data from the server
            this.hasStoredAnswers = true;
            this.clickedIndexArray = data.answer.split(";");
        }
        if (this.ccArray === undefined) {
            this.modifyClickables(this.newDiv.childNodes);
        } else {
            // For use with Sphinx-rendered HTML
            this.ccCounter = 0;
            this.ccIndex = 0;
            this.ciIndex = 0;
            if (!this.isTable) {
                this.modifyViaCC(this.newDiv.children);
            } else {
                this.modifyTableViaCC(this.newDiv.children);
            }
        }
    }
    checkLocalStorage() {
        if (this.graderactive) {
            return;
        }
        var storageObj;
        // Gets previous answer data from local storage if it exists
        this.hasStoredAnswers = false;
        var len = localStorage.length;
        if (len > 0) {
            var ex = localStorage.getItem(this.localStorageKey());
            if (ex !== null) {
                this.hasStoredAnswers = true;
                try {
                    storageObj = JSON.parse(ex);
                    this.clickedIndexArray = storageObj.answer.split(";");
                } catch (err) {
                    // error while parsing; likely due to bad value stored in storage
                    console.log(err.message);
                    localStorage.removeItem(this.localStorageKey());
                    this.hasStoredAnswers = false;
                    this.restoreAnswers({});
                    return;
                }
                if (this.useRunestoneServices) {
                    // log answer to server
                    this.givenIndexArray = [];
                    for (var i = 0; i < this.clickableArray.length; i++) {
                        if (
                            $(this.clickableArray[i]).hasClass(
                                "clickable-clicked"
                            )
                        ) {
                            this.givenIndexArray.push(i);
                        }
                    }
                    this.logBookEvent({
                        event: "clickableArea",
                        act: this.clickedIndexArray.join(";"),
                        div_id: this.divid,
                        correct: storageObj.correct,
                    });
                }
            }
        }
        this.restoreAnswers({}); // pass empty object
    }
    setLocalStorage(data) {
        // Array of the indices of clicked elements is passed to local storage
        var answer;
        if (data.answer !== undefined) {
            // If we got data from the server, we can just use that
            answer = this.clickedIndexArray.join(";");
        } else {
            this.givenIndexArray = [];
            for (var i = 0; i < this.clickableArray.length; i++) {
                if ($(this.clickableArray[i]).hasClass("clickable-clicked")) {
                    this.givenIndexArray.push(i);
                }
            }
            answer = this.givenIndexArray.join(";");
        }
        var timeStamp = new Date();
        var correct = data.correct;
        var storageObject = {
            answer: answer,
            correct: correct,
            timestamp: timeStamp,
        };
        localStorage.setItem(
            this.localStorageKey(),
            JSON.stringify(storageObject)
        );
    }
    /*==========================
    === Auxilliary functions ===
    ==========================*/
    modifyClickables(childNodes) {
        // Strips the data-correct/data-incorrect labels and updates the correct/incorrect arrays
        for (var i = 0; i < childNodes.length; i++) {
            if (
                $(childNodes[i]).is("[data-correct]") ||
                $(childNodes[i]).is("[data-incorrect]")
            ) {
                this.manageNewClickable(childNodes[i]);
                if ($(childNodes[i]).is("[data-correct]")) {
                    $(childNodes[i]).removeAttr("data-correct");
                    this.correctArray.push(childNodes[i]);
                } else {
                    $(childNodes[i]).removeAttr("data-incorrect");
                    this.incorrectArray.push(childNodes[i]);
                }
            }
            if (childNodes[i].childNodes.length !== 0) {
                this.modifyClickables(childNodes[i].childNodes);
            }
        }
    }
    modifyViaCC(children) {
        for (var i = 0; i < children.length; i++) {
            if (children[i].children.length !== 0) {
                this.modifyViaCC(children[i].children);
            } else {
                this.ccCounter++;
                if (this.ccCounter === Math.floor(this.ccArray[this.ccIndex])) {
                    this.manageNewClickable(children[i]);
                    this.correctArray.push(children[i]);
                    this.ccIndex++;
                } else if (
                    this.ccCounter === Math.floor(this.ciArray[this.ciIndex])
                ) {
                    this.manageNewClickable(children[i]);
                    this.incorrectArray.push(children[i]);
                    this.ciIndex++;
                }
            }
        }
    }
    modifyTableViaCC(children) {
        // table version of modifyViaCC
        var tComponentArr = [];
        for (let i = 0; i < children.length; i++) {
            if (children[i].nodeName === "TABLE") {
                let tmp = children[i];
                for (let j = 0; j < tmp.children.length; j++) {
                    if (tmp.children[j].nodeName === "THEAD") {
                        tComponentArr.push(tmp.children[j]);
                    } else if (tmp.children[j].nodeName === "TBODY") {
                        tComponentArr.push(tmp.children[j]);
                    } else if (tmp.children[j].nodeName === "TFOOT") {
                        tComponentArr.push(tmp.children[j]);
                    }
                }
            }
        }
        for (var t = 0; t < tComponentArr.length; t++) {
            for (let i = 0; i < tComponentArr[t].children.length; i++) {
                this.ccCounter++;
                // First check if the entire row needs to be clickable
                if (
                    this.ccIndex < this.ccArray.length &&
                    this.ccCounter ===
                        Math.floor(this.ccArray[this.ccIndex].split(",")[0]) &&
                    Math.floor(this.ccArray[this.ccIndex].split(",")[1]) === 0
                ) {
                    this.manageNewClickable(tComponentArr[t].children[i]);
                    this.correctArray.push(tComponentArr[t].children[i]);
                    this.ccIndex++;
                } else if (
                    this.ciIndex < this.ciArray.length &&
                    this.ccCounter ===
                        Math.floor(this.ciArray[this.ciIndex].split(",")[0]) &&
                    Math.floor(this.ciArray[this.ciIndex].split(",")[1]) === 0
                ) {
                    this.manageNewClickable(tComponentArr[t].children[i]);
                    this.incorrectArray.push(tComponentArr[t].children[i]);
                    this.ciIndex++;
                } else {
                    // If not, check the individual data cells
                    for (
                        let j = 0;
                        j < tComponentArr[t].children[i].children.length;
                        j++
                    ) {
                        let tmp = j + 1;
                        if (
                            this.ccIndex < this.ccArray.length &&
                            tmp ===
                                Math.floor(
                                    this.ccArray[this.ccIndex].split(",")[1]
                                ) &&
                            this.ccCounter ===
                                Math.floor(
                                    this.ccArray[this.ccIndex].split(",")[0]
                                )
                        ) {
                            this.manageNewClickable(
                                tComponentArr[t].children[i].children[j]
                            );
                            this.correctArray.push(
                                tComponentArr[t].children[i].children[j]
                            );
                            this.ccIndex++;
                        } else if (
                            this.ciIndex < this.ciArray.length &&
                            tmp ===
                                Math.floor(
                                    this.ciArray[this.ciIndex].split(",")[1]
                                ) &&
                            this.ccCounter ===
                                Math.floor(
                                    this.ciArray[this.ciIndex].split(",")[0]
                                )
                        ) {
                            this.manageNewClickable(
                                tComponentArr[t].children[i].children[j]
                            );
                            this.incorrectArray.push(
                                tComponentArr[t].children[i].children[j]
                            );
                            this.ciIndex++;
                        }
                    }
                }
            }
        }
    }
    manageNewClickable(clickable) {
        // adds the "clickable" functionality
        $(clickable).addClass("clickable");
        if (this.hasStoredAnswers) {
            // Check if the element we're about to append to the pre was in local storage as clicked via its index
            if (
                this.clickedIndexArray[this.clickIndex].toString() ===
                this.clickableCounter.toString()
            ) {
                $(clickable).addClass("clickable-clicked");
                this.clickIndex++;
                if (this.clickIndex === this.clickedIndexArray.length) {
                    // Stop doing this if the index array is used up
                    this.hasStoredAnswers = false;
                }
            }
        }
        let self = this;
        clickable.onclick = function () {
            self.isAnswered = true;
            if ($(this).hasClass("clickable-clicked")) {
                $(this).removeClass("clickable-clicked");
                $(this).removeClass("clickable-incorrect");
            } else {
                $(this).addClass("clickable-clicked");
            }
        };
        this.clickableArray.push(clickable);
        this.clickableCounter++;
    }
    /*======================================
    == Evaluation and displaying feedback ==
    ======================================*/
    checkCurrentAnswer() {
        // Evaluation is done by iterating over the correct/incorrect arrays and checking by class
        this.correct = true;
        this.correctNum = 0;
        this.incorrectNum = 0;
        for (let i = 0; i < this.correctArray.length; i++) {
            if (!$(this.correctArray[i]).hasClass("clickable-clicked")) {
                this.correct = false;
            } else {
                this.correctNum++;
            }
        }
        for (let i = 0; i < this.incorrectArray.length; i++) {
            if ($(this.incorrectArray[i]).hasClass("clickable-clicked")) {
                this.correct = false;
                this.incorrectNum++;
            } else {
                $(this.incorrectArray[i]).removeClass("clickable-incorrect");
            }
        }
        this.percent =
            (this.correctNum - this.incorrectNum) / this.correctArray.length;
        this.setLocalStorage({ correct: this.correct ? "T" : "F" });
    }

    logCurrentAnswer() {
        this.logBookEvent({
            event: "clickableArea",
            act: this.givenIndexArray.join(";"),
            div_id: this.divid,
            correct: this.correct ? "T" : "F",
        });
    }

    renderFeedback() {
        if (this.correct) {
            $(this.feedBackDiv).html("You are Correct!");
            $(this.feedBackDiv).attr("class", "alert alert-info");
        } else {
            for (let i = 0; i < this.incorrectArray.length; i++) {
                if ($(this.incorrectArray[i]).hasClass("clickable-clicked")) {
                    $(this.incorrectArray[i]).addClass("clickable-incorrect");
                } else {
                    $(this.incorrectArray[i]).removeClass(
                        "clickable-incorrect"
                    );
                }
            }
            $(this.feedBackDiv).html(
                "Incorrect. You clicked on " +
                    this.correctNum +
                    " of the " +
                    this.correctArray.length.toString() +
                    " correct elements and " +
                    this.incorrectNum +
                    " of the " +
                    this.incorrectArray.length.toString() +
                    " incorrect elements. " +
                    this.feedback
            );
            $(this.feedBackDiv).attr("class", "alert alert-danger");
        }
    }

    disableInteraction() {
        for (var i = 0; i < this.clickableArray.length; i++) {
            $(this.clickableArray[i]).css("cursor", "initial");
            this.clickableArray[i].onclick = function () {
                return;
            };
        }
    }
}

/*=================================
== Find the custom HTML tags and ==
==   execute our code on them    ==
=================================*/
$(document).bind("runestone:login-complete", function () {
    $("[data-component=clickablearea]").each(function (index) {
        if ($(this).closest("[data-component=timedAssessment]").length == 0) {
            // If this element exists within a timed component, don't render it here
            try {
                CAList[this.id] = new ClickableArea({
                    orig: this,
                    useRunestoneServices: eBookConfig.useRunestoneServices,
                });
            } catch (err) {
                console.log(`Error rendering ClickableArea Problem ${this.id}
                             Details: ${err}`);
            }
        }
    });
});


/***/ }),

/***/ "./runestone/clickableArea/js/timedclickable.js":
/*!******************************************************!*\
  !*** ./runestone/clickableArea/js/timedclickable.js ***!
  \******************************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return TimedClickableArea; });
/* harmony import */ var _clickable_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./clickable.js */ "./runestone/clickableArea/js/clickable.js");


("use strict");

class TimedClickableArea extends _clickable_js__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(opts) {
        super(opts);
        this.restoreAnswers({});
        this.renderTimedIcon(this.containerDiv);
        this.hideButtons();
    }

    hideButtons() {
        $(this.submitButton).hide();
    }

    renderTimedIcon(component) {
        // renders the clock icon on timed components.    The component parameter
        // is the element that the icon should be appended to.
        var timeIconDiv = document.createElement("div");
        var timeIcon = document.createElement("img");
        $(timeIcon).attr({
            src: "../_static/clock.png",
            style: "width:15px;height:15px",
        });
        timeIconDiv.className = "timeTip";
        timeIconDiv.title = "";
        timeIconDiv.appendChild(timeIcon);
        $(component).prepend(timeIconDiv);
    }

    checkCorrectTimed() {
        // Returns if the question was correct, incorrect, or skipped (return null in the last case)
        if (this.correctNum === 0 && this.incorrectNum === 0) {
            this.correct = null;
        }
        switch (this.correct) {
            case true:
                return "T";
            case false:
                return "F";
            default:
                return null;
        }
    }

    hideFeedback() {
        $(this.feedBackDiv).hide();
    }
}

if (typeof window.component_factory === "undefined") {
    window.component_factory = {};
}
window.component_factory.clickablearea = function (opts) {
    if (opts.timed) {
        return new TimedClickableArea(opts);
    }
    return new _clickable_js__WEBPACK_IMPORTED_MODULE_0__["default"](opts);
};


/***/ }),

/***/ "./runestone/codelens/css/pytutor.css":
/*!********************************************!*\
  !*** ./runestone/codelens/css/pytutor.css ***!
  \********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var api = __webpack_require__(/*! ../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
            var content = __webpack_require__(/*! !../../../node_modules/css-loader/dist/cjs.js!./pytutor.css */ "./node_modules/css-loader/dist/cjs.js!./runestone/codelens/css/pytutor.css");

            content = content.__esModule ? content.default : content;

            if (typeof content === 'string') {
              content = [[module.i, content, '']];
            }

var options = {};

options.insert = "head";
options.singleton = false;

var update = api(content, options);



module.exports = content.locals || {};

/***/ }),

/***/ "./runestone/codelens/js/codelens.js":
/*!*******************************************!*\
  !*** ./runestone/codelens/js/codelens.js ***!
  \*******************************************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _css_pytutor_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./../css/pytutor.css */ "./runestone/codelens/css/pytutor.css");
/* harmony import */ var _css_pytutor_css__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_pytutor_css__WEBPACK_IMPORTED_MODULE_0__);
/**
 * Created by bmiller on 5/10/15.
 */

/*
 Since I don't want to modify the codelens code I'll attach the logging functionality this way.
 This actually seems like a better way to do it maybe across the board to separate logging
 from the real funcionality.  It would also allow a better way of turning off/on logging..
 As long as Philip doesn't go and change the id values for the buttons and slider this will
 continue to work.... In the best of all worlds we might add a function to the visualizer to
 return the buttons, but I'm having a hard time thinking of any other use for that besides mine.
 */



function attachLoggers(codelens, divid) {
    let rb = new RunestoneBase();
    codelens.domRoot.find("#jmpFirstInstr").click(function () {
        rb.logBookEvent({ event: "codelens", act: "first", div_id: divid });
    });
    codelens.domRoot.find("#jmpLastInstr").click(function () {
        rb.logBookEvent({ event: "codelens", act: "last", div_id: divid });
    });
    codelens.domRoot.find("#jmpStepBack").click(function () {
        rb.logBookEvent({ event: "codelens", act: "back", div_id: divid });
    });
    codelens.domRoot.find("#jmpStepFwd").click(function () {
        rb.logBookEvent({ event: "codelens", act: "fwd", div_id: divid });
    });
    codelens.domRoot.find("#executionSlider").bind("slide", function (evt, ui) {
        rb.logBookEvent({ event: "codelens", act: "slide", div_id: divid });
    });
}

function styleButtons(divid) {
    var myVis = $("#" + divid);
    $(myVis).find("#jmpFirstInstr").addClass("btn btn-default");
    $(myVis).find("#jmpStepBack").addClass("btn btn-danger");
    $(myVis).find("#jmpStepFwd").addClass("btn btn-success");
    $(myVis).find("#jmpLastInstr").addClass("btn btn-default");
}

if (typeof allVsualizers === "undefined") {
    window.allVisualizers = [];
}

$(document).ready(function () {
    if (typeof allTraceData !== "undefined") {
        for (let divid in allTraceData) {
            let cl = document.getElementById(divid);
            let lang = $(cl).data("params").lang;
            try {
                let vis = addVisualizerToPage(allTraceData[divid], divid, {
                    startingInstruction: 0,
                    editCodeBaseURL: null,
                    hideCode: false,
                    lang: lang,
                });
                attachLoggers(vis, divid);
                styleButtons(divid);
                window.allVisualizers.push(vis);
            } catch (err) {
                console.log(`Error rendering CodeLens Problem ${divid}`);
                console.log(err);
            }
        }
        document.addEventListener("codelens:answer", function (evt) {
            let rb = new RunestoneBase();
            rb.logBookEvent({
                event: "codelens",
                div_id: evt.detail.divid,
                act: `answer:${evt.detail.answer}`,
                correct: evt.detail.correct,
            });
            console.log(evt);
        });
    }
});


/***/ }),

/***/ "./runestone/common/css/active.png":
/*!*****************************************!*\
  !*** ./runestone/common/css/active.png ***!
  \*****************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony default export */ __webpack_exports__["default"] = ("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAIlJREFUeNpi/P//PwMlgImBQjAMDGCBsxZIkaYz4RmaAZhAHYjNoOxTQHwTvwsQgAeIe4E4DU18FhAXAPF3QgbMB+IQLOIgA4WAOBRfIOri0AwDIDktfAbYEBF89jRNB0eI0HMEnwGXgXgNHs1roGrweiERGmUMWKIxkZh08AWI04G4j5iEBBBgAPddF58mobqdAAAAAElFTkSuQmCC");

/***/ }),

/***/ "./runestone/common/css/completed.png":
/*!********************************************!*\
  !*** ./runestone/common/css/completed.png ***!
  \********************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony default export */ __webpack_exports__["default"] = ("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAO5JREFUeNpi/P//PwMlgImBQjAwBthc4osH4skgNguJGrmA1BQgjgPiUJIMAGpWBVKrgNgAiL2P6H3aRrQBQM12QGo1EIsBcQxMM0oYABXxAjETFs2+QGobVHM9UPNSjEAEKgoEUjeBuBhNcwDUZm4gXg/U3IRuAcwLSkAsCcRdQE16QDoRiG2BGGQbOxA/BOIcbN5jhKVEoMYiINULFT8PxCJALAvlhwBtX4vXAKghpSBXoKlZCtQcQ1RCAirsBlJVSEIfgLiRpJQINKQdSNVCuTOB/Nt44xjkBWzY+iJvGRBr4pKHYUZKszNAgAEAln9yGpx/ZXMAAAAASUVORK5CYII=");

/***/ }),

/***/ "./runestone/common/css/user-highlights.css":
/*!**************************************************!*\
  !*** ./runestone/common/css/user-highlights.css ***!
  \**************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var api = __webpack_require__(/*! ../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
            var content = __webpack_require__(/*! !../../../node_modules/css-loader/dist/cjs.js!./user-highlights.css */ "./node_modules/css-loader/dist/cjs.js!./runestone/common/css/user-highlights.css");

            content = content.__esModule ? content.default : content;

            if (typeof content === 'string') {
              content = [[module.i, content, '']];
            }

var options = {};

options.insert = "head";
options.singleton = false;

var update = api(content, options);



module.exports = content.locals || {};

/***/ }),

/***/ "./runestone/common/js/bookfuncs.js":
/*!******************************************!*\
  !*** ./runestone/common/js/bookfuncs.js ***!
  \******************************************/
/*! exports provided: pageProgressTracker */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "pageProgressTracker", function() { return pageProgressTracker; });
/**
 *
 * User: bmiller
 * Original: 2011-04-20
 * Date: 2019-06-14
 * Time: 2:01 PM
 * This change marks the beginning of version 4.0 of the runestone components
 * Login/logout is no longer handled through javascript but rather server side.
 * Many of the components depend on the runestone:login event so we will keep that
 * for now to keep the churn fairly minimal.
 */

/*

 Copyright (C) 2011  Brad Miller  bonelake@gmail.com

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.

 */

//
// Chevron functions - Must correspond with width in runestone-custom-sphinx-bootstrap.css
//
$(function () {
    var resizeWindow = false;
    var resizeWidth = 600;
    $(window)
        .on("resize", function (event) {
            if ($(window).width() <= resizeWidth && resizeWindow == false) {
                resizeWindow = true;
                var topPrev = $("#relations-prev")
                    .clone()
                    .attr("id", "top-relations-prev");
                var topNext = $("#relations-next")
                    .clone()
                    .attr("id", "top-relations-next");
                $("#relations-prev, #relations-next").hide();
                var bottomPrev = topPrev
                    .clone()
                    .attr("id", "bottom-relations-prev");
                var bottomNext = topNext
                    .clone()
                    .attr("id", "bottom-relations-next");
                $("div#main-content > div").prepend(topPrev, topNext);
                $("#top-relations-prev, #top-relations-next").wrapAll(
                    '<ul id="top-relations-console"></ul>'
                );
                $("div#main-content > div").append(bottomPrev, bottomNext);
                $("#bottom-relations-prev, #bottom-relations-next").wrapAll(
                    '<ul id="bottom-relations-console"></ul>'
                );
            }
            if ($(window).width() >= resizeWidth + 1 && resizeWindow == true) {
                resizeWindow = false;
                $("#top-relations-console, #bottom-relations-console").remove();
                $("#relations-prev, #relations-next").show();
            }
        })
        .resize();
});

//
// Page decoration functions
//

function addReadingList() {
    if (eBookConfig.readings) {
        var l, nxt, path_parts, nxt_link;
        let cur_path_parts = window.location.pathname.split("/");
        let name =
            cur_path_parts[cur_path_parts.length - 2] +
            "/" +
            cur_path_parts[cur_path_parts.length - 1];
        let position = eBookConfig.readings.indexOf(name);
        let num_readings = eBookConfig.readings.length;
        if (position == eBookConfig.readings.length - 1) {
            // no more readings
            l = $("<div />", {
                text: `Finished reading assignment. Page ${num_readings} of ${num_readings}.`,
            });
        } else if (position >= 0) {
            // get next name
            nxt = eBookConfig.readings[position + 1];
            path_parts = cur_path_parts.slice(0, cur_path_parts.length - 2);
            path_parts.push(nxt);
            nxt_link = path_parts.join("/");
            l = $("<a />", {
                name: "link",
                class: "btn btn-lg ' + 'buttonConfirmCompletion'",
                href: nxt_link,
                text: `Continue to page ${
                    position + 2
                } of ${num_readings} in the reading assignment.`,
            });
        } else {
            l = $("<div />", {
                text:
                    "This page is not part of the last reading assignment you visited.",
            });
        }
        $("#main-content").append(l);
    }
}

function timedRefresh() {
    var timeoutPeriod = 900000; // 75 minutes
    $(document).bind("idle.idleTimer", function () {
        // After timeout period send the user back to the index.  This will force a login
        // if needed when they want to go to a particular page.  This may not be perfect
        // but its an easy way to make sure laptop users are properly logged in when they
        // take quizzes and save stuff.
        if (location.href.indexOf("index.html") < 0) {
            console.log("Idle timer - " + location.pathname);
            location.href =
                eBookConfig.app +
                "/default/user/login?_next=" +
                location.pathname +
                location.search;
        }
    });
    $.idleTimer(timeoutPeriod);
}

class PageProgressBar {
    constructor(actDict) {
        this.possible = 0;
        this.total = 1;
        if (actDict && Object.keys(actDict).length > 0) {
            this.activities = actDict;
        } else {
            let activities = { page: 0 };
            $(".runestone").each(function (idx, e) {
                activities[e.firstElementChild.id] = 0;
            });
            this.activities = activities;
        }
        this.calculateProgress();
        if (
            window.location.pathname.match(
                /.*(index.html|toctree.html|Exercises.html|Glossary.html|search.html)$/i
            )
        ) {
            $("#scprogresscontainer").hide();
        }
        this.renderProgress();
    }

    calculateProgress() {
        for (let k in this.activities) {
            if (k !== undefined) {
                this.possible++;
                if (this.activities[k] > 0) {
                    this.total++;
                }
            }
        }
    }

    renderProgress() {
        let value = 0;
        $("#scprogresstotal").text(this.total);
        $("#scprogressposs").text(this.possible);
        try {
            value = (100 * this.total) / this.possible;
        } catch (e) {
            value = 0;
        }
        $("#subchapterprogress").progressbar({
            value: value,
        });
        if (!eBookConfig.isLoggedIn) {
            $("#subchapterprogress>div").addClass("loggedout");
        }
    }

    updateProgress(div_id) {
        this.activities[div_id]++;
        // Only update the progress bar on the first interaction with an object.
        if (this.activities[div_id] === 1) {
            this.total++;
            let val = (100 * this.total) / this.possible;
            $("#scprogresstotal").text(this.total);
            $("#scprogressposs").text(this.possible);
            $("#subchapterprogress").progressbar("option", "value", val);
            if (
                val == 100.0 &&
                $("#completionButton").text().toLowerCase() ===
                    "mark as completed"
            ) {
                $("#completionButton").click();
            }
        }
    }
}

var pageProgressTracker = {};

function handlePageSetup() {
    var mess;
    if (eBookConfig.useRunestoneServices) {
        jQuery.get(eBookConfig.ajaxURL + "set_tz_offset", {
            timezoneoffset: new Date().getTimezoneOffset() / 60,
        });
    }

    if (eBookConfig.isLoggedIn) {
        mess = `username: ${eBookConfig.username}`;
        if (!eBookConfig.isInstructor) {
            $("#ip_dropdown_link").remove();
        }
        $(document).trigger("runestone:login");
        addReadingList();
        timedRefresh();
    } else {
        mess = "Not logged in";
        $(document).trigger("runestone:logout");
    }
    $(".loggedinuser").html(mess);

    pageProgressTracker = new PageProgressBar(eBookConfig.activities);
    notifyRunestoneComponents();
}

function setupNavbarLoggedIn() {
    $("#profilelink").show();
    $("#passwordlink").show();
    $("#registerlink").hide();
    $("li.loginout").html(
        '<a href="' + eBookConfig.app + '/default/user/logout">Log Out</a>'
    );
}
$(document).bind("runestone:login", setupNavbarLoggedIn);

function setupNavbarLoggedOut() {
    console.log("setup navbar for logged out");
    $("#registerlink").show();
    $("#profilelink").hide();
    $("#passwordlink").hide();
    $("#ip_dropdown_link").hide();
    $("li.loginout").html(
        '<a href="' + eBookConfig.app + '/default/user/login">Login</a>'
    );
    $(".footer").html("user not logged in");
}
$(document).bind("runestone:logout", setupNavbarLoggedOut);

function notifyRunestoneComponents() {
    // Runestone components wait until login process is over to load components because of storage issues
    $(document).trigger("runestone:login-complete");
    if (typeof $pjQ !== "undefined")
        $pjQ(document).trigger("runestone:login-complete"); // for parsons components which are using a different version of jQuery
}

// initialize stuff
$(document).ready(function () {
    if (eBookConfig) {
        handlePageSetup();
    } else {
        if (typeof eBookConfig === "undefined") {
            console.log(
                "eBookConfig is not defined.  This page must not be set up for Runestone"
            );
        }
    }
});

// misc stuff
// todo:  This could be further distributed but making a video.js file just for one function seems dumb.
$(document).ready(function () {
    // add the video play button overlay image
    $(".video-play-overlay").each(function () {
        $(this).css(
            "background-image",
            "url('{{pathto('_static/play_overlay_icon.png', 1)}}')"
        );
    });

    // This function is needed to allow the dropdown search bar to work;
    // The default behaviour is that the dropdown menu closes when something in
    // it (like the search bar) is clicked
    $(function () {
        // Fix input element click problem
        $(".dropdown input, .dropdown label").click(function (e) {
            e.stopPropagation();
        });
    });
});


/***/ }),

/***/ "./runestone/common/js/renderComponent.js":
/*!************************************************!*\
  !*** ./runestone/common/js/renderComponent.js ***!
  \************************************************/
/*! exports provided: renderRunestoneComponent, createTimedComponent */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "renderRunestoneComponent", function() { return renderRunestoneComponent; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createTimedComponent", function() { return createTimedComponent; });
/* harmony import */ var _mchoice_js_timedmc__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../mchoice/js/timedmc */ "./runestone/mchoice/js/timedmc.js");


function renderRunestoneComponent(componentSrc, whereDiv, moreOpts) {
    /**
     *  The easy part is adding the componentSrc to the existing div.
     *  The tedious part is calling the right functions to turn the
     *  source into the actual component.
     */
    let patt = /..\/_images/g;
    componentSrc = componentSrc.replace(
        patt,
        `${eBookConfig.app}/books/published/${eBookConfig.basecourse}/_images`
    );
    jQuery(`#${whereDiv}`).html(componentSrc);

    if (typeof window.edList === "undefined") {
        window.edList = {};
    }

    let componentKind = $($(`#${whereDiv} [data-component]`)[0]).data(
        "component"
    );
    let opt = {};
    opt.orig = jQuery(`#${whereDiv} [data-component]`)[0];
    if (opt.orig) {
        opt.lang = $(opt.orig).data("lang");
        opt.useRunestoneServices = true;
        opt.graderactive = false;
        opt.python3 = true;
        if (typeof moreOpts !== "undefined") {
            for (let key in moreOpts) {
                opt[key] = moreOpts[key];
            }
        }
    }

    if (typeof component_factory === "undefined") {
        alert(
            "Error:  Missing the component factory!  Clear you browser cache."
        );
    } else {
        if (
            !window.component_factory[componentKind] &&
            !jQuery(`#${whereDiv}`).html()
        ) {
            jQuery(`#${whereDiv}`).html(
                `<p>Preview not available for ${componentKind}</p>`
            );
        } else {
            let res = window.component_factory[componentKind](opt);
            if (componentKind === "activecode") {
                if (moreOpts.multiGrader) {
                    window.edList[
                        `${moreOpts.gradingContainer} ${res.divid}`
                    ] = res;
                } else {
                    window.edList[res.divid] = res;
                }
            }
        }
    }
}

function createTimedComponent(componentSrc, moreOpts) {
    /* The important distinction is that the component does not really need to be rendered
    into the page, in fact, due to the async nature of getting the source the list of questions
    is made and the original html is replaced by the look of the exam.
    */

    let patt = /..\/_images/g;
    componentSrc = componentSrc.replace(
        patt,
        `${eBookConfig.app}/books/published/${eBookConfig.basecourse}/_images`
    );

    let componentKind = $($(componentSrc).find("[data-component]")[0]).data(
        "component"
    );

    let origId = $(componentSrc).find("[data-component]").first().attr("id");

    // Double check -- if the component source is not in the DOM, then briefly add it
    // and call the constructor.
    let hdiv;
    if (!document.getElementById(origId)) {
        hdiv = $("<div/>", {
            css: { display: "none" },
        }).appendTo("body");
        hdiv.html(componentSrc);
    }
    // at this point hdiv is a jquery object

    let ret;
    let opts = {
        orig: document.getElementById(origId),
        timed: true,
    };
    if (typeof moreOpts !== "undefined") {
        for (let key in moreOpts) {
            opts[key] = moreOpts[key];
        }
    }

    if (componentKind in window.component_factory) {
        ret = window.component_factory[componentKind](opts);
    }

    let rdict = {};
    rdict.question = ret;
    return rdict;
}


/***/ }),

/***/ "./runestone/common/js/runestonebase.js":
/*!**********************************************!*\
  !*** ./runestone/common/js/runestonebase.js ***!
  \**********************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return RunestoneBase; });
/* harmony import */ var _bookfuncs_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./bookfuncs.js */ "./runestone/common/js/bookfuncs.js");
/* ********************************
 * |docname| - Runestone Base Class
 * ********************************
 * All runestone components should inherit from RunestoneBase. In addition all runestone components should do the following things:
 *
 * 1.   Ensure that they are wrapped in a div with the class runestone
 * 2.   Write their source AND their generated html to the database if the database is configured
 * 3.   Properly save and restore their answers using the checkServer mechanism in this base class. Each component must provide an implementation of:
 *
 *      -    checkLocalStorage
 *      -    setLocalStorage
 *      -    restoreAnswers
 *      -    disableInteraction
 *
 * 4.   provide a Selenium based unit test
 */


//import "./../styles/runestone-custom-sphinx-bootstrap.css";

class RunestoneBase {
    constructor(opts) {
        this.optional = false;
        if (opts) {
            this.sid = opts.sid;
            this.graderactive = opts.graderactive;
            this.showfeedback = true;
            if (opts.timed) {
                this.isTimed = true;
            }
            if (opts.enforceDeadline) {
                this.deadline = opts.deadline;
            }
            if ($(opts.orig).data("optional")) {
                this.optional = true;
            } else {
                this.optional = false;
            }
            if (opts.selector_id) {
                this.selector_id = opts.selector_id;
            }
            if (typeof opts.assessmentTaken !== "undefined") {
                this.assessmentTaken = opts.assessmentTaken;
            } else {
                // default to true as this opt is only provided from a timedAssessment
                this.assessmentTaken = true;
            }
            if (typeof opts.timedWrapper !== "undefined") {
                this.timedWrapper = opts.timedWrapper;
            } else {
                this.timedWrapper = null;
            }
            if ($(opts.orig).data("question_label")) {
                this.question_label = $(opts.orig).data("question_label");
            }
        }
        this.jsonHeaders = new Headers({
            "Content-type": "application/json; charset=utf-8",
            Accept: "application/json",
        });
    }

    // .. _logBookEvent:
    //
    // logBookEvent
    // ------------
    // This function sends the provided ``eventInfo`` to the `hsblog endpoint` of the server. Awaiting this function returns either ``undefined`` (if Runestone services are not available) or the data returned by the server as a JavaScript object (already JSON-decoded).
    async logBookEvent(eventInfo) {
        if (this.graderactive) {
            return;
        }
        let post_return;
        eventInfo.course = eBookConfig.course;
        eventInfo.clientLoginStatus = eBookConfig.isLoggedIn;
        eventInfo.timezoneoffset = new Date().getTimezoneOffset() / 60;
        if (this.percent) {
            eventInfo.percent = this.percent;
        }
        if (eBookConfig.useRunestoneServices && eBookConfig.logLevel > 0) {
            let request = new Request(eBookConfig.ajaxURL + "hsblog", {
                method: "POST",
                headers: this.jsonHeaders,
                body: JSON.stringify(eventInfo),
            });
            let response = await fetch(request);
            if (!response.ok) {
                throw new Error("Failed to save the log entry");
            }
            post_return = response.json();
        }
        if (!this.isTimed || eBookConfig.debug) {
            console.log("logging event " + JSON.stringify(eventInfo));
        }
        if (
            typeof _bookfuncs_js__WEBPACK_IMPORTED_MODULE_0__["pageProgressTracker"].updateProgress === "function" &&
            eventInfo.act != "edit" &&
            this.optional == false
        ) {
            _bookfuncs_js__WEBPACK_IMPORTED_MODULE_0__["pageProgressTracker"].updateProgress(eventInfo.div_id);
        }
        return post_return;
    }

    // .. _logRunEvent:
    //
    // logRunEvent
    // -----------
    // This function sends the provided ``eventInfo`` to the `runlog endpoint`. When awaited, this function returns the data (decoded from JSON) the server sent back.
    async logRunEvent(eventInfo) {
        let post_promise = "done";
        if (this.graderactive) {
            return;
        }
        eventInfo.course = eBookConfig.course;
        eventInfo.clientLoginStatus = eBookConfig.isLoggedIn;
        eventInfo.timezoneoffset = new Date().getTimezoneOffset() / 60;
        if (this.forceSave || "to_save" in eventInfo === false) {
            eventInfo.save_code = "True";
        }
        if (eBookConfig.useRunestoneServices && eBookConfig.logLevel > 0) {
            let request = new Request(eBookConfig.ajaxURL + "runlog.json", {
                method: "POST",
                headers: this.jsonHeaders,
                body: JSON.stringify(eventInfo),
            });
            let response = await fetch(request);
            if (!response.ok) {
                throw new Error("Failed to log the run");
            }
            post_promise = await response.json();
        }
        if (!this.isTimed || eBookConfig.debug) {
            console.log("running " + JSON.stringify(eventInfo));
        }
        if (
            typeof _bookfuncs_js__WEBPACK_IMPORTED_MODULE_0__["pageProgressTracker"].updateProgress === "function" &&
            this.optional == false
        ) {
            _bookfuncs_js__WEBPACK_IMPORTED_MODULE_0__["pageProgressTracker"].updateProgress(eventInfo.div_id);
        }
        return post_promise;
    }
    /* Checking/loading from storage 
    **WARNING:**  DO NOT `await` this function!
    This function, although async, does not explicitly resolve its promise by returning a value.  The reason for this is because it is called by the constructor for nearly every component.  In Javascript constructors cannot be async! 
    
    One of the recommended ways to handle the async requirements from within a constructor is to use an attribute as a promise and resolve that attribute at the appropriate time.
    */
    async checkServer(eventInfo) {
        // Check if the server has stored answer
        let self = this;
        this.checkServerComplete = new Promise(function (resolve, reject) {
            self.csresolver = resolve;
        });
        if (this.useRunestoneServices || this.graderactive) {
            let data = {};
            data.div_id = this.divid;
            data.course = eBookConfig.course;
            data.event = eventInfo;
            if (this.graderactive && this.deadline) {
                data.deadline = this.deadline;
                data.rawdeadline = this.rawdeadline;
                data.tzoff = this.tzoff;
            }
            if (this.sid) {
                data.sid = this.sid;
            }
            if (!eBookConfig.practice_mode && this.assessmentTaken) {
                let request = new Request(
                    eBookConfig.ajaxURL + "getAssessResults",
                    {
                        method: "POST",
                        body: JSON.stringify(data),
                        headers: this.jsonHeaders,
                    }
                );
                try {
                    let response = await fetch(request);
                    data = await response.json();
                    this.repopulateFromStorage(data);
                    this.csresolver("server");
                } catch (err) {
                    try {
                        this.checkLocalStorage();
                    } catch (err) {
                        console.log(err);
                    }
                }
            } else {
                this.loadData({});
                this.csresolver("not taken");
            }
        } else {
            this.checkLocalStorage(); // just go right to local storage
            this.csresolver("local");
        }
    }

    loadData(data) {
        // for most classes, loadData doesn't do anything. But for Parsons, and perhaps others in the future,
        // initialization can happen even when there's no history to be loaded
        return null;
    }

    /**
     * repopulateFromStorage is called after a successful API call is made to ``getAssessResults`` in
     * the checkServer method in this class
     *
     * ``restoreAnswers,`` ``setLocalStorage`` and ``checkLocalStorage`` are defined in the child classes.
     *
     * @param {*} data - a JSON object representing the data needed to restore a previous answer for a component
     * @param {*} status - the http status
     * @param {*} whatever - ignored
     */
    repopulateFromStorage(data) {
        // decide whether to use the server's answer (if there is one) or to load from storage
        if (data !== null && this.shouldUseServer(data)) {
            this.restoreAnswers(data);
            this.setLocalStorage(data);
        } else {
            this.checkLocalStorage();
        }
    }
    shouldUseServer(data) {
        // returns true if server data is more recent than local storage or if server storage is correct
        if (
            data.correct === "T" ||
            localStorage.length === 0 ||
            this.graderactive === true ||
            this.isTimed
        ) {
            return true;
        }
        let ex = localStorage.getItem(this.localStorageKey());
        if (ex === null) {
            return true;
        }
        let storedData;
        try {
            storedData = JSON.parse(ex);
        } catch (err) {
            // error while parsing; likely due to bad value stored in storage
            console.log(err.message);
            localStorage.removeItem(this.localStorageKey());
            // definitely don't want to use local storage here
            return true;
        }
        if (data.answer == storedData.answer) return true;
        let storageDate = new Date(storedData.timestamp);
        let serverDate = new Date(data.timestamp);
        return serverDate >= storageDate;
    }
    // Return the key which to be used when accessing local storage.
    localStorageKey() {
        return (
            eBookConfig.email +
            ":" +
            eBookConfig.course +
            ":" +
            this.divid +
            "-given"
        );
    }
    addCaption(elType) {
        //someElement.parentNode.insertBefore(newElement, someElement.nextSibling);
        if (!this.isTimed) {
            var capDiv = document.createElement("p");
            if (this.question_label) {
                this.caption = `Activity: ${this.question_label} ${this.caption}  <span class="runestone_caption_divid">(${this.divid})</span>`;
                $(capDiv).html(this.caption);
                $(capDiv).addClass(`${elType}_caption`);
            } else {
                $(capDiv).html(this.caption + " (" + this.divid + ")");
                $(capDiv).addClass(`${elType}_caption`);
                $(capDiv).addClass(`${elType}_caption_text`);
            }
            this.capDiv = capDiv;
            //this.outerDiv.parentNode.insertBefore(capDiv, this.outerDiv.nextSibling);
            this.containerDiv.appendChild(capDiv);
        }
    }

    hasUserActivity() {
        return this.isAnswered;
    }

    checkCurrentAnswer() {
        console.log(
            "Each component should provide an implementation of checkCurrentAnswer"
        );
    }

    async logCurrentAnswer() {
        console.log(
            "Each component should provide an implementation of logCurrentAnswer"
        );
    }
    renderFeedback() {
        console.log(
            "Each component should provide an implementation of renderFeedback"
        );
    }
    disableInteraction() {
        console.log(
            "Each component should provide an implementation of disableInteraction"
        );
    }
}

window.RunestoneBase = RunestoneBase;


/***/ }),

/***/ "./runestone/common/js/user-highlights.js":
/*!************************************************!*\
  !*** ./runestone/common/js/user-highlights.js ***!
  \************************************************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _runestonebase__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./runestonebase */ "./runestone/common/js/runestonebase.js");
/* harmony import */ var _css_user_highlights_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../css/user-highlights.css */ "./runestone/common/css/user-highlights.css");
/* harmony import */ var _css_user_highlights_css__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_user_highlights_css__WEBPACK_IMPORTED_MODULE_1__);
/*global variable declarations*/






var urlList;
var extendType;
var rsb = new _runestonebase__WEBPACK_IMPORTED_MODULE_0__["default"]();

function getCompletions() {
    // Get the completion status
    if (
        window.location.href.match(
            /(index.html|toctree.html|genindex.html|navhelp.html|toc.html|assignments.html|Exercises.html)/
        )
    ) {
        return;
    }

    var currentPathname = window.location.pathname;
    if (currentPathname.indexOf("?") !== -1) {
        currentPathname = currentPathname.substring(
            0,
            currentPathname.lastIndexOf("?")
        );
    }
    var data = { lastPageUrl: currentPathname };
    jQuery
        .ajax({
            url: eBookConfig.ajaxURL + "getCompletionStatus",
            data: data,
            async: false,
        })
        .done(function (data) {
            if (data != "None") {
                var completionData = $.parseJSON(data);
                var completionClass, completionMsg;
                if (completionData[0].completionStatus == 1) {
                    completionClass = "buttonConfirmCompletion";
                    completionMsg =
                        "<i class='glyphicon glyphicon-ok'></i> Completed. Well Done!";
                } else {
                    completionClass = "buttonAskCompletion";
                    completionMsg = "Mark as Completed";
                }
                $("#main-content").append(
                    '<div style="text-align:center"><button class="btn btn-lg ' +
                        completionClass +
                        '" id="completionButton">' +
                        completionMsg +
                        "</button></div>"
                );
            }
        });
}

function showLastPositionBanner() {
    var lastPositionVal = $.getUrlVar("lastPosition");
    if (typeof lastPositionVal !== "undefined") {
        $("body").append(
            '<img src="../_static/last-point.png" style="position:absolute; padding-top:55px; left: 10px; top: ' +
                parseInt(lastPositionVal) +
                'px;"/>'
        );
        $("html, body").animate({ scrollTop: parseInt(lastPositionVal) }, 1000);
    }
}

function addNavigationAndCompletionButtons() {
    if (
        window.location.href.match(
            /(index.html|genindex.html|navhelp.html|toc.html|assignments.html|Exercises.html|toctree.html)/
        )
    ) {
        return;
    }
    var navLinkBgRightHiddenPosition = -$("#navLinkBgRight").outerWidth() - 5;
    var navLinkBgRightHalfOpen;
    var navLinkBgRightFullOpen = 0;

    if ($("#completionButton").hasClass("buttonAskCompletion")) {
        navLinkBgRightHalfOpen = navLinkBgRightHiddenPosition + 70;
    } else if ($("#completionButton").hasClass("buttonConfirmCompletion")) {
        navLinkBgRightHalfOpen = 0;
    }
    var relationsNextIconInitialPosition = $("#relations-next").css("right");
    var relationsNextIconNewPosition = -(navLinkBgRightHiddenPosition + 35);

    $("#navLinkBgRight").css("right", navLinkBgRightHiddenPosition).show();
    var navBgShown = false;
    $(window).scroll(function () {
        if (
            $(window).scrollTop() + $(window).height() ==
            $(document).height()
        ) {
            $("#navLinkBgRight").animate(
                { right: navLinkBgRightHalfOpen },
                200
            );
            $("#navLinkBgLeft").animate({ left: "0px" }, 200);
            if ($("#completionButton").hasClass("buttonConfirmCompletion")) {
                $("#relations-next").animate(
                    { right: relationsNextIconNewPosition },
                    200
                );
            }
            navBgShown = true;
        } else if (navBgShown) {
            $("#navLinkBgRight").animate(
                { right: navLinkBgRightHiddenPosition },
                200
            );
            $("#navLinkBgLeft").animate({ left: "-65px" }, 200);
            $("#relations-next").animate({
                right: relationsNextIconInitialPosition,
            });
            navBgShown = false;
        }
    });

    var completionFlag = 0;
    if ($("#completionButton").hasClass("buttonAskCompletion")) {
        completionFlag = 0;
    } else {
        completionFlag = 1;
    }
    $("#completionButton").on("click", function () {
        if ($(this).hasClass("buttonAskCompletion")) {
            $(this)
                .removeClass("buttonAskCompletion")
                .addClass("buttonConfirmCompletion")
                .html(
                    "<i class='glyphicon glyphicon-ok'></i> Completed. Well Done!"
                );
            $("#navLinkBgRight").animate({ right: navLinkBgRightFullOpen });
            $("#relations-next").animate({
                right: relationsNextIconNewPosition,
            });
            navLinkBgRightHalfOpen = 0;
            completionFlag = 1;
        } else if ($(this).hasClass("buttonConfirmCompletion")) {
            $(this)
                .removeClass("buttonConfirmCompletion")
                .addClass("buttonAskCompletion")
                .html("Mark as Completed");
            navLinkBgRightHalfOpen = navLinkBgRightHiddenPosition + 70;
            $("#navLinkBgRight").animate({ right: navLinkBgRightHalfOpen });
            $("#relations-next").animate({
                right: relationsNextIconInitialPosition,
            });
            completionFlag = 0;
        }
        processPageState(completionFlag);
    });

    $(window).on("beforeunload", function (e) {
        if (completionFlag == 0) {
            processPageState(completionFlag);
        }
    });
}

function decorateTableOfContents() {
    if (
        window.location.href.toLowerCase().indexOf("toc.html") != -1 ||
        window.location.href.toLowerCase().indexOf("index.html") != -1
    ) {
        jQuery.get(eBookConfig.ajaxURL + "getAllCompletionStatus", function (
            data
        ) {
            var subChapterList;
            if (data != "None") {
                subChapterList = $.parseJSON(data);

                var allSubChapterURLs = $("#main-content div li a");
                $.each(subChapterList, function (index, item) {
                    for (var s = 0; s < allSubChapterURLs.length; s++) {
                        if (
                            allSubChapterURLs[s].href.indexOf(
                                item.chapterName + "/" + item.subChapterName
                            ) != -1
                        ) {
                            if (item.completionStatus == 1) {
                                $(allSubChapterURLs[s].parentElement)
                                    .addClass("completed")
                                    .append(
                                        '<span class="infoTextCompleted">- Completed this topic on ' +
                                            item.endDate +
                                            "</span>"
                                    )
                                    .children()
                                    .first()
                                    .hover(
                                        function () {
                                            $(this)
                                                .next(".infoTextCompleted")
                                                .show();
                                        },
                                        function () {
                                            $(this)
                                                .next(".infoTextCompleted")
                                                .hide();
                                        }
                                    );
                            } else if (item.completionStatus == 0) {
                                $(allSubChapterURLs[s].parentElement)
                                    .addClass("active")
                                    .append(
                                        '<span class="infoTextActive">Last read this topic on ' +
                                            item.endDate +
                                            "</span>"
                                    )
                                    .children()
                                    .first()
                                    .hover(
                                        function () {
                                            $(this)
                                                .next(".infoTextActive")
                                                .show();
                                        },
                                        function () {
                                            $(this)
                                                .next(".infoTextActive")
                                                .hide();
                                        }
                                    );
                            }
                        }
                    }
                });
            }
        });
        var data = { course: eBookConfig.course };
        jQuery.get(eBookConfig.ajaxURL + "getlastpage", data, function (data) {
            var lastPageData;
            if (data != "None") {
                lastPageData = $.parseJSON(data);
                if (lastPageData[0].lastPageChapter != null) {
                    $("#continue-reading")
                        .show()
                        .html(
                            '<div id="jump-to-chapter" class="alert alert-info" ><strong>You were Last Reading:</strong> ' +
                                lastPageData[0].lastPageChapter +
                                (lastPageData[0].lastPageSubchapter
                                    ? " &gt; " +
                                      lastPageData[0].lastPageSubchapter
                                    : "") +
                                ' <a href="' +
                                lastPageData[0].lastPageUrl +
                                "?lastPosition=" +
                                lastPageData[0].lastPageScrollLocation +
                                '">Continue Reading</a></div>'
                        );
                }
            }
        });
    }
}

function enableCompletions() {
    getCompletions();
    showLastPositionBanner();
    addNavigationAndCompletionButtons();
    decorateTableOfContents();
}

// call enable user highlights after login
$(document).bind("runestone:login", enableCompletions);

function processPageState(completionFlag) {
    /*Log last page visited*/
    var currentPathname = window.location.pathname;
    if (currentPathname.indexOf("?") !== -1) {
        currentPathname = currentPathname.substring(
            0,
            currentPathname.lastIndexOf("?")
        );
    }
    var data = {
        lastPageUrl: currentPathname,
        lastPageScrollLocation: $(window).scrollTop(),
        completionFlag: completionFlag,
        course: eBookConfig.course,
    };
    $(document).ajaxError(function (e, jqhxr, settings, exception) {
        console.log("Request Failed for " + settings.url);
        console.log(e);
    });
    jQuery.ajax({
        url: eBookConfig.ajaxURL + "updatelastpage",
        data: data,
        async: true,
    });
}

$.extend({
    getUrlVars: function () {
        var vars = [],
            hash;
        var hashes = window.location.search
            .slice(window.location.search.indexOf("?") + 1)
            .split("&");
        for (var i = 0; i < hashes.length; i++) {
            hash = hashes[i].split("=");
            vars.push(hash[0]);
            vars[hash[0]] = hash[1];
        }
        return vars;
    },
    getUrlVar: function (name) {
        return $.getUrlVars()[name];
    },
});


/***/ }),

/***/ "./runestone/datafile/css/datafile.css":
/*!*********************************************!*\
  !*** ./runestone/datafile/css/datafile.css ***!
  \*********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var api = __webpack_require__(/*! ../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
            var content = __webpack_require__(/*! !../../../node_modules/css-loader/dist/cjs.js!./datafile.css */ "./node_modules/css-loader/dist/cjs.js!./runestone/datafile/css/datafile.css");

            content = content.__esModule ? content.default : content;

            if (typeof content === 'string') {
              content = [[module.i, content, '']];
            }

var options = {};

options.insert = "head";
options.singleton = false;

var update = api(content, options);



module.exports = content.locals || {};

/***/ }),

/***/ "./runestone/datafile/js/datafile.js":
/*!*******************************************!*\
  !*** ./runestone/datafile/js/datafile.js ***!
  \*******************************************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _common_js_runestonebase__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/js/runestonebase */ "./runestone/common/js/runestonebase.js");
/* harmony import */ var _css_datafile_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../css/datafile.css */ "./runestone/datafile/css/datafile.css");
/* harmony import */ var _css_datafile_css__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_datafile_css__WEBPACK_IMPORTED_MODULE_1__);
/*==========================================
=======     Master datafile.js      ========
============================================
===     This file contains the JS for    ===
===   the Runestone Datafile component.  ===
============================================
===              Created by              ===
===           Isaiah Mayerchak           ===
===                6/8/15                ===
==========================================*/





var dfList = {}; // Dictionary that contains all instances of Datafile objects

class DataFile extends _common_js_runestonebase__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(opts) {
        super(opts);
        var orig = opts.orig; // entire <pre> element that will be replaced by new HTML
        this.origElem = orig;
        this.divid = orig.id;
        this.dataEdit = false;
        this.isImage = $(orig).data("isimage");
        if ($(this.origElem).data("edit") === true) {
            this.dataEdit = true;
        }
        this.displayClass = "block"; // Users can specify the non-edit component to be hidden--default is not hidden
        if ($(this.origElem).is("[data-hidden]")) {
            this.displayClass = "none";
        }
        // Users can specify numbers of rows/columns when editing is true
        this.numberOfRows = $(this.origElem).data("rows");
        this.numberOfCols = $(this.origElem).data("cols");
        if (!this.isImage) {
            if (this.dataEdit) {
                this.createTextArea();
            } else {
                this.createPre();
            }
        }
    }
    /*=====================================
    == Create either <pre> or <textarea> ==
    ==  depending on if editing is true  ==
    ==================================*/
    createPre() {
        this.preContainer = document.createElement("pre");
        this.preContainer.id = this.divid;
        $(this.preContainer).attr({ style: "display: " + this.displayClass });
        this.preContainer.innerHTML = this.origElem.innerHTML;
        $(this.origElem).replaceWith(this.preContainer);
    }
    createTextArea() {
        this.textAreaContainer = document.createElement("textarea");
        this.textAreaContainer.id = this.divid;
        this.textAreaContainer.rows = this.numberOfRows;
        this.textAreaContainer.cols = this.numberOfCols;
        this.textAreaContainer.innerHTML = this.origElem.innerHTML;
        $(this.textAreaContainer).addClass("datafiletextfield");
        $(this.origElem).replaceWith(this.textAreaContainer);
    }
}

/*=================================
== Find the custom HTML tags and ==
==   execute our code on them    ==
=================================*/
$(document).ready(function () {
    $("[data-component=datafile]").each(function (index) {
        try {
            dfList[this.id] = new DataFile({ orig: this });
        } catch (err) {
            console.log(`Error rendering DataFile ${this.id}`);
        }
    });
});

if (typeof window.component_factory === "undefined") {
    window.component_factory = {};
}

window.component_factory.datafile = function (opts) {
    return new DataFile(opts);
};


/***/ }),

/***/ "./runestone/dragndrop/css/dragndrop.css":
/*!***********************************************!*\
  !*** ./runestone/dragndrop/css/dragndrop.css ***!
  \***********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var api = __webpack_require__(/*! ../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
            var content = __webpack_require__(/*! !../../../node_modules/css-loader/dist/cjs.js!./dragndrop.css */ "./node_modules/css-loader/dist/cjs.js!./runestone/dragndrop/css/dragndrop.css");

            content = content.__esModule ? content.default : content;

            if (typeof content === 'string') {
              content = [[module.i, content, '']];
            }

var options = {};

options.insert = "head";
options.singleton = false;

var update = api(content, options);



module.exports = content.locals || {};

/***/ }),

/***/ "./runestone/dragndrop/js/dragndrop-i18n.en.js":
/*!*****************************************************!*\
  !*** ./runestone/dragndrop/js/dragndrop-i18n.en.js ***!
  \*****************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

$.i18n().load({
    en: {
        msg_dragndrop_correct_answer: "You are correct!",
        msg_dragndrop_incorrect_answer:
            "Incorret. You got $1 correct and $2 incorrect out of $3. You left $4 blank.",
        msg_dragndrop_check_me: "Check me",
        msg_dragndrop_reset: "Reset",
    },
});


/***/ }),

/***/ "./runestone/dragndrop/js/dragndrop-i18n.pt-br.js":
/*!********************************************************!*\
  !*** ./runestone/dragndrop/js/dragndrop-i18n.pt-br.js ***!
  \********************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

$.i18n().load({
    "pt-br": {
        msg_dragndrop_correct_answer: "Correto!",
        msg_dragndrop_incorrect_answer:
            "Incorreto. Você teve $1 correto(s) e $2 incorreto(s) de $3. Você deixou $4 em branco.",
        msg_dragndrop_check_me: "Verificar",
        msg_dragndrop_reset: "Resetar",
    },
});


/***/ }),

/***/ "./runestone/dragndrop/js/dragndrop.js":
/*!*********************************************!*\
  !*** ./runestone/dragndrop/js/dragndrop.js ***!
  \*********************************************/
/*! exports provided: ddList, default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ddList", function() { return ddList; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return DragNDrop; });
/* harmony import */ var _common_js_runestonebase_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/js/runestonebase.js */ "./runestone/common/js/runestonebase.js");
/* harmony import */ var _css_dragndrop_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../css/dragndrop.css */ "./runestone/dragndrop/css/dragndrop.css");
/* harmony import */ var _css_dragndrop_css__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_dragndrop_css__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _dragndrop_i18n_en_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./dragndrop-i18n.en.js */ "./runestone/dragndrop/js/dragndrop-i18n.en.js");
/* harmony import */ var _dragndrop_i18n_en_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_dragndrop_i18n_en_js__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var _dragndrop_i18n_pt_br_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./dragndrop-i18n.pt-br.js */ "./runestone/dragndrop/js/dragndrop-i18n.pt-br.js");
/* harmony import */ var _dragndrop_i18n_pt_br_js__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_dragndrop_i18n_pt_br_js__WEBPACK_IMPORTED_MODULE_3__);
/*==========================================
=======     Master dragndrop.js     ========
============================================
===     This file contains the JS for    ===
=== the Runestone Drag n drop component. ===
============================================
===              Created by              ===
===           Isaiah Mayerchak           ===
===                7/6/15                ===
===              Brad MIller             ===
===                2/7/19                ===
==========================================*/







var ddList = {}; // Dictionary that contains all instances of dragndrop objects

class DragNDrop extends _common_js_runestonebase_js__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(opts) {
        super(opts);
        var orig = opts.orig; // entire <ul> element that will be replaced by new HTML
        this.origElem = orig;
        this.divid = orig.id;
        this.useRunestoneServices = opts.useRunestoneServices;
        this.random = false;
        if ($(this.origElem).is("[data-random]")) {
            this.random = true;
        }
        this.feedback = "";
        this.dragPairArray = [];
        this.question = "";
        this.populate(); // Populates this.dragPairArray, this.feedback and this.question
        this.createNewElements();
        this.caption = "Drag-N-Drop";
        this.addCaption("runestone");
    }
    /*======================
    === Update variables ===
    ======================*/
    populate() {
        for (var i = 0; i < this.origElem.childNodes.length; i++) {
            if (
                $(this.origElem.childNodes[i]).data("component") === "dropzone"
            ) {
                var tmp = $(this.origElem).find(
                    `#${$(this.origElem.childNodes[i]).attr("for")}`
                )[0];
                var replaceSpan = document.createElement("span");
                replaceSpan.innerHTML = tmp.innerHTML;
                replaceSpan.id = this.divid + tmp.id;
                $(replaceSpan).attr("draggable", "true");
                $(replaceSpan).addClass("draggable-drag");
                var otherReplaceSpan = document.createElement("span");
                otherReplaceSpan.innerHTML = this.origElem.childNodes[
                    i
                ].innerHTML;
                $(otherReplaceSpan).addClass("draggable-drop");
                this.setEventListeners(replaceSpan, otherReplaceSpan);
                var tmpArr = [];
                tmpArr.push(replaceSpan);
                tmpArr.push(otherReplaceSpan);
                this.dragPairArray.push(tmpArr);
            } else if (
                $(this.origElem.childNodes[i]).data("component") === "question"
            ) {
                this.question = this.origElem.childNodes[i].innerHTML;
            } else if (
                $(this.origElem.childNodes[i]).data("component") === "feedback"
            ) {
                this.feedback = this.origElem.childNodes[i].innerHTML;
            }
        }
    }
    /*========================================
    == Create new HTML elements and replace ==
    ==      original element with them      ==
    ========================================*/
    createNewElements() {
        this.containerDiv = document.createElement("div");
        $(this.containerDiv).addClass(
            "alert alert-warning draggable-container"
        );
        $(this.containerDiv).text(this.question);
        this.containerDiv.appendChild(document.createElement("br"));
        this.dragDropWrapDiv = document.createElement("div"); // Holds the draggables/dropzones, prevents feedback from bleeding in
        $(this.dragDropWrapDiv).css("display", "block");
        this.containerDiv.appendChild(this.dragDropWrapDiv);
        this.draggableDiv = document.createElement("div");
        $(this.draggableDiv).addClass("rsdraggable dragzone");
        this.addDragDivListeners();
        this.dropZoneDiv = document.createElement("div");
        $(this.dropZoneDiv).addClass("rsdraggable");
        this.dragDropWrapDiv.appendChild(this.draggableDiv);
        this.dragDropWrapDiv.appendChild(this.dropZoneDiv);
        this.createButtons();
        this.checkServer("dragNdrop");
    }
    finishSettingUp() {
        this.appendReplacementSpans();
        this.renderFeedbackDiv();
        $(this.origElem).replaceWith(this.containerDiv);
        if (!this.hasStoredDropzones) {
            this.minheight = $(this.draggableDiv).height();
        }
        this.draggableDiv.style.minHeight = this.minheight.toString() + "px";
        if ($(this.dropZoneDiv).height() > this.minheight) {
            this.dragDropWrapDiv.style.minHeight =
                $(this.dropZoneDiv).height().toString() + "px";
        } else {
            this.dragDropWrapDiv.style.minHeight =
                this.minheight.toString() + "px";
        }
    }
    addDragDivListeners() {
        let self = this;
        this.draggableDiv.addEventListener(
            "dragover",
            function (ev) {
                ev.preventDefault();
                if ($(this.draggableDiv).hasClass("possibleDrop")) {
                    return;
                }
                $(this.draggableDiv).addClass("possibleDrop");
            }.bind(this)
        );
        this.draggableDiv.addEventListener(
            "drop",
            function (ev) {
                self.isAnswered = true;
                ev.preventDefault();
                if ($(this.draggableDiv).hasClass("possibleDrop")) {
                    $(this.draggableDiv).removeClass("possibleDrop");
                }
                var data = ev.dataTransfer.getData("draggableID");
                var draggedSpan = document.getElementById(data);
                if (
                    !$(this.draggableDiv).has(draggedSpan).length &&
                    !this.strangerDanger(draggedSpan)
                ) {
                    // Make sure element isn't already there--prevents erros w/appending child
                    this.draggableDiv.appendChild(draggedSpan);
                }
            }.bind(this)
        );
        this.draggableDiv.addEventListener(
            "dragleave",
            function (e) {
                if (!$(this.draggableDiv).hasClass("possibleDrop")) {
                    return;
                }
                $(this.draggableDiv).removeClass("possibleDrop");
            }.bind(this)
        );
    }
    createButtons() {
        this.buttonDiv = document.createElement("div");
        this.submitButton = document.createElement("button"); // Check me button
        this.submitButton.textContent = $.i18n("msg_dragndrop_check_me");
        $(this.submitButton).attr({
            class: "btn btn-success drag-button",
            name: "do answer",
            type: "button",
        });
        this.submitButton.onclick = function () {
            this.checkCurrentAnswer();
            this.renderFeedback();
            this.logCurrentAnswer(true);
        }.bind(this);
        this.resetButton = document.createElement("button"); // Check me button
        this.resetButton.textContent = $.i18n("msg_dragndrop_reset");
        $(this.resetButton).attr({
            class: "btn btn-default drag-button drag-reset",
            name: "do answer",
        });
        this.resetButton.onclick = function () {
            this.resetDraggables();
        }.bind(this);
        this.buttonDiv.appendChild(this.submitButton);
        this.buttonDiv.appendChild(this.resetButton);
        this.containerDiv.appendChild(this.buttonDiv);
    }
    appendReplacementSpans() {
        this.createIndexArray();
        this.randomizeIndexArray();
        for (let i = 0; i < this.dragPairArray.length; i++) {
            if (this.hasStoredDropzones) {
                if (
                    $.inArray(this.indexArray[i][0], this.pregnantIndexArray) <
                    0
                ) {
                    this.draggableDiv.appendChild(
                        this.dragPairArray[this.indexArray[i]][0]
                    );
                }
            } else {
                this.draggableDiv.appendChild(
                    this.dragPairArray[this.indexArray[i]][0]
                );
            }
        }
        this.randomizeIndexArray();
        for (let i = 0; i < this.dragPairArray.length; i++) {
            if (this.hasStoredDropzones) {
                if (this.pregnantIndexArray[this.indexArray[i]] !== "-1") {
                    this.dragPairArray[this.indexArray[i]][1].appendChild(
                        this.dragPairArray[
                            this.pregnantIndexArray[this.indexArray[i]]
                        ][0]
                    );
                }
            }
            this.dropZoneDiv.appendChild(
                this.dragPairArray[this.indexArray[i]][1]
            );
        }
    }
    setEventListeners(dgSpan, dpSpan) {
        // Adds HTML5 "drag and drop" UI functionality
        let self = this;
        dgSpan.addEventListener("dragstart", function (ev) {
            ev.dataTransfer.setData("draggableID", ev.target.id);
        });
        dgSpan.addEventListener("dragover", function (ev) {
            ev.preventDefault();
        });
        dgSpan.addEventListener(
            "drop",
            function (ev) {
                self.isAnswered = true;
                ev.preventDefault();
                var data = ev.dataTransfer.getData("draggableID");
                var draggedSpan = document.getElementById(data);
                if (
                    this.hasNoDragChild(ev.target) &&
                    draggedSpan != ev.target &&
                    !this.strangerDanger(draggedSpan)
                ) {
                    // Make sure element isn't already there--prevents erros w/appending child
                    this.draggableDiv.appendChild(draggedSpan);
                }
            }.bind(this)
        );
        dpSpan.addEventListener(
            "dragover",
            function (ev) {
                self.isAnswered = true;
                ev.preventDefault();
                if ($(ev.target).hasClass("possibleDrop")) {
                    return;
                }
                if (
                    $(ev.target).hasClass("draggable-drop") &&
                    this.hasNoDragChild(ev.target)
                ) {
                    $(ev.target).addClass("possibleDrop");
                }
            }.bind(this)
        );
        dpSpan.addEventListener("dragleave", function (ev) {
            self.isAnswered = true;
            ev.preventDefault();
            if (!$(ev.target).hasClass("possibleDrop")) {
                return;
            }
            $(ev.target).removeClass("possibleDrop");
        });
        dpSpan.addEventListener(
            "drop",
            function (ev) {
                self.isAnswered = true;
                ev.preventDefault();
                if ($(ev.target).hasClass("possibleDrop")) {
                    $(ev.target).removeClass("possibleDrop");
                }
                var data = ev.dataTransfer.getData("draggableID");
                var draggedSpan = document.getElementById(data);
                if (
                    $(ev.target).hasClass("draggable-drop") &&
                    this.hasNoDragChild(ev.target) &&
                    !this.strangerDanger(draggedSpan)
                ) {
                    // Make sure element isn't already there--prevents erros w/appending child
                    ev.target.appendChild(draggedSpan);
                }
            }.bind(this)
        );
    }
    renderFeedbackDiv() {
        if (!this.feedBackDiv) {
            this.feedBackDiv = document.createElement("div");
            this.feedBackDiv.id = this.divid + "_feedback";
            this.containerDiv.appendChild(document.createElement("br"));
            this.containerDiv.appendChild(this.feedBackDiv);
        }
    }
    /*=======================
    == Auxiliary functions ==
    =======================*/
    strangerDanger(testSpan) {
        // Returns true if the test span doesn't belong to this instance of DragNDrop
        var strangerDanger = true;
        for (var i = 0; i < this.dragPairArray.length; i++) {
            if (testSpan === this.dragPairArray[i][0]) {
                strangerDanger = false;
            }
        }
        return strangerDanger;
    }
    hasNoDragChild(parent) {
        // Ensures that each dropZoneDiv can have only one draggable child
        var counter = 0;
        for (var i = 0; i < parent.childNodes.length; i++) {
            if ($(parent.childNodes[i]).attr("draggable") === "true") {
                counter++;
            }
        }
        if (counter >= 1) {
            return false;
        } else {
            return true;
        }
    }
    createIndexArray() {
        this.indexArray = [];
        for (var i = 0; i < this.dragPairArray.length; i++) {
            this.indexArray.push(i);
        }
    }
    randomizeIndexArray() {
        // Shuffles around indices so the matchable elements aren't in a predictable order
        var currentIndex = this.indexArray.length,
            temporaryValue,
            randomIndex;
        // While there remain elements to shuffle...
        while (currentIndex !== 0) {
            // Pick a remaining element...
            randomIndex = Math.floor(Math.random() * currentIndex);
            currentIndex -= 1;
            // And swap it with the current element.
            temporaryValue = this.indexArray[currentIndex];
            this.indexArray[currentIndex] = this.indexArray[randomIndex];
            this.indexArray[randomIndex] = temporaryValue;
        }
    }
    /*==============================
    == Reset button functionality ==
    ==============================*/
    resetDraggables() {
        for (var i = 0; i < this.dragPairArray.length; i++) {
            for (
                var j = 0;
                j < this.dragPairArray[i][1].childNodes.length;
                j++
            ) {
                if (
                    $(this.dragPairArray[i][1].childNodes[j]).attr(
                        "draggable"
                    ) === "true"
                ) {
                    this.draggableDiv.appendChild(
                        this.dragPairArray[i][1].childNodes[j]
                    );
                }
            }
        }
        this.feedBackDiv.style.display = "none";
    }
    /*===========================
    == Evaluation and feedback ==
    ===========================*/

    checkCurrentAnswer() {
        this.correct = true;
        this.unansweredNum = 0;
        this.incorrectNum = 0;
        this.dragNum = this.dragPairArray.length;
        for (var i = 0; i < this.dragPairArray.length; i++) {
            if (
                !$(this.dragPairArray[i][1]).has(this.dragPairArray[i][0])
                    .length
            ) {
                this.correct = false;
                this.incorrectNum++;
            }
            if (this.hasNoDragChild(this.dragPairArray[i][1])) {
                this.unansweredNum++;
                this.incorrectNum -= 1;
            }
        }
        this.correctNum = this.dragNum - this.incorrectNum - this.unansweredNum;
        this.percent = this.correctNum / this.dragPairArray.length;
        this.setLocalStorage({ correct: this.correct ? "T" : "F" });
    }

    logCurrentAnswer() {
        let answer = this.pregnantIndexArray.join(";");
        this.logBookEvent({
            event: "dragNdrop",
            act: answer,
            answer: answer,
            minHeight: this.minheight,
            div_id: this.divid,
            correct: this.correct,
            correctNum: this.correctNum,
            dragNum: this.dragNum,
        });
    }
    renderFeedback() {
        for (var i = 0; i < this.dragPairArray.length; i++) {
            if (
                !$(this.dragPairArray[i][1]).has(this.dragPairArray[i][0])
                    .length
            ) {
                $(this.dragPairArray[i][1]).addClass("drop-incorrect");
            } else {
                $(this.dragPairArray[i][1]).removeClass("drop-incorrect");
            }
        }

        if (!this.feedBackDiv) {
            this.renderFeedbackDiv();
        }
        this.feedBackDiv.style.display = "block";
        if (this.correct) {
            var msgCorrect = $.i18n("msg_dragndrop_correct_answer");
            $(this.feedBackDiv).html(msgCorrect);
            $(this.feedBackDiv).attr(
                "class",
                "alert alert-info draggable-feedback"
            );
        } else {
            var msgIncorrect = $.i18n(
                $.i18n("msg_dragndrop_incorrect_answer"),
                this.correctNum,
                this.incorrectNum,
                this.dragNum,
                this.unansweredNum
            );
            $(this.feedBackDiv).html(msgIncorrect + " " + this.feedback);
            $(this.feedBackDiv).attr(
                "class",
                "alert alert-danger draggable-feedback"
            );
        }
    }
    /*===================================
    === Checking/restoring from storage ===
    ===================================*/
    restoreAnswers(data) {
        // Restore answers from storage retrieval done in RunestoneBase
        this.hasStoredDropzones = true;
        this.minheight = data.minHeight;
        this.pregnantIndexArray = data.answer.split(";");
        this.finishSettingUp();
    }
    checkLocalStorage() {
        if (this.graderactive) {
            return;
        }
        var storedObj;
        this.hasStoredDropzones = false;
        var len = localStorage.length;
        if (len > 0) {
            var ex = localStorage.getItem(this.localStorageKey());
            if (ex !== null) {
                this.hasStoredDropzones = true;
                try {
                    storedObj = JSON.parse(ex);
                    this.minheight = storedObj.minHeight;
                } catch (err) {
                    // error while parsing; likely due to bad value stored in storage
                    console.log(err.message);
                    localStorage.removeItem(this.localStorageKey());
                    this.hasStoredDropzones = false;
                    this.finishSettingUp();
                    return;
                }
                this.pregnantIndexArray = storedObj.answer.split(";");
                if (this.useRunestoneServices) {
                    // store answer in database
                    var answer = this.pregnantIndexArray.join(";");
                    this.logBookEvent({
                        event: "dragNdrop",
                        act: answer,
                        answer: answer,
                        minHeight: this.minheight,
                        div_id: this.divid,
                        correct: storedObj.correct,
                    });
                }
            }
        }
        this.finishSettingUp();
    }

    setLocalStorage(data) {
        if (data.answer === undefined) {
            // If we didn't load from the server, we must generate the data
            this.pregnantIndexArray = [];
            for (var i = 0; i < this.dragPairArray.length; i++) {
                if (!this.hasNoDragChild(this.dragPairArray[i][1])) {
                    for (var j = 0; j < this.dragPairArray.length; j++) {
                        if (
                            $(this.dragPairArray[i][1]).has(
                                this.dragPairArray[j][0]
                            ).length
                        ) {
                            this.pregnantIndexArray.push(j);
                        }
                    }
                } else {
                    this.pregnantIndexArray.push(-1);
                }
            }
        }
        var timeStamp = new Date();
        var correct = data.correct;
        var storageObj = {
            answer: this.pregnantIndexArray.join(";"),
            minHeight: this.minheight,
            timestamp: timeStamp,
            correct: correct,
        };
        localStorage.setItem(
            this.localStorageKey(),
            JSON.stringify(storageObj)
        );
    }

    disableInteraction() {
        $(this.resetButton).hide();
        for (var i = 0; i < this.dragPairArray.length; i++) {
            // No more dragging
            $(this.dragPairArray[i][0]).attr("draggable", "false");
            $(this.dragPairArray[i][0]).css("cursor", "initial");
        }
    }
}

/*=================================
== Find the custom HTML tags and ==
==   execute our code on them    ==
=================================*/
$(document).bind("runestone:login-complete", function () {
    $("[data-component=dragndrop]").each(function (index) {
        var opts = {
            orig: this,
            useRunestoneServices: eBookConfig.useRunestoneServices,
        };
        if ($(this).closest("[data-component=timedAssessment]").length == 0) {
            // If this element exists within a timed component, don't render it here
            try {
                ddList[this.id] = new DragNDrop(opts);
            } catch (err) {
                console.log(`Error rendering DragNDrop Problem ${this.id}`);
            }
        }
    });
});


/***/ }),

/***/ "./runestone/dragndrop/js/timeddnd.js":
/*!********************************************!*\
  !*** ./runestone/dragndrop/js/timeddnd.js ***!
  \********************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return TimedDragNDrop; });
/* harmony import */ var _dragndrop_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./dragndrop.js */ "./runestone/dragndrop/js/dragndrop.js");




class TimedDragNDrop extends _dragndrop_js__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(opts) {
        super(opts);
        this.finishSettingUp();
        this.renderTimedIcon(this.containerDiv);
        this.hideButtons();
    }
    hideButtons() {
        $(this.submitButton).hide();
    }
    renderTimedIcon(component) {
        // renders the clock icon on timed components.    The component parameter
        // is the element that the icon should be appended to.
        var timeIconDiv = document.createElement("div");
        var timeIcon = document.createElement("img");
        $(timeIcon).attr({
            src: "../_static/clock.png",
            style: "width:15px;height:15px",
        });
        timeIconDiv.className = "timeTip";
        timeIconDiv.title = "";
        timeIconDiv.appendChild(timeIcon);
        $(component).prepend(timeIconDiv);
    }
    checkCorrectTimed() {
        // Returns if the question was correct.    Used for timed assessment grading.
        if (this.unansweredNum === this.dragPairArray.length) {
            this.correct = null;
        }
        switch (this.correct) {
            case true:
                return "T";
            case false:
                return "F";
            default:
                return null;
        }
    }
    hideFeedback() {
        $(this.feedBackDiv).hide();
    }
}

if (typeof window.component_factory === "undefined") {
    window.component_factory = {};
}
window.component_factory["dragndrop"] = function (opts) {
    if (opts.timed) {
        return new TimedDragNDrop(opts);
    }
    return new _dragndrop_js__WEBPACK_IMPORTED_MODULE_0__["default"](opts);
};


/***/ }),

/***/ "./runestone/fitb/css/fitb.css":
/*!*************************************!*\
  !*** ./runestone/fitb/css/fitb.css ***!
  \*************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var api = __webpack_require__(/*! ../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
            var content = __webpack_require__(/*! !../../../node_modules/css-loader/dist/cjs.js!./fitb.css */ "./node_modules/css-loader/dist/cjs.js!./runestone/fitb/css/fitb.css");

            content = content.__esModule ? content.default : content;

            if (typeof content === 'string') {
              content = [[module.i, content, '']];
            }

var options = {};

options.insert = "head";
options.singleton = false;

var update = api(content, options);



module.exports = content.locals || {};

/***/ }),

/***/ "./runestone/fitb/js/fitb-i18n.en.js":
/*!*******************************************!*\
  !*** ./runestone/fitb/js/fitb-i18n.en.js ***!
  \*******************************************/
/*! no static exports found */
/***/ (function(module, exports) {

$.i18n().load({
    en: {
        msg_no_answer: "No answer provided.",
        msg_fitb_check_me: "Check me",
        msg_fitb_compare_me: "Compare me",
    },
});


/***/ }),

/***/ "./runestone/fitb/js/fitb-i18n.pt-br.js":
/*!**********************************************!*\
  !*** ./runestone/fitb/js/fitb-i18n.pt-br.js ***!
  \**********************************************/
/*! no static exports found */
/***/ (function(module, exports) {

$.i18n().load({
    "pt-br": {
        msg_no_answer: "Nenhuma resposta dada.",
        msg_fitb_check_me: "Verificar",
        msg_fitb_compare_me: "Comparar"
    },
});


/***/ }),

/***/ "./runestone/fitb/js/fitb.js":
/*!***********************************!*\
  !*** ./runestone/fitb/js/fitb.js ***!
  \***********************************/
/*! exports provided: FITBList, default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FITBList", function() { return FITBList; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return FITB; });
/* harmony import */ var _common_js_runestonebase_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/js/runestonebase.js */ "./runestone/common/js/runestonebase.js");
/* harmony import */ var _fitb_i18n_en_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./fitb-i18n.en.js */ "./runestone/fitb/js/fitb-i18n.en.js");
/* harmony import */ var _fitb_i18n_en_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_fitb_i18n_en_js__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _fitb_i18n_pt_br_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./fitb-i18n.pt-br.js */ "./runestone/fitb/js/fitb-i18n.pt-br.js");
/* harmony import */ var _fitb_i18n_pt_br_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_fitb_i18n_pt_br_js__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var _css_fitb_css__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../css/fitb.css */ "./runestone/fitb/css/fitb.css");
/* harmony import */ var _css_fitb_css__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_css_fitb_css__WEBPACK_IMPORTED_MODULE_3__);
/*==========================================
========       Master fitb.js      =========
============================================
===   This file contains the JS for the  ===
===  Runestone fillintheblank component. ===
============================================
===              Created By              ===
===           Isaiah Mayerchak           ===
===                 and                  ===
===             Kirby Olson              ===
===                6/4/15                ===
===             Brad Miller              ===
===                2/7/20                ===
==========================================*/







var FITBList = {}; // Object containing all instances of FITB that aren't a child of a timed assessment.

// FITB constructor
class FITB extends _common_js_runestonebase_js__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(opts) {
        super(opts);
        var orig = opts.orig; // entire <p> element
        this.useRunestoneServices = opts.useRunestoneServices;
        this.origElem = orig;
        this.divid = orig.id;
        this.correct = null;
        // See comments in fitb.py for the format of ``feedbackArray`` (which is identical in both files).
        //
        // Find the script tag containing JSON and parse it. See `SO <https://stackoverflow.com/questions/9320427/best-practice-for-embedding-arbitrary-json-in-the-dom>`_. If this parses to ``false``, then no feedback is available; server-side grading will be performed.
        this.feedbackArray = JSON.parse(
            this.scriptSelector(this.origElem).html()
        );
        this.createFITBElement();
        this.checkServer("fillb");
        this.caption = "Fill in the Blank";
        this.addCaption("runestone");
    }
    // Find the script tag containing JSON in a given root DOM node.
    scriptSelector(root_node) {
        return $(root_node).find(`script[type="application/json"]`);
    }
    /*===========================================
    ====   Functions generating final HTML   ====
    ===========================================*/
    createFITBElement() {
        this.renderFITBInput();
        this.renderFITBButtons();
        this.renderFITBFeedbackDiv();
        // replaces the intermediate HTML for this component with the rendered HTML of this component
        $(this.origElem).replaceWith(this.containerDiv);
    }
    renderFITBInput() {
        // The text [input] elements are created by the template.
        this.containerDiv = document.createElement("div");
        $(this.containerDiv).addClass("alert alert-warning");
        this.containerDiv.id = this.divid;
        // Copy the original elements to the container holding what the user will see.
        $(this.origElem).children().clone().appendTo(this.containerDiv);
        // Remove the script tag.
        this.scriptSelector(this.containerDiv).remove();
        // Set the class for the text inputs, then store references to them.
        let ba = $(this.containerDiv).find(":input");
        ba.attr("class", "form form-control selectwidthauto");
        ba.attr("aria-label", "input area");
        this.blankArray = ba.toArray();
        // When a blank is changed mark this component as interacted with.
        // And set a class on the component in case we want to render components that have been used
        // differently
        for (let blank of this.blankArray) {
            $(blank).change(this.recordAnswered.bind(this));
        }
    }

    recordAnswered() {
        this.isAnswered = true;
        //let rcontainer = this.containerDiv.closest(".runestone");
        //rcontainer.addClass("answered");
    }

    renderFITBButtons() {
        // "submit" button and "compare me" button
        this.submitButton = document.createElement("button");
        this.submitButton.textContent = $.i18n("msg_fitb_check_me");
        $(this.submitButton).attr({
            class: "btn btn-success",
            name: "do answer",
            type: "button",
        });
        this.submitButton.addEventListener(
            "click",
            function () {
                this.startEvaluation(true);
            }.bind(this),
            false
        );
        this.containerDiv.appendChild(document.createElement("br"));
        this.containerDiv.appendChild(document.createElement("br"));
        this.containerDiv.appendChild(this.submitButton);
        if (this.useRunestoneServices) {
            this.compareButton = document.createElement("button");
            $(this.compareButton).attr({
                class: "btn btn-default",
                id: this.origElem.id + "_bcomp",
                disabled: "",
                name: "compare",
            });
            this.compareButton.textContent = $.i18n("msg_fitb_compare_me");
            this.compareButton.addEventListener(
                "click",
                function () {
                    this.compareFITBAnswers();
                }.bind(this),
                false
            );
            this.containerDiv.appendChild(this.compareButton);
        }
        this.containerDiv.appendChild(document.createElement("div"));
    }
    renderFITBFeedbackDiv() {
        this.feedBackDiv = document.createElement("div");
        this.feedBackDiv.id = this.divid + "_feedback";
        this.containerDiv.appendChild(document.createElement("br"));
        this.containerDiv.appendChild(this.feedBackDiv);
    }
    /*===================================
    === Checking/loading from storage ===
    ===================================*/
    restoreAnswers(data) {
        var arr;
        // Restore answers from storage retrieval done in RunestoneBase.
        try {
            // The newer format encodes data as a JSON object.
            arr = JSON.parse(data.answer);
            // The result should be an array. If not, try comma parsing instead.
            if (!Array.isArray(arr)) {
                throw new Error();
            }
        } catch (err) {
            // The old format didn't.
            arr = data.answer.split(",");
        }
        for (var i = 0; i < this.blankArray.length; i++) {
            $(this.blankArray[i]).attr("value", arr[i]);
        }
        // Use the feedback from the server, or recompute it locally.
        if (!this.feedbackArray) {
            this.displayFeed = data.displayFeed;
            this.correct = data.correct;
            this.isCorrectArray = data.isCorrectArray;
            // Only render if all the data is present; local storage might have old data missing some of these items.
            if (
                typeof this.displayFeed !== "undefined" &&
                typeof this.correct !== "undefined" &&
                typeof this.isCorrectArray !== "undefined"
            ) {
                this.renderFeedback();
            }
        } else {
            this.startEvaluation(false);
        }
    }
    checkLocalStorage() {
        // Loads previous answers from local storage if they exist
        var storedData;
        if (this.graderactive) {
            return;
        }
        var len = localStorage.length;
        if (len > 0) {
            var ex = localStorage.getItem(this.localStorageKey());
            if (ex !== null) {
                try {
                    storedData = JSON.parse(ex);
                    var arr = storedData.answer;
                } catch (err) {
                    // error while parsing; likely due to bad value stored in storage
                    console.log(err.message);
                    localStorage.removeItem(this.localStorageKey());
                    return;
                }
                this.restoreAnswers(storedData);
            }
        }
    }
    setLocalStorage(data) {
        let key = this.localStorageKey();
        localStorage.setItem(key, JSON.stringify(data));
    }

    checkCurrentAnswer() {
        // Start of the evaulation chain
        this.isCorrectArray = [];
        this.displayFeed = [];
        this.given_arr = [];
        for (var i = 0; i < this.blankArray.length; i++)
            this.given_arr.push(this.blankArray[i].value);
        // Grade locally if we can't ask the server to grade.
        if (this.feedbackArray) {
            this.evaluateAnswers();
        }
    }

    async logCurrentAnswer() {
        let answer = JSON.stringify(this.given_arr);
        // Save the answer locally.
        this.setLocalStorage({
            answer: answer,
            timestamp: new Date(),
        });
        var ret = this.logBookEvent({
            event: "fillb",
            act: answer,
            answer: answer,
            correct: this.correct ? "T" : "F",
            div_id: this.divid,
        });
        return ret;
    }

    /*==============================
    === Evaluation of answer and ===
    ===     display feedback     ===
    ==============================*/
    /*
     * keeping this function for backward compatiblitiy
     * going forward we should be using
     * checkCurrentAnswer()
     * logCurrentAnswer()
     * renderFeedback()
     * independently.
     */
    async startEvaluation(logFlag) {
        this.checkCurrentAnswer();
        if (this.feedbackArray) {
            this.renderFeedback();
        }
        if (logFlag) {
            // Sometimes we don't want to log the answer--for example, when timed exam questions are re-loaded
            let data = await this.logCurrentAnswer();
            if (!this.feedbackArray) {
                // On success, update the feedback from the server's grade.
                this.setLocalStorage({
                    answer: JSON.stringify(this.given_arr),
                    timestamp: data.timestamp,
                });
                this.correct = data.correct;
                this.displayFeed = data.displayFeed;
                this.isCorrectArray = data.isCorrectArray;
                this.renderFeedback();
            }
        }
        if (this.useRunestoneServices) {
            if (eBookConfig.enableCompareMe) {
                this.enableCompareButton();
            }
        }
    }
    // Inputs:
    //
    // - Strings entered by the student in ``this.blankArray[i].value``.
    // - Feedback in ``this.feedbackArray``.
    //
    // Outputs:
    //
    // - ``this.displayFeed`` is an array of HTML feedback.
    // - ``this.isCorrectArray`` is an array of true, false, or null (the question wasn't answered).
    // - ``this.correct`` is true, false, or null (the question wasn't answered).
    evaluateAnswers() {
        // Keep track if all answers are correct or not.
        this.correct = true;
        for (var i = 0; i < this.blankArray.length; i++) {
            var given = this.blankArray[i].value;
            // If this blank is empty, provide no feedback for it.
            if (given === "") {
                this.isCorrectArray.push(null);
                this.displayFeed.push($.i18n("msg_no_answer"));
                this.correct = false;
            } else {
                // Look through all feedback for this blank. The last element in the array always matches.
                var fbl = this.feedbackArray[i];
                for (var j = 0; j < fbl.length; j++) {
                    // The last item of feedback always matches.
                    if (j === fbl.length - 1) {
                        this.displayFeed.push(fbl[j]["feedback"]);
                        break;
                    }
                    // If this is a regexp...
                    if ("regex" in fbl[j]) {
                        var patt = RegExp(
                            fbl[j]["regex"],
                            fbl[j]["regexFlags"]
                        );
                        if (patt.test(given)) {
                            this.displayFeed.push(fbl[j]["feedback"]);
                            break;
                        }
                    } else {
                        // This is a number.
                        console.assert("number" in fbl[j]);
                        var [min, max] = fbl[j]["number"];
                        // Convert the given string to a number. While there are `lots of ways <https://coderwall.com/p/5tlhmw/converting-strings-to-number-in-javascript-pitfalls>`_ to do this; this version supports other bases (hex/binary/octal) as well as floats.
                        var actual = +given;
                        if (actual >= min && actual <= max) {
                            this.displayFeed.push(fbl[j]["feedback"]);
                            break;
                        }
                    }
                }
                // The answer is correct if it matched the first element in the array. A special case: if only one answer is provided, count it wrong; this is a misformed problem.
                let is_correct = j === 0 && fbl.length > 1;
                this.isCorrectArray.push(is_correct);
                if (!is_correct) {
                    this.correct = false;
                }
            }
        }
        this.percent =
            this.isCorrectArray.filter(Boolean).length / this.blankArray.length;
    }

    renderFeedback() {
        if (this.correct) {
            $(this.feedBackDiv).attr("class", "alert alert-info");
            for (let j = 0; j < this.blankArray.length; j++) {
                $(this.blankArray[j]).removeClass("input-validation-error");
            }
        } else {
            if (this.displayFeed === null) {
                this.displayFeed = "";
            }
            for (let j = 0; j < this.blankArray.length; j++) {
                if (this.isCorrectArray[j] !== true) {
                    $(this.blankArray[j]).addClass("input-validation-error");
                } else {
                    $(this.blankArray[j]).removeClass("input-validation-error");
                }
            }
            $(this.feedBackDiv).attr("class", "alert alert-danger");
        }
        var feedback_html = "<ul>";
        for (var i = 0; i < this.displayFeed.length; i++) {
            feedback_html += "<li>" + this.displayFeed[i] + "</li>";
        }
        feedback_html += "</ul>";
        // Remove the list if it's just one element.
        if (this.displayFeed.length == 1) {
            feedback_html = feedback_html.slice(
                "<ul><li>".length,
                -"</li></ul>".length
            );
        }
        this.feedBackDiv.innerHTML = feedback_html;
        if (typeof MathJax !== "undefined") {
            MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
        }
    }

    /*==================================
    === Functions for compare button ===
    ==================================*/
    enableCompareButton() {
        this.compareButton.disabled = false;
    }
    compareFITBAnswers() {
        var data = {};
        data.div_id = this.divid;
        data.course = eBookConfig.course;
        jQuery.get(
            eBookConfig.ajaxURL + "gettop10Answers",
            data,
            this.compareFITB
        );
    }
    compareFITB(data, status, whatever) {
        var answers = eval(data)[0];
        var misc = eval(data)[1];
        var body = "<table>";
        body += "<tr><th>Answer</th><th>Count</th></tr>";
        for (var row in answers) {
            body +=
                "<tr><td>" +
                answers[row].answer +
                "</td><td>" +
                answers[row].count +
                " times</td></tr>";
        }
        body += "</table>";
        if (misc["yourpct"] !== "unavailable") {
            body +=
                "<br /><p>You have " +
                misc["yourpct"] +
                "% correct for all questions</p>";
        }
        var html =
            "<div class='modal fade'>" +
            "    <div class='modal-dialog compare-modal'>" +
            "        <div class='modal-content'>" +
            "            <div class='modal-header'>" +
            "                <button type='button' class='close' data-dismiss='modal' aria-hidden='true'>&times;</button>" +
            "                <h4 class='modal-title'>Top Answers</h4>" +
            "            </div>" +
            "            <div class='modal-body'>" +
            body +
            "            </div>" +
            "        </div>" +
            "    </div>" +
            "</div>";
        var el = $(html);
        el.modal();
    }

    disableInteraction() {
        for (var i = 0; i < this.blankArray.length; i++) {
            this.blankArray[i].disabled = true;
        }
    }
}

/*=================================
== Find the custom HTML tags and ==
==   execute our code on them    ==
=================================*/
$(document).bind("runestone:login-complete", function () {
    $("[data-component=fillintheblank]").each(function (index) {
        var opts = {
            orig: this,
            useRunestoneServices: eBookConfig.useRunestoneServices,
        };
        if ($(this).closest("[data-component=timedAssessment]").length == 0) {
            // If this element exists within a timed component, don't render it here
            try {
                FITBList[this.id] = new FITB(opts);
            } catch (err) {
                console.log(
                    `Error rendering Fill in the Blank Problem ${this.id}
                     Details: ${err}`
                );
            }
        }
    });
});


/***/ }),

/***/ "./runestone/fitb/js/timedfitb.js":
/*!****************************************!*\
  !*** ./runestone/fitb/js/timedfitb.js ***!
  \****************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return TimedFITB; });
/* harmony import */ var _fitb_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./fitb.js */ "./runestone/fitb/js/fitb.js");

class TimedFITB extends _fitb_js__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(opts) {
        super(opts);
        this.renderTimedIcon(this.inputDiv);
        this.hideButtons();
        this.needsReinitialization = true;
    }
    hideButtons() {
        $(this.submitButton).hide();
        $(this.compareButton).hide();
    }
    renderTimedIcon(component) {
        // renders the clock icon on timed components.    The component parameter
        // is the element that the icon should be appended to.
        var timeIconDiv = document.createElement("div");
        var timeIcon = document.createElement("img");
        $(timeIcon).attr({
            src: "../_static/clock.png",
            style: "width:15px;height:15px",
        });
        timeIconDiv.className = "timeTip";
        timeIconDiv.title = "";
        timeIconDiv.appendChild(timeIcon);
        $(component).prepend(timeIconDiv);
    }
    checkCorrectTimed() {
        // Returns if the question was correct, incorrect, or skipped (return null in the last case)
        switch (this.correct) {
            case true:
                return "T";
            case false:
                return "F";
            default:
                return null;
        }
    }
    hideFeedback() {
        for (var i = 0; i < this.blankArray.length; i++) {
            $(this.blankArray[i]).removeClass("input-validation-error");
        }
        this.feedBackDiv.style.display = "none";
    }

    reinitializeListeners() {
        for (let blank of this.blankArray) {
            $(blank).change(this.recordAnswered.bind(this));
        }
    }
}

if (typeof window.component_factory === "undefined") {
    window.component_factory = {};
}
window.component_factory.fillintheblank = function (opts) {
    if (opts.timed) {
        return new TimedFITB(opts);
    }
    return new _fitb_js__WEBPACK_IMPORTED_MODULE_0__["default"](opts);
};


/***/ }),

/***/ "./runestone/lp/js/lp.js":
/*!*******************************!*\
  !*** ./runestone/lp/js/lp.js ***!
  \*******************************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _common_js_runestonebase__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/js/runestonebase */ "./runestone/common/js/runestonebase.js");
/* harmony import */ var codemirror__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! codemirror */ "./node_modules/codemirror/lib/codemirror.js");
/* harmony import */ var codemirror__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(codemirror__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var codemirror_mode_gas_gas_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! codemirror/mode/gas/gas.js */ "./node_modules/codemirror/mode/gas/gas.js");
/* harmony import */ var codemirror_mode_gas_gas_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(codemirror_mode_gas_gas_js__WEBPACK_IMPORTED_MODULE_2__);
// .. Copyright (C) 2017 Bryan A. Jones.
//
//    This file is part of E-Book Binder.
//
//    E-Book Binder is free software: you can redistribute it and/or modify it
//    under the terms of the GNU General Public License as published by the Free
//    Software Foundation, either version 3 of the License, or (at your option)
//    any later version.
//
//    E-Book Binder is distributed in the hope that it will be useful, but WITHOUT
//    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
//    FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
//    details.
//
//    You should have received a copy of the GNU General Public License along
//    with E-Book Binder.  If not, see <http://www.gnu.org/licenses/>.
//
// .. highlight:: javascript
//
// **************************************************************************************
// |docname| - JavaScript functions supporting immediate feedback to in-browser questions
// **************************************************************************************







// Constructor
// ===========
// Object containing all instances of LP problems. (I assume there is just one per page.)
window.LPList = {};

// FITB constructor
class LP extends _common_js_runestonebase__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(opts) {
        super(opts);
        this.useRunestoneServices = opts.useRunestoneServices;
        // Store the DOM element (the input) for the "Test" button.
        this.element = opts.orig;
        this.divid = this.element.id;
        // Store the DOM element (the textarea) where compile results will be displayed.
        this.resultElement = $(this.element).siblings("textarea");
        // Store the DOM element (a span) where feedback will be displayed.
        this.feedbackElement = $(this.element).siblings("div");
        // Use a nice editor.
        let that = this;
        this.textAreas = [];
        $(".code_snippet").each(function (index, element) {
            let editor = codemirror__WEBPACK_IMPORTED_MODULE_1___default.a.fromTextArea(element, {
                lineNumbers: true,
                mode: $(that.element).attr("data-lang"),
                indentUnit: 4,
                matchBrackets: true,
                autoMatchParens: true,
                extraKeys: { Tab: "indentMore", "Shift-Tab": "indentLess" },
            });
            // Make the editor resizable.
            $(editor.getWrapperElement()).resizable({
                resize: function () {
                    editor.setSize($(this).width(), $(this).height());
                    editor.refresh();
                },
            });
            // Keep track of it.
            that.textAreas.push(editor);
        });
        this.checkServer("lp_build");
        // Handle clicks to the "Save and run" button.
        $(this.element).click((eventObject) => that.onSaveAndRun(eventObject).then(null));
    }

    async onSaveAndRun(_eventObject) {
        $(this.resultElement).val("Building...");
        $(this.feedbackElement).text("").attr("");
        // Since the Save and run button was clicked, we assume the code snippets have been changed; therefore, don't store ``correct`` or ``answer.resultString`` because they are out of date.
        let answer = { code_snippets: this.textareasToData() };
        this.setLocalStorage({
            answer: answer,
            timestamp: new Date(),
        });
        // Store the answer that the server returns, which includes additional data (correct/incorrect, feedback from the build, etc.).
        let serverAnswer;
        try {
            serverAnswer = await this.logBookEvent({
                event: "lp_build",
                // All values must be strings, or the resulting values on the server side come out confused.
                answer: JSON.stringify(answer),
                // Find the relative path to this web page. Slice off the leading ``/``.
                path: window.location.href
                    .replace(eBookConfig.app, "")
                    .slice(1),
                div_id: this.divid,
            });
        } catch (err) {
            $(this.feedbackElement)
                .val(`Error contacting server: {err}.`)
                .attr("class", "alert alert-danger");
            return;
        }
        // The server doesn't return the ``code_snippets``, for efficiency. Include those. If an error was returned, note that there is no ``answer`` yet.
        if (!("answer" in serverAnswer)) {
            serverAnswer["answer"] = {};
        }
        serverAnswer["answer"]["code_snippets"] = this.textareasToData();
        this.displayAnswer(serverAnswer);
        this.setLocalStorage(serverAnswer);
    }

    // Given a single answer, display it.
    displayAnswer(data) {
        if ("errors" in data) {
            // Display any server-side errors. If this key is present, other keys won't be.
            $(this.feedbackElement)
                .text(data.errors.join("<br>"))
                .attr("class", "alert alert-danger");
        } else {
            // Display and color-code the results.
            $(this.resultElement).val(data.answer.resultString);
            if (data.correct == null) {
                $(this.feedbackElement)
                    .text("Response recorded.")
                    .attr("class", "alert alert-success");
            } else if (data.correct >= 100) {
                $(this.feedbackElement)
                    .text("Correct. Grade: " + data.correct + "%")
                    .attr("class", "alert alert-success");
            } else {
                $(this.feedbackElement)
                    .text("Incorrect. Grade: " + data.correct + "%")
                    .attr("class", "alert alert-danger");
            }
            // Scroll to the bottom of the results.
            $(this.resultElement).scrollTop(this.resultElement[0].scrollHeight);
        }
    }
    // Store the contents of each textarea into an array of strings.
    textareasToData() {
        return $.map(this.textAreas, function (obj, index) {
            // See https://codemirror.net/doc/manual.html#api.
            return obj.getValue();
        });
    }
    // Store an array of strings in ``data.code_snippets`` into each textarea.
    dataToTextareas(data) {
        // Find all code snippet textareas.
        $(this.textAreas).each(function (index, value) {
            // Silently ignore if ``data.answer.code_snippets`` or ``data.answer.code_snippets[index]`` isn't defined.
            value.setValue((data.answer.code_snippets || "")[index] || "");
        });
    }
    // Restore answers from storage retrieval done in RunestoneBase.
    restoreAnswers(data) {
        this.dataToTextareas(data);
        this.displayAnswer(data);
    }
    checkLocalStorage() {
        // Loads previous answers from local storage if they exist.
        var storedData;
        if (localStorage.length > 0) {
            var key = this.localStorageKey();
            var ex = localStorage.getItem(key);
            if (ex !== null) {
                try {
                    storedData = JSON.parse(ex);
                } catch (err) {
                    // error while parsing; likely due to bad value stored in storage
                    console.log(err.message);
                    localStorage.removeItem(key);
                    return;
                }
                this.restoreAnswers(storedData);
            }
        }
    }
    setLocalStorage(data) {
        localStorage.setItem(this.localStorageKey(), JSON.stringify(data));
    }
}

// Initialization
// ==============
// Find the custom HTML tags and execute our code on them.
$(document).bind("runestone:login-complete", function () {
    $("[data-component=lp_build]").each(function (index) {
        try {
            LPList[this.id] = new LP({
                orig: this,
                useRunestoneServices: eBookConfig.useRunestoneServices,
            });
        } catch (err) {
            console.log(`Error rendering LP Problem ${this.id}`);
        }
    });
});

if (typeof window.component_factory === "undefined") {
    window.component_factory = {};
}
window.component_factory["lp_build"] = function (opts) {
    return new LP(opts);
};


/***/ }),

/***/ "./runestone/mchoice/css/mchoice.css":
/*!*******************************************!*\
  !*** ./runestone/mchoice/css/mchoice.css ***!
  \*******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var api = __webpack_require__(/*! ../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
            var content = __webpack_require__(/*! !../../../node_modules/css-loader/dist/cjs.js!./mchoice.css */ "./node_modules/css-loader/dist/cjs.js!./runestone/mchoice/css/mchoice.css");

            content = content.__esModule ? content.default : content;

            if (typeof content === 'string') {
              content = [[module.i, content, '']];
            }

var options = {};

options.insert = "head";
options.singleton = false;

var update = api(content, options);



module.exports = content.locals || {};

/***/ }),

/***/ "./runestone/mchoice/js/mchoice.js":
/*!*****************************************!*\
  !*** ./runestone/mchoice/js/mchoice.js ***!
  \*****************************************/
/*! exports provided: mcList, default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mcList", function() { return mcList; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return MultipleChoice; });
/* harmony import */ var _common_js_runestonebase_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/js/runestonebase.js */ "./runestone/common/js/runestonebase.js");
/* harmony import */ var _css_mchoice_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../css/mchoice.css */ "./runestone/mchoice/css/mchoice.css");
/* harmony import */ var _css_mchoice_css__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_mchoice_css__WEBPACK_IMPORTED_MODULE_1__);
/*==========================================
========      Master mchoice.js     =========
============================================
===  This file contains the JS for the   ===
=== Runestone multiple choice component. ===
============================================
===              Created By              ===
===           Isaiah Mayerchak           ===
===                 and                  ===
===             Kirby Olson              ===
===                6/4/15                ===
==========================================*/


//import "./../styles/runestone-custom-sphinx-bootstrap.css";


var mcList = {}; // Multiple Choice dictionary

// MC constructor
class MultipleChoice extends _common_js_runestonebase_js__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(opts) {
        super(opts);
        opts = opts || {};
        var orig = opts.orig; // entire <ul> element
        this.origElem = orig;
        this.useRunestoneServices = opts.useRunestoneServices;
        this.multipleanswers = false;
        this.divid = orig.id;
        if ($(this.origElem).data("multipleanswers") === true) {
            this.multipleanswers = true;
        }
        this.children = this.origElem.childNodes;
        this.random = false;
        if ($(this.origElem).is("[data-random]")) {
            this.random = true;
        }
        this.correct = null;
        this.answerList = [];
        this.correctList = [];
        this.correctIndexList = [];
        this.feedbackList = [];
        this.question = null;
        this.caption = "Multiple Choice";
        this.findAnswers();
        this.findQuestion();
        this.findFeedbacks();
        this.createCorrectList();
        this.createMCForm();
        this.checkServer("mChoice");
        this.addCaption("runestone");
    }

    /*====================================
    ==== Functions parsing variables  ====
    ====  out of intermediate HTML    ====
    ====================================*/
    findQuestion() {
        var delimiter;
        for (var i = 0; i < this.origElem.childNodes.length; i++) {
            if (this.origElem.childNodes[i].nodeName === "LI") {
                delimiter = this.origElem.childNodes[i].outerHTML;
                break;
            }
        }
        var fulltext = $(this.origElem).html();
        var temp = fulltext.split(delimiter);
        this.question = temp[0];
    }

    findAnswers() {
        // Creates answer objects and pushes them to answerList
        // format: ID, Correct bool, Content (text)
        var ChildAnswerList = [];
        for (var i = 0; i < this.children.length; i++) {
            if ($(this.children[i]).is("[data-component=answer]")) {
                ChildAnswerList.push(this.children[i]);
            }
        }
        for (var j = 0; j < ChildAnswerList.length; j++) {
            var answer_id = $(ChildAnswerList[j]).attr("id");
            var is_correct = false;
            if ($(ChildAnswerList[j]).is("[data-correct]")) {
                // If data-correct attribute exists, answer is correct
                is_correct = true;
            }
            var answer_text = $(ChildAnswerList[j]).html();
            var answer_object = {
                id: answer_id,
                correct: is_correct,
                content: answer_text,
            };
            this.answerList.push(answer_object);
        }
    }

    findFeedbacks() {
        for (var i = 0; i < this.children.length; i++) {
            if ($(this.children[i]).is("[data-component=feedback]")) {
                this.feedbackList.push(this.children[i].innerHTML);
            }
        }
    }

    createCorrectList() {
        // Creates array that holds the ID"s of correct answers
        // Also populates an array that holds the indeces of correct answers
        for (var i = 0; i < this.answerList.length; i++) {
            if (this.answerList[i].correct) {
                this.correctList.push(this.answerList[i].id);
                this.correctIndexList.push(i);
            }
        }
    }

    /*===========================================
    ====   Functions generating final HTML   ====
    ===========================================*/
    createMCForm() {
        this.renderMCContainer();
        this.renderMCForm(); // renders the form with options and buttons
        this.renderMCfeedbackDiv();
        // replaces intermediate HTML with rendered HTML
        $(this.origElem).replaceWith(this.containerDiv);
    }

    renderMCContainer() {
        this.containerDiv = document.createElement("div");
        $(this.containerDiv).html(this.question);
        $(this.containerDiv).addClass(this.origElem.getAttribute("class"));
        this.containerDiv.id = this.divid;
    }

    renderMCForm() {
        this.optsForm = document.createElement("form");
        this.optsForm.id = this.divid + "_form";
        $(this.optsForm).attr({
            method: "get",
            action: "",
            onsubmit: "return false;",
        });
        // generate form options
        this.renderMCFormOpts();
        this.renderMCFormButtons();
        // Append the form to the container
        this.containerDiv.appendChild(this.optsForm);
    }

    renderMCFormOpts() {
        // creates input DOM elements
        this.optionArray = []; // array with an object for each option containing the input and label for that option
        var input_type = "radio";
        if (this.multipleanswers) {
            input_type = "checkbox";
        }
        // this.indexArray is used to index through the answers
        // it is just 0-n normally, but the order is shuffled if the random option is present
        this.indexArray = [];
        for (var i = 0; i < this.answerList.length; i++) {
            this.indexArray.push(i);
        }
        if (this.random) {
            this.randomizeAnswers();
        }
        let self = this;
        let answerFunc = function () {
            self.isAnswered = true;
        };
        for (var j = 0; j < this.answerList.length; j++) {
            var k = this.indexArray[j];
            var optid = this.divid + "_opt_" + k;
            // Create the label for the input
            var label = document.createElement("label");
            // If the content begins with a ``<p>``, put the label inside of it. (Sphinx 2.0 puts all content in a ``<p>``, while Sphinx 1.8 doesn't).
            var content = this.answerList[k].content;
            var prefix = "";
            if (content.startsWith("<p>")) {
                prefix = "<p>";
                content = content.slice(3);
            }
            $(label).html(
                `${prefix}<input type="${input_type}" name="group1" value=${k} id=${optid}>${String.fromCharCode(
                    "A".charCodeAt(0) + j
                )}. ${content}`
            );
            // create the object to store in optionArray
            var optObj = {
                input: $(label).find("input")[0],
                label: label,
            };
            optObj.input.onclick = answerFunc;

            this.optionArray.push(optObj);
            // add the option to the form
            this.optsForm.appendChild(label);
            this.optsForm.appendChild(document.createElement("br"));
        }
    }

    renderMCFormButtons() {
        // submit and compare me buttons
        // Create submit button
        this.submitButton = document.createElement("button");
        this.submitButton.textContent = "Check Me";
        $(this.submitButton).attr({
            class: "btn btn-success",
            name: "do answer",
            type: "button",
        });
        if (this.multipleanswers) {
            this.submitButton.addEventListener(
                "click",
                function () {
                    this.processMCMASubmission(true);
                }.bind(this),
                false
            );
        } else {
            this.submitButton.addEventListener(
                "click",
                function (ev) {
                    ev.preventDefault();
                    this.processMCMFSubmission(true);
                }.bind(this),
                false
            );
        } // end else
        this.optsForm.appendChild(this.submitButton);
        // Create compare button
        if (this.useRunestoneServices) {
            this.compareButton = document.createElement("button");
            $(this.compareButton).attr({
                class: "btn btn-default",
                id: this.divid + "_bcomp",
                disabled: "",
                name: "compare",
            });
            this.compareButton.textContent = "Compare me";
            this.compareButton.addEventListener(
                "click",
                function () {
                    this.compareAnswers(this.divid);
                }.bind(this),
                false
            );
            this.optsForm.appendChild(this.compareButton);
        }
    }

    renderMCfeedbackDiv() {
        this.feedBackDiv = document.createElement("div");
        this.feedBackDiv.id = this.divid + "_feedback";
        this.containerDiv.appendChild(document.createElement("br"));
        this.containerDiv.appendChild(this.feedBackDiv);
    }

    randomizeAnswers() {
        // Makes the ordering of the answer choices random
        var currentIndex = this.indexArray.length,
            temporaryValue,
            randomIndex;
        // While there remain elements to shuffle...
        while (currentIndex !== 0) {
            // Pick a remaining element...
            randomIndex = Math.floor(Math.random() * currentIndex);
            currentIndex -= 1;
            // And swap it with the current element.
            temporaryValue = this.indexArray[currentIndex];
            this.indexArray[currentIndex] = this.indexArray[randomIndex];
            this.indexArray[randomIndex] = temporaryValue;
            var temporaryFeedback = this.feedbackList[currentIndex];
            this.feedbackList[currentIndex] = this.feedbackList[randomIndex];
            this.feedbackList[randomIndex] = temporaryFeedback;
        }
    }

    /*===================================
    === Checking/loading from storage ===
    ===================================*/
    restoreAnswers(data) {
        // Restore answers from storage retrieval done in RunestoneBase
        // sometimes data.answer can be null
        if (!data.answer) {
            data.answer = "";
        }
        var answers = data.answer.split(",");
        for (var a = 0; a < answers.length; a++) {
            var index = answers[a];
            for (var b = 0; b < this.optionArray.length; b++) {
                if (this.optionArray[b].input.value == index) {
                    $(this.optionArray[b].input).attr("checked", "true");
                }
            }
        }
        if (this.multipleanswers) {
            this.processMCMASubmission(false);
        } else {
            this.processMCMFSubmission(false);
        }
    }

    checkLocalStorage() {
        // Repopulates MCMA questions with a user's previous answers,
        // which were stored into local storage.
        var storedData;
        var answers;
        if (this.graderactive) {
            return;
        }
        var len = localStorage.length;
        if (len > 0) {
            var ex = localStorage.getItem(this.localStorageKey());
            if (ex !== null) {
                try {
                    storedData = JSON.parse(ex);
                    answers = storedData.answer.split(",");
                } catch (err) {
                    // error while parsing; likely due to bad value stored in storage
                    console.log(err.message);
                    localStorage.removeItem(this.localStorageKey());
                    return;
                }
                for (var a = 0; a < answers.length; a++) {
                    var index = answers[a];
                    for (var b = 0; b < this.optionArray.length; b++) {
                        if (this.optionArray[b].input.value == index) {
                            $(this.optionArray[b].input).attr(
                                "checked",
                                "true"
                            );
                        }
                    }
                }
                if (this.useRunestoneServices) {
                    this.enableMCComparison();
                    this.getSubmittedOpts(); // to populate givenlog for logging
                    if (this.multipleanswers) {
                        this.logMCMAsubmission();
                    } else {
                        this.logMCMFsubmission();
                    }
                }
            }
        }
    }

    setLocalStorage(data) {
        var timeStamp = new Date();
        var storageObj = {
            answer: data.answer,
            timestamp: timeStamp,
            correct: data.correct,
        };
        localStorage.setItem(
            this.localStorageKey(),
            JSON.stringify(storageObj)
        );
    }

    /*===============================
    === Processing MC Submissions ===
    ===============================*/
    processMCMASubmission(logFlag) {
        // Called when the submit button is clicked
        this.getSubmittedOpts(); // make sure this.givenArray is populated
        this.scoreMCMASubmission();
        this.setLocalStorage({
            correct: this.correct ? "T" : "F",
            answer: this.givenArray.join(","),
        });
        if (logFlag) {
            this.logMCMAsubmission();
        }
        this.renderMCMAFeedBack();
        if (this.useRunestoneServices) {
            this.enableMCComparison();
        }
    }

    getSubmittedOpts() {
        var given;
        this.singlefeedback = ""; // Used for MCMF questions
        this.feedbackString = ""; // Used for MCMA questions
        this.givenArray = [];
        this.givenlog = "";
        var buttonObjs = this.optsForm.elements.group1;
        for (var i = 0; i < buttonObjs.length; i++) {
            if (buttonObjs[i].checked) {
                given = buttonObjs[i].value;
                this.givenArray.push(given);
                this.feedbackString += `<li value="${i + 1}">${this.feedbackList[i]
                    }</li>`;
                this.givenlog += given + ",";
                this.singlefeedback = this.feedbackList[i];
            }
        }
        this.givenArray.sort();
    }

    checkCurrentAnswer() {
        this.getSubmittedOpts();
        if (this.multipleanswers) {
            this.scoreMCMASubmission();
        } else {
            this.scoreMCMFSubmission();
        }
    }

    logCurrentAnswer() {
        if (this.multipleanswers) {
            this.logMCMAsubmission();
        } else {
            this.logMCMFsubmission();
        }
    }

    renderFeedback() {
        if (this.multipleanswers) {
            this.renderMCMAFeedBack();
        } else {
            this.renderMCMFFeedback();
        }
    }
    scoreMCMASubmission() {
        this.correctCount = 0;
        var correctIndex = 0;
        var givenIndex = 0;
        while (
            correctIndex < this.correctIndexList.length &&
            givenIndex < this.givenArray.length
        ) {
            if (
                this.givenArray[givenIndex] <
                this.correctIndexList[correctIndex]
            ) {
                givenIndex++;
            } else if (
                this.givenArray[givenIndex] ==
                this.correctIndexList[correctIndex]
            ) {
                this.correctCount++;
                givenIndex++;
                correctIndex++;
            } else {
                correctIndex++;
            }
        }
        var numGiven = this.givenArray.length;
        var numCorrect = this.correctCount;
        var numNeeded = this.correctList.length;
        this.answer = this.givenArray.join(",");
        this.correct = numCorrect === numNeeded && numNeeded === numGiven;
        if (numGiven === numNeeded) {
            this.percent = numCorrect / numNeeded;
        } else {
            this.percent = numCorrect / Math.max(numGiven, numNeeded);
        }
    }

    logMCMAsubmission() {
        var answer = this.answer;
        var correct = this.correct;
        var logAnswer =
            "answer:" + answer + ":" + (correct == "T" ? "correct" : "no");
        this.logBookEvent({
            event: "mChoice",
            act: logAnswer,
            answer: answer,
            correct: correct,
            div_id: this.divid,
        });
    }

    renderMCMAFeedBack() {
        var answerStr = "answers";
        var numGiven = this.givenArray.length;
        if (numGiven === 1) {
            answerStr = "answer";
        }
        var numCorrect = this.correctCount;
        var numNeeded = this.correctList.length;
        var feedbackText = this.feedbackString;
        if (this.correct) {
            $(this.feedBackDiv).html(`✔️ <ol type="A">${feedbackText}</ul>`);
            $(this.feedBackDiv).attr("class", "alert alert-info");
        } else {
            $(this.feedBackDiv).html(
                `✖️ You gave ${numGiven} ${answerStr} and got ${numCorrect} correct of ${numNeeded} needed.<ol type="A">${feedbackText}</ul>`
            );
            $(this.feedBackDiv).attr("class", "alert alert-danger");
        }
    }

    processMCMFSubmission(logFlag) {
        // Called when the submit button is clicked
        this.getSubmittedOpts(); // make sure this.givenArray is populated
        this.scoreMCMFSubmission();
        this.setLocalStorage({
            correct: this.correct ? "T" : "F",
            answer: this.givenArray.join(","),
        });
        if (logFlag) {
            this.logMCMFsubmission();
        }
        this.renderMCMFFeedback();
        if (this.useRunestoneServices) {
            this.enableMCComparison();
        }
    }

    scoreMCMFSubmission() {
        if (this.givenArray[0] == this.correctIndexList[0]) {
            this.correct = true;
            this.percent = 1.0;
        } else if (this.givenArray[0] != null) {
            // if given is null then the question wasn"t answered and should be counted as skipped
            this.correct = false;
            this.percent = 0.0;
        }
    }

    logMCMFsubmission() {
        var answer = this.givenArray[0];
        var correct =
            this.givenArray[0] == this.correctIndexList[0] ? "T" : "F";
        var logAnswer =
            "answer:" + answer + ":" + (correct == "T" ? "correct" : "no"); // backward compatible
        this.logBookEvent({
            event: "mChoice",
            act: logAnswer,
            answer: answer,
            correct: correct,
            div_id: this.divid,
        });
    }

    renderMCMFFeedback() {
        let correct = this.givenArray[0] == this.correctIndexList[0];
        let feedbackText = this.singlefeedback;

        if (correct) {
            $(this.feedBackDiv).html("✔️ " + feedbackText);
            $(this.feedBackDiv).attr("class", "alert alert-info"); // use blue for better red/green blue color blindness
        } else {
            if (feedbackText == null) {
                feedbackText = "";
            }
            $(this.feedBackDiv).html("✖️ " + feedbackText);
            $(this.feedBackDiv).attr("class", "alert alert-danger");
        }
    }
    enableMCComparison() {
        if (eBookConfig.enableCompareMe) {
            this.compareButton.disabled = false;
        }
    }
    instructorMchoiceModal(data) {
        // data.reslist -- student and their answers
        // data.answerDict    -- answers and count
        // data.correct - correct answer
        var res = "<table><tr><th>Student</th><th>Answer(s)</th></tr>";
        for (var i in data) {
            res +=
                "<tr><td>" +
                data[i][0] +
                "</td><td>" +
                data[i][1] +
                "</td></tr>";
        }
        res += "</table>";
        return res;
    }
    compareModal(data, status, whatever) {
        var datadict = JSON.parse(data)[0];
        var answers = datadict.answerDict;
        var misc = datadict.misc;
        var kl = Object.keys(answers).sort();
        var body = "<table>";
        body += "<tr><th>Answer</th><th>Percent</th></tr>";
        var theClass = "";
        for (var k in kl) {
            if (kl[k] === misc.correct) {
                theClass = "success";
            } else {
                theClass = "info";
            }
            body +=
                "<tr><td>" + kl[k] + "</td><td class='compare-me-progress'>";
            var pct = answers[kl[k]] + "%";
            body += "<div class='progress'>";
            body +=
                "    <div class='progress-bar progress-bar-" +
                theClass +
                "' style='width:" +
                pct +
                ";'>" +
                pct;
            body += "    </div>";
            body += "</div></td></tr>";
        }
        body += "</table>";
        if (misc.yourpct !== "unavailable") {
            body +=
                "<br /><p>You have " +
                misc.yourpct +
                "% correct for all questions</p>";
        }
        if (datadict.reslist !== undefined) {
            body += this.instructorMchoiceModal(datadict.reslist);
        }
        var html =
            "<div class='modal fade'>" +
            "    <div class='modal-dialog compare-modal'>" +
            "        <div class='modal-content'>" +
            "            <div class='modal-header'>" +
            "                <button type='button' class='close' data-dismiss='modal' aria-hidden='true'>&times;</button>" +
            "                <h4 class='modal-title'>Distribution of Answers</h4>" +
            "            </div>" +
            "            <div class='modal-body'>" +
            body +
            "            </div>" +
            "        </div>" +
            "    </div>" +
            "</div>";
        var el = $(html);
        el.modal();
    }
    compareAnswers() {
        var data = {};
        data.div_id = this.divid;
        data.course = eBookConfig.course;
        jQuery.get(
            eBookConfig.ajaxURL + "getaggregateresults",
            data,
            this.compareModal.bind(this)
        );
    }

    disableInteraction() {
        for (var i = 0; i < this.optionArray.length; i++) {
            this.optionArray[i].input.disabled = true;
        }
    }
}

/*=================================
== Find the custom HTML tags and ==
==   execute our code on them    ==
=================================*/
$(document).bind("runestone:login-complete", function () {
    $("[data-component=multiplechoice]").each(function (index) {
        // MC
        var opts = {
            orig: this,
            useRunestoneServices: eBookConfig.useRunestoneServices,
        };
        if ($(this).closest("[data-component=timedAssessment]").length == 0) {
            // If this element exists within a timed component, don't render it here
            mcList[this.id] = new MultipleChoice(opts);
        }
    });
});


/***/ }),

/***/ "./runestone/mchoice/js/timedmc.js":
/*!*****************************************!*\
  !*** ./runestone/mchoice/js/timedmc.js ***!
  \*****************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return TimedMC; });
/* harmony import */ var _mchoice_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./mchoice.js */ "./runestone/mchoice/js/mchoice.js");


class TimedMC extends _mchoice_js__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(opts) {
        super(opts);
        $(this.containerDiv).addClass("alert alert-warning runestone");
        this.needsReinitialization = true;
        this.renderTimedIcon(this.MCContainer);
        this.hideButtons(); // Don't show per-question buttons in a timed assessment
    }

    renderTimedIcon(component) {
        // renders the clock icon on timed components.    The component parameter
        // is the element that the icon should be appended to.
        var timeIconDiv = document.createElement("div");
        var timeIcon = document.createElement("img");
        $(timeIcon).attr({
            src: "../_static/clock.png",
            style: "width:15px;height:15px",
        });
        timeIconDiv.className = "timeTip";
        timeIconDiv.title = "";
        timeIconDiv.appendChild(timeIcon);
        $(component).prepend(timeIconDiv);
    }
    hideButtons() {
        //Just hiding the buttons doesn't prevent submitting the form when entering is clicked
        //We need to completely disable the buttons
        $(this.submitButton).attr("disabled", "true");
        $(this.submitButton).hide();
        $(this.compareButton).hide();
    }

    // These methods override the methods in the base class. Called from renderFeedback()
    //
    renderMCMAFeedBack() {
        this.feedbackTimedMC();
    }
    renderMCMFFeedback(whatever, whateverr) {
        this.feedbackTimedMC();
    }
    feedbackTimedMC() {
        for (var i = 0; i < this.indexArray.length; i++) {
            var tmpindex = this.indexArray[i];
            $(this.feedBackEachArray[i]).text(
                String.fromCharCode(65 + i) + ". " + this.feedbackList[i]
            );
            var tmpid = this.answerList[tmpindex].id;
            if (this.correctList.indexOf(tmpid) >= 0) {
                this.feedBackEachArray[i].classList.add(
                    "alert",
                    "alert-success"
                );
            } else {
                this.feedBackEachArray[i].classList.add(
                    "alert",
                    "alert-danger"
                );
            }
        }
    }
    renderMCFormOpts() {
        super.renderMCFormOpts();
        this.feedBackEachArray = [];
        for (var j = 0; j < this.answerList.length; j++) {
            var k = this.indexArray[j];
            var feedBackEach = document.createElement("div");
            feedBackEach.id = this.divid + "_eachFeedback_" + k;
            feedBackEach.classList.add("eachFeedback");
            this.feedBackEachArray.push(feedBackEach);
            this.optsForm.appendChild(feedBackEach);
        }
    }
    checkCorrectTimedMCMA() {
        if (
            this.correctCount === this.correctList.length &&
            this.correctList.length === this.givenArray.length
        ) {
            this.correct = true;
        } else if (this.givenArray.length !== 0) {
            this.correct = false;
        } else {
            // question was skipped
            this.correct = null;
        }
        switch (this.correct) {
            case true:
                return "T";
            case false:
                return "F";
            default:
                return null;
        }
    }
    checkCorrectTimedMCMF() {
        // Returns if the question was correct, incorrect, or skipped (return null in the last case)
        switch (this.correct) {
            case true:
                return "T";
            case false:
                return "F";
            default:
                return null;
        }
    }
    checkCorrectTimed() {
        if (this.multipleanswers) {
            return this.checkCorrectTimedMCMA();
        } else {
            return this.checkCorrectTimedMCMF();
        }
    }
    hideFeedback() {
        for (var i = 0; i < this.feedBackEachArray.length; i++) {
            $(this.feedBackEachArray[i]).hide();
        }
    }

    reinitializeListeners() {
        let self = this;
        let answerFunc = function () {
            self.isAnswered = true;
        };
        for (let opt of this.optionArray) {
            opt.input.onclick = answerFunc;
        }
    }
}

if (typeof window.component_factory === "undefined") {
    window.component_factory = {};
}

window.component_factory.multiplechoice = function (opts) {
    if (opts.timed) {
        return new TimedMC(opts);
    } else {
        return new _mchoice_js__WEBPACK_IMPORTED_MODULE_0__["default"](opts);
    }
};


/***/ }),

/***/ "./runestone/parsons/css/images/parsons-ll.svg":
/*!*****************************************************!*\
  !*** ./runestone/parsons/css/images/parsons-ll.svg ***!
  \*****************************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony default export */ __webpack_exports__["default"] = ("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNS4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iMTJweCIgaGVpZ2h0PSI2MDBweCIgdmlld0JveD0iMCAwIDEyIDYwMCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMTIgNjAwIiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwb2x5Z29uIGZpbGw9IiNGRkNDMDAiIHBvaW50cz0iMTIsMCAwLDAgMCw2MDAgMTIsNjAwIDEyLDMxMCAyLDMwMCAxMiwyOTAgIi8+DQo8cG9seWdvbiBwb2ludHM9IjUsMzAwIDEyLDI5MyAxMiwyOTAgMiwzMDAgMTIsMzEwIDEyLDMwNyAiLz4NCjwvc3ZnPg0K");

/***/ }),

/***/ "./runestone/parsons/css/images/parsons-lr.svg":
/*!*****************************************************!*\
  !*** ./runestone/parsons/css/images/parsons-lr.svg ***!
  \*****************************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony default export */ __webpack_exports__["default"] = ("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNS4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iMTJweCIgaGVpZ2h0PSI2MDBweCIgdmlld0JveD0iMCAwIDEyIDYwMCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMTIgNjAwIiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwb2x5Z29uIHBvaW50cz0iOSwzMDAgMiwyOTMgMiwyOTAgMTIsMzAwIDIsMzEwIDIsMzA3ICIvPg0KPHBvbHlnb24gZmlsbD0iI0ZGQ0MwMCIgcG9pbnRzPSI5LDMwMCAwLDI5MSAwLDMwOSAiLz4NCjxyZWN0IGZpbGw9IiNGRkNDMDAiIHdpZHRoPSIyIiBoZWlnaHQ9IjYwMCIvPg0KPC9zdmc+DQo=");

/***/ }),

/***/ "./runestone/parsons/css/images/parsons-rl.svg":
/*!*****************************************************!*\
  !*** ./runestone/parsons/css/images/parsons-rl.svg ***!
  \*****************************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony default export */ __webpack_exports__["default"] = ("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNS4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iMTJweCIgaGVpZ2h0PSI2MDBweCIgdmlld0JveD0iMCAwIDEyIDYwMCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMTIgNjAwIiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwb2x5Z29uIHBvaW50cz0iMTAsMzA3IDEwLDMxMCAwLDMwMCAxMCwyOTAgMTAsMjkzIDMsMzAwICIvPg0KPHBvbHlnb24gZmlsbD0iI0ZGQ0MwMCIgcG9pbnRzPSIxMiwzMDkgMTIsMjkxIDMsMzAwICIvPg0KPHJlY3QgeD0iMTAiIGZpbGw9IiNGRkNDMDAiIHdpZHRoPSIyIiBoZWlnaHQ9IjYwMCIvPg0KPC9zdmc+DQo=");

/***/ }),

/***/ "./runestone/parsons/css/images/parsons-rr.svg":
/*!*****************************************************!*\
  !*** ./runestone/parsons/css/images/parsons-rr.svg ***!
  \*****************************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony default export */ __webpack_exports__["default"] = ("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNS4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iMTJweCIgaGVpZ2h0PSI2MDBweCIgdmlld0JveD0iMCAwIDEyIDYwMCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMTIgNjAwIiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwb2x5Z29uIGZpbGw9IiNGRkNDMDAiIHBvaW50cz0iMCwyOTAgMTAsMzAwIDAsMzEwIDAsNjAwIDEyLDYwMCAxMiwwIDAsMCAiLz4NCjxwb2x5Z29uIHBvaW50cz0iMCwzMDcgMCwzMTAgMTAsMzAwIDAsMjkwIDAsMjkzIDcsMzAwICIvPg0KPC9zdmc+DQo=");

/***/ }),

/***/ "./runestone/parsons/css/parsons.css":
/*!*******************************************!*\
  !*** ./runestone/parsons/css/parsons.css ***!
  \*******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var api = __webpack_require__(/*! ../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
            var content = __webpack_require__(/*! !../../../node_modules/css-loader/dist/cjs.js!./parsons.css */ "./node_modules/css-loader/dist/cjs.js!./runestone/parsons/css/parsons.css");

            content = content.__esModule ? content.default : content;

            if (typeof content === 'string') {
              content = [[module.i, content, '']];
            }

var options = {};

options.insert = "head";
options.singleton = false;

var update = api(content, options);



module.exports = content.locals || {};

/***/ }),

/***/ "./runestone/parsons/css/prettify.css":
/*!********************************************!*\
  !*** ./runestone/parsons/css/prettify.css ***!
  \********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var api = __webpack_require__(/*! ../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
            var content = __webpack_require__(/*! !../../../node_modules/css-loader/dist/cjs.js!./prettify.css */ "./node_modules/css-loader/dist/cjs.js!./runestone/parsons/css/prettify.css");

            content = content.__esModule ? content.default : content;

            if (typeof content === 'string') {
              content = [[module.i, content, '']];
            }

var options = {};

options.insert = "head";
options.singleton = false;

var update = api(content, options);



module.exports = content.locals || {};

/***/ }),

/***/ "./runestone/parsons/js/hammer.min.js":
/*!********************************************!*\
  !*** ./runestone/parsons/js/hammer.min.js ***!
  \********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_RESULT__;/*! Hammer.JS - v2.0.8 - 2016-04-23
 * http://hammerjs.github.io/
 *
 * Copyright (c) 2016 Jorik Tangelder;
 * Licensed under the MIT license */
!function(a,b,c,d){"use strict";function e(a,b,c){return setTimeout(j(a,c),b)}function f(a,b,c){return Array.isArray(a)?(g(a,c[b],c),!0):!1}function g(a,b,c){var e;if(a)if(a.forEach)a.forEach(b,c);else if(a.length!==d)for(e=0;e<a.length;)b.call(c,a[e],e,a),e++;else for(e in a)a.hasOwnProperty(e)&&b.call(c,a[e],e,a)}function h(b,c,d){var e="DEPRECATED METHOD: "+c+"\n"+d+" AT \n";return function(){var c=new Error("get-stack-trace"),d=c&&c.stack?c.stack.replace(/^[^\(]+?[\n$]/gm,"").replace(/^\s+at\s+/gm,"").replace(/^Object.<anonymous>\s*\(/gm,"{anonymous}()@"):"Unknown Stack Trace",f=a.console&&(a.console.warn||a.console.log);return f&&f.call(a.console,e,d),b.apply(this,arguments)}}function i(a,b,c){var d,e=b.prototype;d=a.prototype=Object.create(e),d.constructor=a,d._super=e,c&&la(d,c)}function j(a,b){return function(){return a.apply(b,arguments)}}function k(a,b){return typeof a==oa?a.apply(b?b[0]||d:d,b):a}function l(a,b){return a===d?b:a}function m(a,b,c){g(q(b),function(b){a.addEventListener(b,c,!1)})}function n(a,b,c){g(q(b),function(b){a.removeEventListener(b,c,!1)})}function o(a,b){for(;a;){if(a==b)return!0;a=a.parentNode}return!1}function p(a,b){return a.indexOf(b)>-1}function q(a){return a.trim().split(/\s+/g)}function r(a,b,c){if(a.indexOf&&!c)return a.indexOf(b);for(var d=0;d<a.length;){if(c&&a[d][c]==b||!c&&a[d]===b)return d;d++}return-1}function s(a){return Array.prototype.slice.call(a,0)}function t(a,b,c){for(var d=[],e=[],f=0;f<a.length;){var g=b?a[f][b]:a[f];r(e,g)<0&&d.push(a[f]),e[f]=g,f++}return c&&(d=b?d.sort(function(a,c){return a[b]>c[b]}):d.sort()),d}function u(a,b){for(var c,e,f=b[0].toUpperCase()+b.slice(1),g=0;g<ma.length;){if(c=ma[g],e=c?c+f:b,e in a)return e;g++}return d}function v(){return ua++}function w(b){var c=b.ownerDocument||b;return c.defaultView||c.parentWindow||a}function x(a,b){var c=this;this.manager=a,this.callback=b,this.element=a.element,this.target=a.options.inputTarget,this.domHandler=function(b){k(a.options.enable,[a])&&c.handler(b)},this.init()}function y(a){var b,c=a.options.inputClass;return new(b=c?c:xa?M:ya?P:wa?R:L)(a,z)}function z(a,b,c){var d=c.pointers.length,e=c.changedPointers.length,f=b&Ea&&d-e===0,g=b&(Ga|Ha)&&d-e===0;c.isFirst=!!f,c.isFinal=!!g,f&&(a.session={}),c.eventType=b,A(a,c),a.emit("hammer.input",c),a.recognize(c),a.session.prevInput=c}function A(a,b){var c=a.session,d=b.pointers,e=d.length;c.firstInput||(c.firstInput=D(b)),e>1&&!c.firstMultiple?c.firstMultiple=D(b):1===e&&(c.firstMultiple=!1);var f=c.firstInput,g=c.firstMultiple,h=g?g.center:f.center,i=b.center=E(d);b.timeStamp=ra(),b.deltaTime=b.timeStamp-f.timeStamp,b.angle=I(h,i),b.distance=H(h,i),B(c,b),b.offsetDirection=G(b.deltaX,b.deltaY);var j=F(b.deltaTime,b.deltaX,b.deltaY);b.overallVelocityX=j.x,b.overallVelocityY=j.y,b.overallVelocity=qa(j.x)>qa(j.y)?j.x:j.y,b.scale=g?K(g.pointers,d):1,b.rotation=g?J(g.pointers,d):0,b.maxPointers=c.prevInput?b.pointers.length>c.prevInput.maxPointers?b.pointers.length:c.prevInput.maxPointers:b.pointers.length,C(c,b);var k=a.element;o(b.srcEvent.target,k)&&(k=b.srcEvent.target),b.target=k}function B(a,b){var c=b.center,d=a.offsetDelta||{},e=a.prevDelta||{},f=a.prevInput||{};b.eventType!==Ea&&f.eventType!==Ga||(e=a.prevDelta={x:f.deltaX||0,y:f.deltaY||0},d=a.offsetDelta={x:c.x,y:c.y}),b.deltaX=e.x+(c.x-d.x),b.deltaY=e.y+(c.y-d.y)}function C(a,b){var c,e,f,g,h=a.lastInterval||b,i=b.timeStamp-h.timeStamp;if(b.eventType!=Ha&&(i>Da||h.velocity===d)){var j=b.deltaX-h.deltaX,k=b.deltaY-h.deltaY,l=F(i,j,k);e=l.x,f=l.y,c=qa(l.x)>qa(l.y)?l.x:l.y,g=G(j,k),a.lastInterval=b}else c=h.velocity,e=h.velocityX,f=h.velocityY,g=h.direction;b.velocity=c,b.velocityX=e,b.velocityY=f,b.direction=g}function D(a){for(var b=[],c=0;c<a.pointers.length;)b[c]={clientX:pa(a.pointers[c].clientX),clientY:pa(a.pointers[c].clientY)},c++;return{timeStamp:ra(),pointers:b,center:E(b),deltaX:a.deltaX,deltaY:a.deltaY}}function E(a){var b=a.length;if(1===b)return{x:pa(a[0].clientX),y:pa(a[0].clientY)};for(var c=0,d=0,e=0;b>e;)c+=a[e].clientX,d+=a[e].clientY,e++;return{x:pa(c/b),y:pa(d/b)}}function F(a,b,c){return{x:b/a||0,y:c/a||0}}function G(a,b){return a===b?Ia:qa(a)>=qa(b)?0>a?Ja:Ka:0>b?La:Ma}function H(a,b,c){c||(c=Qa);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return Math.sqrt(d*d+e*e)}function I(a,b,c){c||(c=Qa);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return 180*Math.atan2(e,d)/Math.PI}function J(a,b){return I(b[1],b[0],Ra)+I(a[1],a[0],Ra)}function K(a,b){return H(b[0],b[1],Ra)/H(a[0],a[1],Ra)}function L(){this.evEl=Ta,this.evWin=Ua,this.pressed=!1,x.apply(this,arguments)}function M(){this.evEl=Xa,this.evWin=Ya,x.apply(this,arguments),this.store=this.manager.session.pointerEvents=[]}function N(){this.evTarget=$a,this.evWin=_a,this.started=!1,x.apply(this,arguments)}function O(a,b){var c=s(a.touches),d=s(a.changedTouches);return b&(Ga|Ha)&&(c=t(c.concat(d),"identifier",!0)),[c,d]}function P(){this.evTarget=bb,this.targetIds={},x.apply(this,arguments)}function Q(a,b){var c=s(a.touches),d=this.targetIds;if(b&(Ea|Fa)&&1===c.length)return d[c[0].identifier]=!0,[c,c];var e,f,g=s(a.changedTouches),h=[],i=this.target;if(f=c.filter(function(a){return o(a.target,i)}),b===Ea)for(e=0;e<f.length;)d[f[e].identifier]=!0,e++;for(e=0;e<g.length;)d[g[e].identifier]&&h.push(g[e]),b&(Ga|Ha)&&delete d[g[e].identifier],e++;return h.length?[t(f.concat(h),"identifier",!0),h]:void 0}function R(){x.apply(this,arguments);var a=j(this.handler,this);this.touch=new P(this.manager,a),this.mouse=new L(this.manager,a),this.primaryTouch=null,this.lastTouches=[]}function S(a,b){a&Ea?(this.primaryTouch=b.changedPointers[0].identifier,T.call(this,b)):a&(Ga|Ha)&&T.call(this,b)}function T(a){var b=a.changedPointers[0];if(b.identifier===this.primaryTouch){var c={x:b.clientX,y:b.clientY};this.lastTouches.push(c);var d=this.lastTouches,e=function(){var a=d.indexOf(c);a>-1&&d.splice(a,1)};setTimeout(e,cb)}}function U(a){for(var b=a.srcEvent.clientX,c=a.srcEvent.clientY,d=0;d<this.lastTouches.length;d++){var e=this.lastTouches[d],f=Math.abs(b-e.x),g=Math.abs(c-e.y);if(db>=f&&db>=g)return!0}return!1}function V(a,b){this.manager=a,this.set(b)}function W(a){if(p(a,jb))return jb;var b=p(a,kb),c=p(a,lb);return b&&c?jb:b||c?b?kb:lb:p(a,ib)?ib:hb}function X(){if(!fb)return!1;var b={},c=a.CSS&&a.CSS.supports;return["auto","manipulation","pan-y","pan-x","pan-x pan-y","none"].forEach(function(d){b[d]=c?a.CSS.supports("touch-action",d):!0}),b}function Y(a){this.options=la({},this.defaults,a||{}),this.id=v(),this.manager=null,this.options.enable=l(this.options.enable,!0),this.state=nb,this.simultaneous={},this.requireFail=[]}function Z(a){return a&sb?"cancel":a&qb?"end":a&pb?"move":a&ob?"start":""}function $(a){return a==Ma?"down":a==La?"up":a==Ja?"left":a==Ka?"right":""}function _(a,b){var c=b.manager;return c?c.get(a):a}function aa(){Y.apply(this,arguments)}function ba(){aa.apply(this,arguments),this.pX=null,this.pY=null}function ca(){aa.apply(this,arguments)}function da(){Y.apply(this,arguments),this._timer=null,this._input=null}function ea(){aa.apply(this,arguments)}function fa(){aa.apply(this,arguments)}function ga(){Y.apply(this,arguments),this.pTime=!1,this.pCenter=!1,this._timer=null,this._input=null,this.count=0}function ha(a,b){return b=b||{},b.recognizers=l(b.recognizers,ha.defaults.preset),new ia(a,b)}function ia(a,b){this.options=la({},ha.defaults,b||{}),this.options.inputTarget=this.options.inputTarget||a,this.handlers={},this.session={},this.recognizers=[],this.oldCssProps={},this.element=a,this.input=y(this),this.touchAction=new V(this,this.options.touchAction),ja(this,!0),g(this.options.recognizers,function(a){var b=this.add(new a[0](a[1]));a[2]&&b.recognizeWith(a[2]),a[3]&&b.requireFailure(a[3])},this)}function ja(a,b){var c=a.element;if(c.style){var d;g(a.options.cssProps,function(e,f){d=u(c.style,f),b?(a.oldCssProps[d]=c.style[d],c.style[d]=e):c.style[d]=a.oldCssProps[d]||""}),b||(a.oldCssProps={})}}function ka(a,c){var d=b.createEvent("Event");d.initEvent(a,!0,!0),d.gesture=c,c.target.dispatchEvent(d)}var la,ma=["","webkit","Moz","MS","ms","o"],na=b.createElement("div"),oa="function",pa=Math.round,qa=Math.abs,ra=Date.now;la="function"!=typeof Object.assign?function(a){if(a===d||null===a)throw new TypeError("Cannot convert undefined or null to object");for(var b=Object(a),c=1;c<arguments.length;c++){var e=arguments[c];if(e!==d&&null!==e)for(var f in e)e.hasOwnProperty(f)&&(b[f]=e[f])}return b}:Object.assign;var sa=h(function(a,b,c){for(var e=Object.keys(b),f=0;f<e.length;)(!c||c&&a[e[f]]===d)&&(a[e[f]]=b[e[f]]),f++;return a},"extend","Use `assign`."),ta=h(function(a,b){return sa(a,b,!0)},"merge","Use `assign`."),ua=1,va=/mobile|tablet|ip(ad|hone|od)|android/i,wa="ontouchstart"in a,xa=u(a,"PointerEvent")!==d,ya=wa&&va.test(navigator.userAgent),za="touch",Aa="pen",Ba="mouse",Ca="kinect",Da=25,Ea=1,Fa=2,Ga=4,Ha=8,Ia=1,Ja=2,Ka=4,La=8,Ma=16,Na=Ja|Ka,Oa=La|Ma,Pa=Na|Oa,Qa=["x","y"],Ra=["clientX","clientY"];x.prototype={handler:function(){},init:function(){this.evEl&&m(this.element,this.evEl,this.domHandler),this.evTarget&&m(this.target,this.evTarget,this.domHandler),this.evWin&&m(w(this.element),this.evWin,this.domHandler)},destroy:function(){this.evEl&&n(this.element,this.evEl,this.domHandler),this.evTarget&&n(this.target,this.evTarget,this.domHandler),this.evWin&&n(w(this.element),this.evWin,this.domHandler)}};var Sa={mousedown:Ea,mousemove:Fa,mouseup:Ga},Ta="mousedown",Ua="mousemove mouseup";i(L,x,{handler:function(a){var b=Sa[a.type];b&Ea&&0===a.button&&(this.pressed=!0),b&Fa&&1!==a.which&&(b=Ga),this.pressed&&(b&Ga&&(this.pressed=!1),this.callback(this.manager,b,{pointers:[a],changedPointers:[a],pointerType:Ba,srcEvent:a}))}});var Va={pointerdown:Ea,pointermove:Fa,pointerup:Ga,pointercancel:Ha,pointerout:Ha},Wa={2:za,3:Aa,4:Ba,5:Ca},Xa="pointerdown",Ya="pointermove pointerup pointercancel";a.MSPointerEvent&&!a.PointerEvent&&(Xa="MSPointerDown",Ya="MSPointerMove MSPointerUp MSPointerCancel"),i(M,x,{handler:function(a){var b=this.store,c=!1,d=a.type.toLowerCase().replace("ms",""),e=Va[d],f=Wa[a.pointerType]||a.pointerType,g=f==za,h=r(b,a.pointerId,"pointerId");e&Ea&&(0===a.button||g)?0>h&&(b.push(a),h=b.length-1):e&(Ga|Ha)&&(c=!0),0>h||(b[h]=a,this.callback(this.manager,e,{pointers:b,changedPointers:[a],pointerType:f,srcEvent:a}),c&&b.splice(h,1))}});var Za={touchstart:Ea,touchmove:Fa,touchend:Ga,touchcancel:Ha},$a="touchstart",_a="touchstart touchmove touchend touchcancel";i(N,x,{handler:function(a){var b=Za[a.type];if(b===Ea&&(this.started=!0),this.started){var c=O.call(this,a,b);b&(Ga|Ha)&&c[0].length-c[1].length===0&&(this.started=!1),this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:za,srcEvent:a})}}});var ab={touchstart:Ea,touchmove:Fa,touchend:Ga,touchcancel:Ha},bb="touchstart touchmove touchend touchcancel";i(P,x,{handler:function(a){var b=ab[a.type],c=Q.call(this,a,b);c&&this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:za,srcEvent:a})}});var cb=2500,db=25;i(R,x,{handler:function(a,b,c){var d=c.pointerType==za,e=c.pointerType==Ba;if(!(e&&c.sourceCapabilities&&c.sourceCapabilities.firesTouchEvents)){if(d)S.call(this,b,c);else if(e&&U.call(this,c))return;this.callback(a,b,c)}},destroy:function(){this.touch.destroy(),this.mouse.destroy()}});var eb=u(na.style,"touchAction"),fb=eb!==d,gb="compute",hb="auto",ib="manipulation",jb="none",kb="pan-x",lb="pan-y",mb=X();V.prototype={set:function(a){a==gb&&(a=this.compute()),fb&&this.manager.element.style&&mb[a]&&(this.manager.element.style[eb]=a),this.actions=a.toLowerCase().trim()},update:function(){this.set(this.manager.options.touchAction)},compute:function(){var a=[];return g(this.manager.recognizers,function(b){k(b.options.enable,[b])&&(a=a.concat(b.getTouchAction()))}),W(a.join(" "))},preventDefaults:function(a){var b=a.srcEvent,c=a.offsetDirection;if(this.manager.session.prevented)return void b.preventDefault();var d=this.actions,e=p(d,jb)&&!mb[jb],f=p(d,lb)&&!mb[lb],g=p(d,kb)&&!mb[kb];if(e){var h=1===a.pointers.length,i=a.distance<2,j=a.deltaTime<250;if(h&&i&&j)return}return g&&f?void 0:e||f&&c&Na||g&&c&Oa?this.preventSrc(b):void 0},preventSrc:function(a){this.manager.session.prevented=!0,a.preventDefault()}};var nb=1,ob=2,pb=4,qb=8,rb=qb,sb=16,tb=32;Y.prototype={defaults:{},set:function(a){return la(this.options,a),this.manager&&this.manager.touchAction.update(),this},recognizeWith:function(a){if(f(a,"recognizeWith",this))return this;var b=this.simultaneous;return a=_(a,this),b[a.id]||(b[a.id]=a,a.recognizeWith(this)),this},dropRecognizeWith:function(a){return f(a,"dropRecognizeWith",this)?this:(a=_(a,this),delete this.simultaneous[a.id],this)},requireFailure:function(a){if(f(a,"requireFailure",this))return this;var b=this.requireFail;return a=_(a,this),-1===r(b,a)&&(b.push(a),a.requireFailure(this)),this},dropRequireFailure:function(a){if(f(a,"dropRequireFailure",this))return this;a=_(a,this);var b=r(this.requireFail,a);return b>-1&&this.requireFail.splice(b,1),this},hasRequireFailures:function(){return this.requireFail.length>0},canRecognizeWith:function(a){return!!this.simultaneous[a.id]},emit:function(a){function b(b){c.manager.emit(b,a)}var c=this,d=this.state;qb>d&&b(c.options.event+Z(d)),b(c.options.event),a.additionalEvent&&b(a.additionalEvent),d>=qb&&b(c.options.event+Z(d))},tryEmit:function(a){return this.canEmit()?this.emit(a):void(this.state=tb)},canEmit:function(){for(var a=0;a<this.requireFail.length;){if(!(this.requireFail[a].state&(tb|nb)))return!1;a++}return!0},recognize:function(a){var b=la({},a);return k(this.options.enable,[this,b])?(this.state&(rb|sb|tb)&&(this.state=nb),this.state=this.process(b),void(this.state&(ob|pb|qb|sb)&&this.tryEmit(b))):(this.reset(),void(this.state=tb))},process:function(a){},getTouchAction:function(){},reset:function(){}},i(aa,Y,{defaults:{pointers:1},attrTest:function(a){var b=this.options.pointers;return 0===b||a.pointers.length===b},process:function(a){var b=this.state,c=a.eventType,d=b&(ob|pb),e=this.attrTest(a);return d&&(c&Ha||!e)?b|sb:d||e?c&Ga?b|qb:b&ob?b|pb:ob:tb}}),i(ba,aa,{defaults:{event:"pan",threshold:10,pointers:1,direction:Pa},getTouchAction:function(){var a=this.options.direction,b=[];return a&Na&&b.push(lb),a&Oa&&b.push(kb),b},directionTest:function(a){var b=this.options,c=!0,d=a.distance,e=a.direction,f=a.deltaX,g=a.deltaY;return e&b.direction||(b.direction&Na?(e=0===f?Ia:0>f?Ja:Ka,c=f!=this.pX,d=Math.abs(a.deltaX)):(e=0===g?Ia:0>g?La:Ma,c=g!=this.pY,d=Math.abs(a.deltaY))),a.direction=e,c&&d>b.threshold&&e&b.direction},attrTest:function(a){return aa.prototype.attrTest.call(this,a)&&(this.state&ob||!(this.state&ob)&&this.directionTest(a))},emit:function(a){this.pX=a.deltaX,this.pY=a.deltaY;var b=$(a.direction);b&&(a.additionalEvent=this.options.event+b),this._super.emit.call(this,a)}}),i(ca,aa,{defaults:{event:"pinch",threshold:0,pointers:2},getTouchAction:function(){return[jb]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.scale-1)>this.options.threshold||this.state&ob)},emit:function(a){if(1!==a.scale){var b=a.scale<1?"in":"out";a.additionalEvent=this.options.event+b}this._super.emit.call(this,a)}}),i(da,Y,{defaults:{event:"press",pointers:1,time:251,threshold:9},getTouchAction:function(){return[hb]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distance<b.threshold,f=a.deltaTime>b.time;if(this._input=a,!d||!c||a.eventType&(Ga|Ha)&&!f)this.reset();else if(a.eventType&Ea)this.reset(),this._timer=e(function(){this.state=rb,this.tryEmit()},b.time,this);else if(a.eventType&Ga)return rb;return tb},reset:function(){clearTimeout(this._timer)},emit:function(a){this.state===rb&&(a&&a.eventType&Ga?this.manager.emit(this.options.event+"up",a):(this._input.timeStamp=ra(),this.manager.emit(this.options.event,this._input)))}}),i(ea,aa,{defaults:{event:"rotate",threshold:0,pointers:2},getTouchAction:function(){return[jb]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.rotation)>this.options.threshold||this.state&ob)}}),i(fa,aa,{defaults:{event:"swipe",threshold:10,velocity:.3,direction:Na|Oa,pointers:1},getTouchAction:function(){return ba.prototype.getTouchAction.call(this)},attrTest:function(a){var b,c=this.options.direction;return c&(Na|Oa)?b=a.overallVelocity:c&Na?b=a.overallVelocityX:c&Oa&&(b=a.overallVelocityY),this._super.attrTest.call(this,a)&&c&a.offsetDirection&&a.distance>this.options.threshold&&a.maxPointers==this.options.pointers&&qa(b)>this.options.velocity&&a.eventType&Ga},emit:function(a){var b=$(a.offsetDirection);b&&this.manager.emit(this.options.event+b,a),this.manager.emit(this.options.event,a)}}),i(ga,Y,{defaults:{event:"tap",pointers:1,taps:1,interval:300,time:250,threshold:9,posThreshold:10},getTouchAction:function(){return[ib]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distance<b.threshold,f=a.deltaTime<b.time;if(this.reset(),a.eventType&Ea&&0===this.count)return this.failTimeout();if(d&&f&&c){if(a.eventType!=Ga)return this.failTimeout();var g=this.pTime?a.timeStamp-this.pTime<b.interval:!0,h=!this.pCenter||H(this.pCenter,a.center)<b.posThreshold;this.pTime=a.timeStamp,this.pCenter=a.center,h&&g?this.count+=1:this.count=1,this._input=a;var i=this.count%b.taps;if(0===i)return this.hasRequireFailures()?(this._timer=e(function(){this.state=rb,this.tryEmit()},b.interval,this),ob):rb}return tb},failTimeout:function(){return this._timer=e(function(){this.state=tb},this.options.interval,this),tb},reset:function(){clearTimeout(this._timer)},emit:function(){this.state==rb&&(this._input.tapCount=this.count,this.manager.emit(this.options.event,this._input))}}),ha.VERSION="2.0.8",ha.defaults={domEvents:!1,touchAction:gb,enable:!0,inputTarget:null,inputClass:null,preset:[[ea,{enable:!1}],[ca,{enable:!1},["rotate"]],[fa,{direction:Na}],[ba,{direction:Na},["swipe"]],[ga],[ga,{event:"doubletap",taps:2},["tap"]],[da]],cssProps:{userSelect:"none",touchSelect:"none",touchCallout:"none",contentZooming:"none",userDrag:"none",tapHighlightColor:"rgba(0,0,0,0)"}};var ub=1,vb=2;ia.prototype={set:function(a){return la(this.options,a),a.touchAction&&this.touchAction.update(),a.inputTarget&&(this.input.destroy(),this.input.target=a.inputTarget,this.input.init()),this},stop:function(a){this.session.stopped=a?vb:ub},recognize:function(a){var b=this.session;if(!b.stopped){this.touchAction.preventDefaults(a);var c,d=this.recognizers,e=b.curRecognizer;(!e||e&&e.state&rb)&&(e=b.curRecognizer=null);for(var f=0;f<d.length;)c=d[f],b.stopped===vb||e&&c!=e&&!c.canRecognizeWith(e)?c.reset():c.recognize(a),!e&&c.state&(ob|pb|qb)&&(e=b.curRecognizer=c),f++}},get:function(a){if(a instanceof Y)return a;for(var b=this.recognizers,c=0;c<b.length;c++)if(b[c].options.event==a)return b[c];return null},add:function(a){if(f(a,"add",this))return this;var b=this.get(a.options.event);return b&&this.remove(b),this.recognizers.push(a),a.manager=this,this.touchAction.update(),a},remove:function(a){if(f(a,"remove",this))return this;if(a=this.get(a)){var b=this.recognizers,c=r(b,a);-1!==c&&(b.splice(c,1),this.touchAction.update())}return this},on:function(a,b){if(a!==d&&b!==d){var c=this.handlers;return g(q(a),function(a){c[a]=c[a]||[],c[a].push(b)}),this}},off:function(a,b){if(a!==d){var c=this.handlers;return g(q(a),function(a){b?c[a]&&c[a].splice(r(c[a],b),1):delete c[a]}),this}},emit:function(a,b){this.options.domEvents&&ka(a,b);var c=this.handlers[a]&&this.handlers[a].slice();if(c&&c.length){b.type=a,b.preventDefault=function(){b.srcEvent.preventDefault()};for(var d=0;d<c.length;)c[d](b),d++}},destroy:function(){this.element&&ja(this,!1),this.handlers={},this.session={},this.input.destroy(),this.element=null}},la(ha,{INPUT_START:Ea,INPUT_MOVE:Fa,INPUT_END:Ga,INPUT_CANCEL:Ha,STATE_POSSIBLE:nb,STATE_BEGAN:ob,STATE_CHANGED:pb,STATE_ENDED:qb,STATE_RECOGNIZED:rb,STATE_CANCELLED:sb,STATE_FAILED:tb,DIRECTION_NONE:Ia,DIRECTION_LEFT:Ja,DIRECTION_RIGHT:Ka,DIRECTION_UP:La,DIRECTION_DOWN:Ma,DIRECTION_HORIZONTAL:Na,DIRECTION_VERTICAL:Oa,DIRECTION_ALL:Pa,Manager:ia,Input:x,TouchAction:V,TouchInput:P,MouseInput:L,PointerEventInput:M,TouchMouseInput:R,SingleTouchInput:N,Recognizer:Y,AttrRecognizer:aa,Tap:ga,Pan:ba,Swipe:fa,Pinch:ca,Rotate:ea,Press:da,on:m,off:n,each:g,merge:ta,extend:sa,assign:la,inherit:i,bindFn:j,prefixed:u});var wb="undefined"!=typeof a?a:"undefined"!=typeof self?self:{};wb.Hammer=ha, true?!(__WEBPACK_AMD_DEFINE_RESULT__ = (function(){return ha}).call(exports, __webpack_require__, exports, module),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)):undefined}(window,document,"Hammer");
//# sourceMappingURL=hammer.min.js.map

/***/ }),

/***/ "./runestone/parsons/js/lineGrader.js":
/*!********************************************!*\
  !*** ./runestone/parsons/js/lineGrader.js ***!
  \********************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return LineBasedGrader; });
class LineBasedGrader {
    constructor(problem) {
        this.problem = problem;
    }
    // Use a LIS (Longest Increasing Subsequence) algorithm to return the indexes
    // that are not part of that subsequence.
    inverseLISIndices(arr) {
        // Get all subsequences
        var allSubsequences = [];
        for (var i = 0; i < arr.length; i++) {
            var subsequenceForCurrent = [arr[i]],
                current = arr[i],
                lastElementAdded = -1;
            for (var j = i; j < arr.length; j++) {
                var subsequent = arr[j];
                if (subsequent > current && lastElementAdded < subsequent) {
                    subsequenceForCurrent.push(subsequent);
                    lastElementAdded = subsequent;
                }
            }
            allSubsequences.push(subsequenceForCurrent);
        }
        // Figure out the longest one
        var longestSubsequenceLength = -1;
        var longestSubsequence;
        for (let i in allSubsequences) {
            var subs = allSubsequences[i];
            if (subs.length > longestSubsequenceLength) {
                longestSubsequenceLength = subs.length;
                longestSubsequence = subs;
            }
        }
        // Create the inverse indexes
        var indexes = [];
        var lIndex = 0;
        for (let i = 0; i < arr.length; i++) {
            if (lIndex > longestSubsequence.length) {
                indexes.push(i);
            } else {
                if (arr[i] == longestSubsequence[lIndex]) {
                    lIndex += 1;
                } else {
                    indexes.push(i);
                }
            }
        }
        return indexes;
    }
    // grade that element, returning the state
    grade() {
        var problem = this.problem;
        problem.clearFeedback();
        this.correctLines = 0;
        this.percentLines = 0;
        this.incorrectIndents = 0;
        var solutionLines = problem.solution;
        var answerLines = problem.answerLines();
        var i;
        var state;
        this.percentLines =
            Math.min(answerLines.length, solutionLines.length) /
            Math.max(answerLines.length, solutionLines.length);
        if (answerLines.length < solutionLines.length) {
            state = "incorrectTooShort";
            this.correctLength = false;
        } else if (answerLines.length == solutionLines.length) {
            this.correctLength = true;
        } else {
            this.correctLength = false;
        }

        // Determine whether the code **that is there** is in the correct order
        // If there is too much or too little code this only matters for
        // calculating a percentage score.
        let isCorrectOrder = true;
        this.correctLines = 0;
        this.solutionLength = solutionLines.length;
        let loopLimit = Math.min(solutionLines.length, answerLines.length);
        for (i = 0; i < loopLimit; i++) {
            if (answerLines[i].text !== solutionLines[i].text) {
                isCorrectOrder = false;
            } else {
                this.correctLines += 1;
            }
        }

        // Determine whether blocks are indented correctly
        this.indentLeft = [];
        this.indentRight = [];
        for (i = 0; i < loopLimit; i++) {
            if (answerLines[i].viewIndent() < solutionLines[i].indent) {
                this.indentRight.push(answerLines[i]);
            } else if (answerLines[i].viewIndent() > solutionLines[i].indent) {
                this.indentLeft.push(answerLines[i]);
            }
        }
        this.incorrectIndents =
            this.indentLeft.length + this.indentRight.length;
        if (
            this.incorrectIndents == 0 &&
            isCorrectOrder &&
            this.correctLength
        ) {
            // Perfect
            state = "correct";
        } else if (this.correctLength && isCorrectOrder) {
            state = "incorrectIndent";
        } else if (!isCorrectOrder && state != "incorrectTooShort") {
            state = "incorrectMoveBlocks";
        }
        this.calculatePercent();
        this.graderState = state;
        return state;
    }

    calculatePercent() {
        let numLines = this.percentLines * 0.2;
        let lines = this.problem.answerLines().length;
        let numCorrectBlocks = (this.correctLines / lines) * 0.4;
        let numCorrectIndents =
            ((this.correctLines - this.incorrectIndents) / lines) * 0.4;

        this.problem.percent = numLines + numCorrectBlocks + numCorrectIndents;
    }
}


/***/ }),

/***/ "./runestone/parsons/js/parsons-i18n.en.js":
/*!*************************************************!*\
  !*** ./runestone/parsons/js/parsons-i18n.en.js ***!
  \*************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

$.i18n().load({
    en: {
        msg_parson_check_me: "Check",
        msg_parson_reset: "Reset",
        msg_parson_help: "Help me",
        msg_parson_too_short: "Your program is too short. Add more blocks.",
        msg_parson_drag_from_here: "Drag from here",
        msg_parson_drag_to_here: "Drop blocks here",
        msg_parson_correct_first_try:
            "Perfect!  It took you only one try to solve this.  Great job!",
        msg_parson_correct:
            "Perfect!  It took you $1 tries to solve this.  Click Reset to try to solve it in one attempt.",
        msg_parson_wrong_indent:
            "This block is not indented correctly. Either indent it more by dragging it right or reduce the indention by dragging it left.",
        msg_parson_wrong_indents:
            "These blocks are not indented correctly. To indent a block more, drag it to the right. To reduce the indention, drag it to the left.",
        msg_parson_wrong_order:
            "Highlighted blocks in your program are wrong or are in the wrong order. This can be fixed by moving, removing, or replacing highlighted blocks.",
        msg_parson_arrow_navigate:
            "Arrow keys to navigate. Space to select / deselect block to move.",
        msg_parson_help_info:
            "Click on the Help Me button if you want to make the problem easier",
        msg_parson_not_solution:
            "Disabled an unneeded code block (one that is not part of the solution).",
        msg_parson_provided_indent: "Provided the indentation.",
        msg_parson_combined_blocks: "Combined two code blocks into one.",
        msg_parson_remove_incorrect:
            "Will remove an incorrect code block from answer area",
        msg_parson_will_combine: "Will combine two blocks",
        msg_parson_atleast_three_attempts:
            "You must make at least three distinct full attempts at a solution before you can get help",
        msg_parson_three_blocks_left:
            "There are only 3 correct blocks left.  You should be able to put them in order",
        msg_parson_will_provide_indent: "Will provide indentation",
    },
});


/***/ }),

/***/ "./runestone/parsons/js/parsons-i18n.pt-br.js":
/*!****************************************************!*\
  !*** ./runestone/parsons/js/parsons-i18n.pt-br.js ***!
  \****************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

$.i18n().load({
    "pt-br": {
        msg_parson_check_me: "Verificar",
        msg_parson_reset: "Resetar",
        msg_parson_help:"Ajuda",
        msg_parson_too_short: "Seu programa é muito curto. Adicione mais blocos.",
        msg_parson_drag_from_here: "Arraste daqui",
        msg_parson_drag_to_here: "Largue os blocos aqui",
        msg_parson_correct_first_try:
            "Perfeito! Você levou apenas uma tentativa para resolver. Bom trabalho!",
        msg_parson_correct:
            "Perfeito! Você levou $1 tentativas para resolver. Clique em Resetar para tentar resolver em uma tentativa." ,
        msg_parson_wrong_indent:
            "Este bloco não está indentado corretamente. Indente mais arrastando-o para a direita ou reduza a indentação arrastando para a esquerda.",
        msg_parson_wrong_indents:
            "Estes blocos não estão indentados corretamente. Para indentar mais, arraste o bloco para a direita. Para reduzir a indentação, arraste para a esquerda.",
        msg_parson_wrong_order:
            "Blocos destacados no seu programa estão errados ou estão na ordem errada. Isso pode ser resolvido movendo, excluindo ou substituindo os blocos destacados.",
        msg_parson_arrow_navigate:
            "Use as teclas de setas para navegar. Espaço para selecionar/ desmarcar blocos para mover.",
        msg_parson_help_info:
            "Clique no botão Ajuda se você quiser facilitar o problema",
        msg_parson_not_solution:
            "Foi desabilitado um bloco de código desnecessário (que não faz parte da solução).",
        msg_parson_provided_indent:"Foi fornecida a indentação.",
        msg_parson_combined_blocks:"Dois blocos de códigos foram combinados em um.",
        msg_parson_remove_incorrect:
            "Será removido um bloco de código incorreto da área de resposta",
        msg_parson_will_combine:"Serão combinados dois blocos",
        msg_parson_atleast_three_attempts:
            "Você deve tentar pelo menos três vezes antes de pedir ajuda",
        msg_parson_three_blocks_left:
            "Restam apenas 3 blocos corretos. Você deve colocá-los em ordem",
        msg_parson_will_provide_indent: "Será fornecida a indentação"
    },
});


/***/ }),

/***/ "./runestone/parsons/js/parsons.js":
/*!*****************************************!*\
  !*** ./runestone/parsons/js/parsons.js ***!
  \*****************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Parsons; });
/* harmony import */ var _common_js_runestonebase_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/js/runestonebase.js */ "./runestone/common/js/runestonebase.js");
/* harmony import */ var _parsons_i18n_en_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./parsons-i18n.en.js */ "./runestone/parsons/js/parsons-i18n.en.js");
/* harmony import */ var _parsons_i18n_en_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_parsons_i18n_en_js__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _parsons_i18n_pt_br_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./parsons-i18n.pt-br.js */ "./runestone/parsons/js/parsons-i18n.pt-br.js");
/* harmony import */ var _parsons_i18n_pt_br_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_parsons_i18n_pt_br_js__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var _prettify_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./prettify.js */ "./runestone/parsons/js/prettify.js");
/* harmony import */ var _prettify_js__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_prettify_js__WEBPACK_IMPORTED_MODULE_3__);
/* harmony import */ var _css_parsons_css__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../css/parsons.css */ "./runestone/parsons/css/parsons.css");
/* harmony import */ var _css_parsons_css__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_css_parsons_css__WEBPACK_IMPORTED_MODULE_4__);
/* harmony import */ var _css_prettify_css__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../css/prettify.css */ "./runestone/parsons/css/prettify.css");
/* harmony import */ var _css_prettify_css__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_css_prettify_css__WEBPACK_IMPORTED_MODULE_5__);
/* harmony import */ var _lineGrader__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./lineGrader */ "./runestone/parsons/js/lineGrader.js");
/* harmony import */ var _parsonsLine__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./parsonsLine */ "./runestone/parsons/js/parsonsLine.js");
/* harmony import */ var _parsonsBlock__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./parsonsBlock */ "./runestone/parsons/js/parsonsBlock.js");
/* =====================================================================
==== Parsons Runestone Directive Javascript ============================
======== Renders a Parsons problem based on the HTML created by the
======== parsons.py script and the RST file.
==== CONTRIBUTORS ======================================================
======== Isaiah Mayerchak
======== Jeff Rick
======== Barbara Ericson
======== Cole Bowers
==== Adapted form the original JS Parsons by ===========================
======== Ville Karavirta
======== Petri Ihantola
======== Juha Helminen
======== Mike Hewner
===================================================================== */
/* =====================================================================
==== LineBasedGrader Object ============================================
======== Used for grading a Parsons problem.
==== PROPERTIES ========================================================
======== problem: the Parsons problem
===================================================================== */












/* =====================================================================
==== Parsons Object ====================================================
======== The model and view of a Parsons problem based on what is
======== specified in the HTML, which is based on what is specified
======== in the RST file
==== PROPERTIES ========================================================
======== options: options largely specified from the HTML
======== grader: a LineGrader for grading the problem
======== lines: an array of all ParsonsLine as specified in the problem
======== solution: an array of ParsonsLine in the solution
======== blocks: the current blocks
======== sourceArea: the element that contains the source blocks
======== answerArea: the element that contains the answer blocks
===================================================================== */

/* =====================================================================
==== INITIALIZATION ====================================================
===================================================================== */

var prsList = {}; // Parsons dictionary
class Parsons extends _common_js_runestonebase_js__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(opts) {
        super(opts);
        var orig = opts.orig; // entire <pre> element that will be replaced by new HTML
        this.origElem = $(orig).find("pre")[0]
        // Find the question text and store it in .question
        this.question = $(orig).find(`.parsons_question`)[0];
        this.useRunestoneServices = opts.useRunestoneServices;
        this.divid = opts.orig.id;
        // Set the storageId (key for storing data)
        var storageId = super.localStorageKey();
        this.storageId = storageId;
        this.children = this.origElem.childNodes; // this contains all of the child elements of the entire tag...
        this.contentArray = [];
        Parsons.counter++; //    Unique identifier
        this.counterId = "parsons-" + Parsons.counter;


        // for (var i = 0; i < this.children.length; i++) {
        //     if ($(this.children[i]).is("[data-question]")) {
        //         this.question = this.children[i];
        //         break;
        //     }
        // }
        this.initializeOptions();
        this.grader = new _lineGrader__WEBPACK_IMPORTED_MODULE_6__["default"](this);
        this.grader.showfeedback = this.showfeedback;
        var fulltext = $(this.origElem).html();
        this.blockIndex = 0;
        this.checkCount = 0;
        this.numDistinct = 0;
        this.hasSolved = false;
        this.initializeLines(fulltext.trim());
        this.initializeView();
        // Check the server for an answer to complete things
        if (this.useRunestoneServices || this.graderactive) {
            this.checkServer("parsons");
        } else {
            this.checkLocalStorage();
            this.checkServerComplete = Promise.resolve("no server");
        }
        this.caption = "Parsons";
        this.addCaption("runestone");
    }
    // Based on the data-fields in the original HTML, initialize options
    initializeOptions() {
        var options = {
            pixelsPerIndent: 30,
        };
        // add maxdist and order if present
        var maxdist = $(this.origElem).data("maxdist");
        var order = $(this.origElem).data("order");
        var noindent = $(this.origElem).data("noindent");
        var adaptive = $(this.origElem).data("adaptive");
        var numbered = $(this.origElem).data("numbered");
        options["numbered"] = numbered;
        if (maxdist !== undefined) {
            options["maxdist"] = maxdist;
        }
        if (order !== undefined) {
            // convert order string to array of numbers
            order = order.match(/\d+/g);
            for (var i = 0; i < order.length; i++) {
                order[i] = parseInt(order[i]);
            }
            options["order"] = order;
        }
        if (noindent == undefined) {
            noindent = false;
        }
        options["noindent"] = noindent;
        this.noindent = noindent;
        if (adaptive == undefined) {
            adaptive = false;
        } else if (adaptive) {
            this.initializeAdaptive();
        }
        options["adaptive"] = adaptive;
        // add locale and language
        var locale = eBookConfig.locale;
        if (locale == undefined) {
            locale = "en";
        }
        options["locale"] = locale;
        var language = $(this.origElem).data("language");
        if (language == undefined) {
            language = eBookConfig.language;
            if (language == undefined) {
                language = "python";
            }
        }
        options["language"] = language;
        var prettifyLanguage = {
            python: "prettyprint lang-py",
            java: "prettyprint lang-java",
            javascript: "prettyprint lang-js",
            html: "prettyprint lang-html",
            c: "prettyprint lang-c",
            "c++": "prettyprint lang-cpp",
            ruby: "prettyprint lang-rb",
        }[language];
        if (prettifyLanguage == undefined) {
            prettifyLanguage = "";
        }
        options["prettifyLanguage"] = prettifyLanguage;
        this.options = options;
    }
    // Based on what is specified in the original HTML, create the HTML view
    initializeView() {
        this.containerDiv = document.createElement("div");
        $(this.containerDiv).addClass("parsons alert alert-warning");
        this.containerDiv.id = this.counterId;
        this.parsTextDiv = document.createElement("div");
        $(this.parsTextDiv).addClass("parsons-text");
        this.keyboardTip = document.createElement("div");
        $(this.keyboardTip).attr("role", "tooltip");
        this.keyboardTip.id = this.counterId + "-tip";
        this.keyboardTip.innerHTML = $.i18n("msg_parson_arrow_navigate");
        this.containerDiv.appendChild(this.keyboardTip);
        $(this.keyboardTip).hide();
        this.sortContainerDiv = document.createElement("div");
        $(this.sortContainerDiv).addClass("sortable-code-container");
        $(this.sortContainerDiv).attr(
            "aria-describedby",
            this.counterId + "-tip"
        );
        this.containerDiv.appendChild(this.sortContainerDiv);
        this.sourceRegionDiv = document.createElement("div");
        this.sourceRegionDiv.id = this.counterId + "-sourceRegion";
        $(this.sourceRegionDiv).addClass("sortable-code");
        this.sourceLabel = document.createElement("div");
        $(this.sourceLabel).attr("role", "tooltip");
        this.sourceLabel.id = this.counterId + "-sourceTip";
        this.sourceLabel.innerHTML = $.i18n("msg_parson_drag_from_here");
        this.sourceRegionDiv.appendChild(this.sourceLabel);
        this.sortContainerDiv.appendChild(this.sourceRegionDiv);
        this.sourceArea = document.createElement("div");
        this.sourceArea.id = this.counterId + "-source";
        $(this.sourceArea).addClass("source");
        $(this.sourceArea).attr(
            "aria-describedby",
            this.counterId + "-sourceTip"
        );
        this.sourceRegionDiv.appendChild(this.sourceArea);
        this.answerRegionDiv = document.createElement("div");
        this.answerRegionDiv.id = this.counterId + "-answerRegion";
        $(this.answerRegionDiv).addClass("sortable-code");
        this.answerLabel = document.createElement("div");
        $(this.answerLabel).attr("role", "tooltip");
        this.answerLabel.id = this.counterId + "-answerTip";
        this.answerLabel.innerHTML = $.i18n("msg_parson_drag_to_here");
        this.answerRegionDiv.appendChild(this.answerLabel);
        this.sortContainerDiv.appendChild(this.answerRegionDiv);
        this.answerArea = document.createElement("div");
        this.answerArea.id = this.counterId + "-answer";
        $(this.answerArea).attr(
            "aria-describedby",
            this.counterId + "-answerTip"
        );
        this.answerRegionDiv.appendChild(this.answerArea);
        this.parsonsControlDiv = document.createElement("div");
        $(this.parsonsControlDiv).addClass("parsons-controls");
        this.containerDiv.appendChild(this.parsonsControlDiv);
        var that = this;
        this.checkButton = document.createElement("button");
        $(this.checkButton).attr("class", "btn btn-success");
        this.checkButton.textContent = $.i18n("msg_parson_check_me");
        this.checkButton.id = this.counterId + "-check";
        this.parsonsControlDiv.appendChild(this.checkButton);
        this.checkButton.type = "button";
        this.checkButton.addEventListener("click", function (event) {
            event.preventDefault();
            that.checkCurrentAnswer();
            that.logCurrentAnswer();
            that.renderFeedback();
        });
        this.resetButton = document.createElement("button");
        $(this.resetButton).attr("class", "btn btn-default");
        this.resetButton.textContent = $.i18n("msg_parson_reset");
        this.resetButton.id = this.counterId + "-reset";
        this.resetButton.type = "button";
        this.parsonsControlDiv.appendChild(this.resetButton);
        this.resetButton.addEventListener("click", function (event) {
            event.preventDefault();
            that.clearFeedback();
            that.resetView();
            that.logMove("reset");
            that.setLocalStorage();
        });
        if (this.options.adaptive) {
            this.helpButton = document.createElement("button");
            $(this.helpButton).attr("class", "btn btn-primary");
            this.helpButton.textContent = $.i18n("msg_parson_help");
            this.helpButton.id = this.counterId + "-help";
            this.helpButton.disabled = false; // bje
            this.parsonsControlDiv.appendChild(this.helpButton);
            this.helpButton.addEventListener("click", function (event) {
                event.preventDefault();
                that.helpMe();
            });
        }
        this.messageDiv = document.createElement("div");
        this.messageDiv.id = this.counterId + "-message";
        this.parsonsControlDiv.appendChild(this.messageDiv);
        $(this.messageDiv).hide();
        $(this.origElem).replaceWith(this.containerDiv);
        $(this.containerDiv).closest(".sqcontainer").css("max-width", "none");
        if (this.containerDiv) {
            if ($(this.question).html().match(/^\s+$/)) {
                $(this.question).remove();
            } else {
                $(this.containerDiv).prepend(this.question);
            }
        }

    }
    // Initialize lines and solution properties
    initializeLines(text) {
        this.lines = [];
        // Create the initial blocks
        var textBlocks = text.split("---");
        if (textBlocks.length === 1) {
            // If there are no ---, then every line is its own block
            textBlocks = text.split("\n");
        }
        var solution = [];
        var indents = [];
        for (var i = 0; i < textBlocks.length; i++) {
            var textBlock = textBlocks[i];
            // Figure out options based on the #option
            // Remove the options from the code
            // only options are #paired or #distractor
            var options = {};
            textBlock = textBlock.replace(
                /#(paired|distractor)/,
                function (mystring, arg1) {
                    options[arg1] = true;
                    return "";
                }
            );
            // Create lines
            var lines = [];
            var split = textBlock.split("\n");
            for (var j = 0; j < split.length; j++) {
                var code = split[j];
                // discard blank rows
                if (!/^\s*$/.test(code)) {
                    var line = new _parsonsLine__WEBPACK_IMPORTED_MODULE_7__["default"](this, code);
                    lines.push(line);
                    if (options["paired"]) {
                        line.distractor = true;
                        line.paired = true;
                    } else if (options["distractor"]) {
                        line.distractor = true;
                        line.paired = false;
                    } else {
                        line.distractor = false;
                        line.paired = false;
                        solution.push(line);
                    }
                    if ($.inArray(line.indent, indents) == -1) {
                        indents.push(line.indent);
                    }
                }
            }
            if (lines.length > 0) {
                // Add groupWithNext
                for (j = 0; j < lines.length - 1; j++) {
                    lines[j].groupWithNext = true;
                }
                lines[lines.length - 1].groupWithNext = false;
            }
        }
        // Normalize the indents
        indents = indents.sort(function (a, b) {
            return a - b;
        });
        for (i = 0; i < this.lines.length; i++) {
            line = this.lines[i];
            line.indent = indents.indexOf(line.indent);
        }
        this.solution = solution;
    }
    // Based on the blocks, create the source and answer areas
    initializeAreas(sourceBlocks, answerBlocks, options) {
        // Create blocks property as the sum of the two
        var blocks = [];
        var i, block;
        for (i = 0; i < sourceBlocks.length; i++) {
            block = sourceBlocks[i];
            blocks.push(block);
            this.sourceArea.appendChild(block.view);
        }
        for (i = 0; i < answerBlocks.length; i++) {
            block = answerBlocks[i];
            blocks.push(block);
            this.answerArea.appendChild(block.view);
        }
        this.blocks = blocks;
        // If present, disable some blocks
        var disabled = options.disabled;
        if (disabled !== undefined) {
            for (i = 0; i < blocks.length; i++) {
                block = blocks[i];
                if (disabled.includes(block.lines[0].index)) {
                    $(block.view).addClass("disabled");
                }
            }
        }
        // Determine how much indent should be possible in the answer area
        var indent = 0;
        if (!this.noindent) {
            if (this.options.language == "natural") {
                indent = this.solutionIndent();
            } else {
                indent = Math.max(0, this.solutionIndent());
            }
        }
        this.indent = indent;
        // For rendering, place in an onscreen position
        var isHidden = this.containerDiv.offsetParent == null;
        var replaceElement;
        if (isHidden) {
            replaceElement = document.createElement("div");
            $(this.containerDiv).replaceWith(replaceElement);
            document.body.appendChild(this.containerDiv);
        }
        if (this.options.prettifyLanguage !== "") {
            prettyPrint();
        }
        for (let i = 0; i < this.lines.length; i++) {
            this.lines[i].initializeWidth();
        }
        // Layout the areas
        var areaWidth, areaHeight;
        // Establish the width and height of the droppable areas
        var item, maxFunction;
        areaHeight = 6;
        var height_add = 0;
        if (this.options.numbered != undefined) {
            height_add = 1;
        }
        if (this.options.language == "natural") {
            areaWidth = 300;
            maxFunction = function (item) {
                item.width(areaWidth - 22);
                var addition = 3.8;
                if (item.outerHeight(true) != 38)
                    addition = (2.1 * (item.outerHeight(true) - 38)) / 21;
                areaHeight += item.outerHeight(true) + height_add * addition;
            };
        } else {
            areaWidth = 300;
            // This maxFunction is how Parsons areas width and height were being calculated previously,
            // but at some point .outerHeight and .outerWidth stopped returning correct values
            // causing lines to overflow and display awkwardly. - Vincent Qiu (September 2020)
            // maxFunction = function (item) {
            //     var addition = 3.8;
            //     if (item.outerHeight(true) != 38) addition = 2.1;
            //     areaHeight += item.outerHeight(true) + height_add * addition;
            //     areaWidth = Math.max(areaWidth, item.outerWidth(true));
            // };

            // This new maxFunction is how Parsons areas width and height are being calculated now manually - Vincent Qiu (September 2020)
            maxFunction = function (item) {
                var addition = 3.8;

                // Determine which index within the Parsons block JavaScript object contains the text lines and consequently the passed through data - Vincent Qiu (September 2020)
                var linesIndex;
                var linesItem = item[0].children;
                for (
                    linesIndex = 0;
                    linesIndex < item[0].children.length;
                    linesIndex++
                ) {
                    if (
                        item[0].children[linesIndex].className.includes("lines")
                    ) {
                        break;
                    }
                }

                // Create a canvas and set the passed through fontSize and fontFamily in order to measure text width - Vincent Qiu (September 2020)
                var fontSize = linesItem[linesIndex].children[0].fontSize;
                var fontFamily = linesItem[linesIndex].children[0].fontFamily;
                var tempCanvas = document.createElement("canvas");
                var tempCanvasCtx = tempCanvas.getContext("2d");
                tempCanvasCtx.font = fontSize + " " + fontFamily;

                // Increment Parsons area height based on number of lines of text in the current Parsons block - Vincent Qiu (September 2020)
                var singleHeight = 40;
                var additionalHeight = 20;
                areaHeight += Math.ceil(
                    // For future more accurate height display, this calculation should also be conditionally based on fontFamily
                    singleHeight +
                    (linesItem[linesIndex].children.length - 1) *
                    additionalHeight +
                    height_add * addition
                );

                // Determine the longest text line in the current Parsons block and calculate its width - Vincent Qiu (September 2020)
                var itemLength;
                var pixelsPerIndent;
                var indent;
                var maxInnerText;
                var maxInnerLength = 0;
                var i;
                var widthLimit = 475;
                var longCount = 0;
                for (i = 0; i < linesItem[linesIndex].children.length; i++) {
                    pixelsPerIndent =
                        linesItem[linesIndex].children[i].pixelsPerIndent;
                    indent = linesItem[linesIndex].children[i].indent;
                    itemLength = Math.ceil(
                        pixelsPerIndent * indent +
                        tempCanvasCtx.measureText(
                            linesItem[linesIndex].children[i].innerText
                        ).width
                    );
                    longCount += Math.floor(itemLength / (widthLimit - 29));
                    if (itemLength > maxInnerLength) {
                        maxInnerText =
                            linesItem[linesIndex].children[i].innerText;
                        maxInnerLength = itemLength;
                    }
                }
                areaWidth = Math.max(areaWidth, maxInnerLength + 40);
                // Set width limit and determine how much additional height is needed to accommodate the forced text overflow - Vincent Qiu (September 2020)
                if (areaWidth > widthLimit) {
                    areaWidth = widthLimit;
                    areaHeight += longCount * additionalHeight;
                }
            };
        }
        for (i = 0; i < blocks.length; i++) {
            maxFunction($(blocks[i].view));
        }
        this.areaWidth = areaWidth;
        if (this.options.numbered != undefined) {
            this.areaWidth += 25;
            //areaHeight += (blocks.length);
        }
        // + 40 to areaHeight to provide some additional buffer in case any text overflow still happens - Vincent Qiu (September 2020)
        this.areaHeight = areaHeight + 40;
        $(this.sourceArea).css({
            width: this.areaWidth + 2,
            height: areaHeight,
        });
        $(this.answerArea).css({
            width: this.options.pixelsPerIndent * indent + this.areaWidth + 2,
            height: areaHeight,
        });
        if (indent > 0 && indent <= 4) {
            $(this.answerArea).addClass("answer" + indent);
        } else {
            $(this.answerArea).addClass("answer");
        }
        // Initialize paired distractor decoration
        var bins = [];
        var bin = [];
        for (i = 0; i < this.lines.length; i++) {
            var line = this.lines[i];
            if (line.block() == undefined) {
                if (bin.length > 0) {
                    bins.push(bin);
                    bin = [];
                }
            } else {
                bin.push(line);
                if (!line.groupWithNext) {
                    bins.push(bin);
                    bin = [];
                }
            }
        }
        var pairedBins = [];
        var lineNumbers = [];
        var pairedDivs = [];
        var j;
        if (this.pairDistractors || !this.options.adaptive) {
            for (i = bins.length - 1; i > -1; i--) {
                bin = bins[i];
                if (bin[0].paired) {
                    // Add all in bin to line numbers
                    for (j = bin.length - 1; j > -1; j--) {
                        lineNumbers.unshift(bin[j].index);
                    }
                } else {
                    if (lineNumbers.length > 0) {
                        // Add all in bin to line numbers
                        for (j = bin.length - 1; j > -1; j--) {
                            lineNumbers.unshift(bin[j].index);
                        }
                        pairedBins.unshift(lineNumbers);
                        lineNumbers = [];
                    }
                }
            }
            for (i = 0; i < pairedBins.length; i++) {
                var pairedDiv = document.createElement("div");
                $(pairedDiv).addClass("paired");
                $(pairedDiv).html(
                    "<span id= 'st' style = 'vertical-align: middle; font-weight: bold'>or{</span>"
                );
                pairedDivs.push(pairedDiv);
                this.sourceArea.appendChild(pairedDiv);
            }
        } else {
            pairedBins = [];
        }
        this.pairedBins = pairedBins;
        this.pairedDivs = pairedDivs;
        if (this.options.numbered != undefined) {
            this.addBlockLabels(sourceBlocks.concat(answerBlocks));
        }
        // Update the view
        this.state = undefined; // needs to be here for loading from storage
        this.updateView();
        // Put back into the offscreen position
        if (isHidden) {
            $(replaceElement).replaceWith(this.containerDiv);
        }
    }
    // Make blocks interactive (both drag-and-drop and keyboard)
    initializeInteractivity() {
        for (var i = 0; i < this.blocks.length; i++) {
            this.blocks[i].initializeInteractivity();
        }
        this.initializeTabIndex();
    }
    // Make one block be keyboard accessible
    initializeTabIndex() {
        for (var i = 0; i < this.blocks.length; i++) {
            var block = this.blocks[i];
            if (block.enabled()) {
                block.makeTabIndex();
                return this;
            }
        }
    }
    /* =====================================================================
    ==== SERVER COMMUNICATION ==============================================
    ===================================================================== */
    // Return the argument that is newer based on the timestamp
    newerData(dataA, dataB) {
        var dateA = dataA.timestamp;
        var dateB = dataB.timestamp;
        if (dateA == undefined) {
            return dataB;
        }
        if (dateB == undefined) {
            return dataA;
        }
        dateA = this.dateFromTimestamp(dateA);
        dateB = this.dateFromTimestamp(dateB);
        if (dateA > dateB) {
            return dataA;
        } else {
            return dataB;
        }
    }
    // Based on the data, load
    loadData(data) {
        var sourceHash = data.source;
        if (sourceHash == undefined) {
            // maintain backwards compatibility
            sourceHash = data.trash;
        }
        var answerHash = data.answer;
        var adaptiveHash = data.adaptive;
        var options;
        if (adaptiveHash == undefined) {
            options = {};
        } else {
            options = this.optionsFromHash(adaptiveHash);
        }
        if (options.noindent !== undefined) {
            this.noindent = true;
        }
        if (options.checkCount !== undefined) {
            this.checkCount = options.checkCount;
        }
        if (options.hasSolved !== undefined) {
            this.hasSolved = options.hasSolved;
        }
        if (
            sourceHash == undefined ||
            answerHash == undefined ||
            answerHash.length == 1
        ) {
            this.initializeAreas(this.blocksFromSource(), [], options);
        } else {
            this.initializeAreas(
                this.blocksFromHash(sourceHash),
                this.blocksFromHash(answerHash),
                options
            );
            this.grade = this.grader.grade();
            this.correct = this.grade;
        }
        // Start the interface
        if (this.needsReinitialization !== true) {
            this.initializeInteractivity();
        }
    }
    // Return what is stored in local storage
    localData() {
        var data = localStorage.getItem(this.storageId);
        if (data !== null) {
            if (data.charAt(0) == "{") {
                data = JSON.parse(data);
            } else {
                data = {};
            }
        } else {
            data = {};
        }
        return data;
    }
    // RunestoneBase: Sent when the server has data
    restoreAnswers(serverData) {
        this.loadData(serverData);
    }
    // RunestoneBase: Load what is in local storage
    checkLocalStorage() {
        if (this.graderactive) {
            return;
        }
        this.loadData(this.localData());
    }
    // RunestoneBase: Set the state of the problem in local storage
    setLocalStorage(data) {
        var toStore;
        if (data == undefined) {
            toStore = {
                source: this.sourceHash(),
                answer: this.answerHash(),
                timestamp: new Date(),
            };
            var adaptiveHash = this.adaptiveHash();
            if (adaptiveHash.length > 0) {
                toStore.adaptive = adaptiveHash;
            }
        } else {
            toStore = data;
        }
        localStorage.setItem(this.storageId, JSON.stringify(toStore));
    }
    /* =====================================================================
    ==== LOGGING ===========================================================
    ===================================================================== */
    // Log the interaction with the problem to the server:
    //   start: the user started interacting with this problem
    //   move: the user moved a block to a new position
    //   reset: the reset button was pressed
    //   removeDistractor: "Help Me" removed a distractor
    //   removeIndentation: "Help Me" removed indentation
    //   combineBlocks: "Help Me" combined blocks
    logMove(activity) {
        var event = {
            event: "parsonsMove",
            div_id: this.divid,
            storageid: super.localStorageKey(),
        };
        var act = activity + "|" + this.sourceHash() + "|" + this.answerHash();
        var adaptiveHash = this.adaptiveHash();
        if (adaptiveHash !== "") {
            act = act + "|" + adaptiveHash;
        }
        event.act = act;
        this.logBookEvent(event);
    }
    // Log the answer to the problem
    //   correct: The answer given matches the solution
    //   incorrect*: The answer is wrong for various reasons
    logCurrentAnswer() {
        var event = {
            event: "parsons",
            div_id: this.divid,
        };
        var answerHash = this.answerHash();
        event.answer = answerHash;
        var sourceHash = this.sourceHash();
        event.source = sourceHash;
        var act = sourceHash + "|" + answerHash;
        var adaptiveHash = this.adaptiveHash();
        if (adaptiveHash !== "") {
            event.adaptive = adaptiveHash;
            act = act + "|" + adaptiveHash;
        }
        if (this.grade == "correct") {
            act = "correct|" + act;
            event.correct = "T";
        } else {
            act = "incorrect|" + act;
            event.correct = "F";
        }
        event.act = act;
        this.logBookEvent(event);
    }
    /* =====================================================================
    ==== ACCESSING =========================================================
    ===================================================================== */
    // Answer the hash of the adaptive state
    adaptiveHash() {
        if (!this.options.adaptive) {
            return "";
        }
        var hash = [];
        for (var i = 0; i < this.blocks.length; i++) {
            var block = this.blocks[i];
            if (!block.enabled()) {
                hash.push("d" + block.lines[0].index);
            }
        }
        if (this.noindent !== this.options.noindent) {
            hash.push("i");
        }
        hash.push("c" + this.checkCount);
        if (this.hasSolved) {
            hash.push("s");
        }
        return hash.join("-");
    }
    // Create options for creating blocks based on a hash
    optionsFromHash(hash) {
        var split;
        if (hash === "-" || hash === "" || hash === null) {
            split = [];
        } else {
            split = hash.split("-");
        }
        var options = {};
        var disabled = [];
        for (var i = 0; i < split.length; i++) {
            var key = split[i];
            if (key[0] == "i") {
                options.noindent = true;
            } else if (key[0] == "d") {
                disabled.push(parseInt(key.slice(1)));
            } else if (key[0] == "s") {
                options.hasSolved = true;
            } else if (key[0] == "c") {
                options.checkCount = parseInt(key.slice(1));
            }
        }
        if (disabled.length > 0) {
            options.disabled = disabled;
        }
        return options;
    }
    // Answer the hash of the answer area
    answerHash() {
        var hash = [];
        var blocks = this.answerBlocks();
        for (var i = 0; i < blocks.length; i++) {
            hash.push(blocks[i].hash());
        }
        if (hash.length === 0) {
            return "-";
        } else {
            return hash.join("-");
        }
    }
    // Answer the hash of the source area
    sourceHash() {
        var hash = [];
        var blocks = this.sourceBlocks();
        for (var i = 0; i < blocks.length; i++) {
            hash.push(blocks[i].hash());
        }
        if (hash.length === 0) {
            return "-";
        } else {
            return hash.join("-");
        }
    }
    // Inter-problem adaptive changes
    // Based on the recentAttempts, remove distractors, add indent, combine blocks
    adaptBlocks(input) {
        var blocks = [];
        var distractors = [];
        var block;
        for (var i = 0; i < input.length; i++) {
            block = input[i];
            if (block.isDistractor()) {
                distractors.push(block);
            } else {
                blocks.push(block);
            }
        }
        this.recentAttempts = localStorage.getItem(
            this.adaptiveId + "recentAttempts"
        );
        if (this.recentAttempts == undefined || this.recentAttempts == "NaN") {
            this.recentAttempts = 3;
        }
        var lastestAttemptCount = this.recentAttempts;
        var nBlocks = blocks.length;
        var nBlocksToCombine = 0;
        var nDistractors = distractors.length;
        var nToRemove = 0;
        this.pairDistractors = true;
        var giveIndentation = false;
        if (lastestAttemptCount < 2) {
            // 1 Try
            this.pairDistractors = false;
            this.limitDistractors = false;
        } else if (lastestAttemptCount < 4) {
            // 2-3 Tries
            // Do nothing they are doing normal
            this.pairDistractors = true;
        } else if (lastestAttemptCount < 6) {
            // 4-5 Tries
            // pair distractors
            this.pairDistractors = true;
        } else if (lastestAttemptCount < 8) {
            // 6-7 Tries
            // Remove 50% of distractors
            nToRemove = 0.5 * nDistractors;
            this.pairDistractors = true;
        } else {
            // 8+ Tries
            // Remove all of distractors
            nToRemove = nDistractors;
            this.pairDistractors = true;
        }
        /*
        else if(lastestAttemptCount < 12) { //10-11
            // Remove all distractors and give indentation
            nToRemove = nDistractors;
            giveIndentation = true;
            this.pairDistractors = true;
        } else if(lastestAttemptCount < 14) { // 12-13 Tries
            // Remove all of distractors
            // give indentation
            // reduce problem to 3/4 size
            giveIndentation = true;
            nToRemove = nDistractors;
            nBlocksToCombine = .25 * nBlocks;
            this.pairDistractors = true;
        } else { // >= 14 Tries
            // Remove all of distractors
            // give indentation
            // reduce problem to 1/2 size
            giveIndentation = true;
            nToRemove = nDistractors;
            nBlocksToCombine = .5 * nBlocks;
            this.pairDistractors = true;
        }
        */
        nBlocksToCombine = Math.min(nBlocksToCombine, nBlocks - 3);
        // Never combine so where there are less than three blocks left
        // Remove distractors
        distractors = this.shuffled(distractors);
        distractors = distractors.slice(0, nToRemove);
        var output = [];
        for (i = 0; i < input.length; i++) {
            block = input[i];
            if (!block.isDistractor()) {
                output.push(block);
            } else if ($.inArray(block, distractors) == -1) {
                output.push(block);
            }
        }
        //var output = input;
        if (giveIndentation) {
            for (let i = 0; i < output.length; i++) {
                output[i].addIndent();
            }
            this.indent = 0;
            this.noindent = true;
        }
        // combine blocks
        var solution = [];
        for (i = 0; i < this.lines.length; i++) {
            for (var j = 0; j < output.length; j++) {
                if (output[j].lines[0].index == i) {
                    solution.push(output[j]);
                }
            }
        }
        for (let i = 0; i < nBlocksToCombine; i++) {
            // combine one set of blocks
            var best = -10;
            var combineIndex = -10;
            for (j = 0; j < solution.length - 1; j++) {
                block = solution[j];
                var next = solution[j + 1];
                var rating = 10 - block.lines.length - next.lines.length;
                var blockIndent = block.minimumLineIndent();
                var nextIndent = next.minimumLineIndent();
                if (blockIndent == nextIndent) {
                    rating += 2;
                } else if (blockIndent > nextIndent) {
                    rating -= 1;
                }
                if (
                    block.lines[block.lines.length - 1].indent ==
                    next.lines[0].indent
                ) {
                    rating += 1;
                }
                if (rating >= best) {
                    best = rating;
                    combineIndex = j;
                }
            }
            block = solution[combineIndex];
            next = solution[combineIndex + 1];
            for (j = 0; j < next.lines.length; j++) {
                block.addLine(next.lines[j]);
            }
            var newSolution = [];
            for (j = 0; j < solution.length; j++) {
                if (j !== combineIndex + 1) {
                    newSolution.push(solution[j]);
                }
            }
            var solution = newSolution;
        }
        // reorder
        var combinedOutput = [];
        for (i = 0; i < output.length; i++) {
            for (j = 0; j < solution.length; j++) {
                if (output[i].lines[0].index == solution[j].lines[0].index) {
                    combinedOutput.push(solution[j]);
                }
            }
        }
        return combinedOutput;
    }
    // Return an array of code blocks based on what is specified in the problem
    blocksFromSource() {
        var unorderedBlocks = [];
        var originalBlocks = [];
        var blocks = [];
        var lines = [];
        var block, line, i;
        for (i = 0; i < this.lines.length; i++) {
            line = this.lines[i];
            lines.push(line);
            if (!line.groupWithNext) {
                unorderedBlocks.push(new _parsonsBlock__WEBPACK_IMPORTED_MODULE_8__["default"](this, lines));
                lines = [];
            }
        }
        originalBlocks = unorderedBlocks;
        // Trim the distractors
        var removedBlocks = [];
        if (this.options.maxdist !== undefined) {
            var maxdist = this.options.maxdist;
            var distractors = [];
            for (i = 0; i < unorderedBlocks.length; i++) {
                block = unorderedBlocks[i];
                if (block.lines[0].distractor) {
                    distractors.push(block);
                }
            }
            if (maxdist < distractors.length) {
                distractors = this.shuffled(distractors);
                distractors = distractors.slice(0, maxdist);
                for (i = 0; i < unorderedBlocks.length; i++) {
                    block = unorderedBlocks[i];
                    if (block.lines[0].distractor) {
                        if ($.inArray(block, distractors) > -1) {
                            blocks.push(block);
                        } else {
                            removedBlocks.push(i);
                        }
                    } else {
                        blocks.push(block);
                    }
                }
                unorderedBlocks = blocks;
                blocks = [];
            }
        }

        // This is necessary, set the pairDistractors value before blocks get shuffled - William Li (August 2020)
        if (this.recentAttempts < 2) {
            // 1 Try
            this.pairDistractors = false;
        } else {
            this.pairDistractors = true;
        }

        if (this.options.order === undefined) {
            // Shuffle, respecting paired distractors
            var chunks = [],
                chunk = [];
            for (i = 0; i < unorderedBlocks.length; i++) {
                block = unorderedBlocks[i];
                if (block.lines[0].paired && this.pairDistractors) {
                    // William Li (August 2020)
                    chunk.push(block);
                } else {
                    chunk = [];
                    chunk.push(block);
                    chunks.push(chunk);
                }
            }
            chunks = this.shuffled(chunks);
            for (i = 0; i < chunks.length; i++) {
                chunk = chunks[i];
                if (chunk.length > 1) {
                    // shuffle paired distractors
                    chunk = this.shuffled(chunk);
                    for (j = 0; j < chunk.length; j++) {
                        blocks.push(chunk[j]);
                    }
                } else {
                    blocks.push(chunk[0]);
                }
            }
        } else {
            // Order according to order specified
            for (i = 0; i < this.options.order.length; i++) {
                block = originalBlocks[this.options.order[i]];
                if (
                    block !== undefined &&
                    $.inArray(this.options.order[i], removedBlocks) < 0
                ) {
                    blocks.push(block);
                }
            }
        }
        this.pairDistractors = true;
        if (this.options.adaptive) {
            this.limitDistractors = true;
            blocks = this.adaptBlocks(blocks);
            if (!this.limitDistractors) {
                for (i = 0; i < removedBlocks.length; i++) {
                    var index =
                        this.options.order == undefined
                            ? Math.random(0, blocks.length)
                            : $.inArray(removedBlocks[i], this.options.order);
                    blocks.splice(index, 0, originalBlocks[removedBlocks[i]]);
                }
            }
        }
        if (this.pairDistractors && this.options.order != undefined) {
            //move pairs together
            //Go through array looking for ditractor and its pair
            for (i = 1; i < originalBlocks.length; i++) {
                if (
                    originalBlocks[i].lines[0].paired &&
                    $.inArray(originalBlocks[i], blocks) >= 0
                ) {
                    var j = i;
                    while ($.inArray(originalBlocks[j - 1], blocks) < 0) {
                        // find the paired distractor or solution block it will be next to
                        j--;
                    }
                    var indexTo = $.inArray(originalBlocks[j - 1], blocks);
                    var indexFrom = $.inArray(originalBlocks[i], blocks);
                    blocks.splice(indexFrom, 1);
                    blocks.splice(indexTo, 0, originalBlocks[i]);
                }
            }
        }
        return blocks;
    }
    // Return a codeblock that corresponds to the hash
    blockFromHash(hash) {
        var split = hash.split("_");
        var lines = [];
        for (var i = 0; i < split.length - 1; i++) {
            lines.push(this.lines[split[i]]);
        }
        var block = new _parsonsBlock__WEBPACK_IMPORTED_MODULE_8__["default"](this, lines);
        if (this.noindent) {
            block.indent = 0;
        } else {
            block.indent = Number(split[split.length - 1]);
        }
        return block;
    }
    // Return an array of codeblocks that corresponds to the hash
    blocksFromHash(hash) {
        var split;
        if (hash === "-" || hash === "" || hash === null) {
            split = [];
        } else {
            split = hash.split("-");
        }
        var blocks = [];
        for (var i = 0; i < split.length; i++) {
            blocks.push(this.blockFromHash(split[i]));
        }
        if (this.options.adaptive) {
            return this.adaptBlocks(blocks);
        } else {
            return blocks;
        }
    }
    // Return a block object by the full id including id prefix
    getBlockById(id) {
        for (var i = 0; i < this.blocks.length; i++) {
            var block = this.blocks[i];
            if (block.view.id == id) {
                return block;
            }
        }
        return undefined;
    }
    // Return array of codeblocks that are the solution
    solutionBlocks() {
        var solutionBlocks = [];
        var solutionLines = [];
        for (var i = 0; i < this.lines.length; i++) {
            if (!this.lines[i].distractor) {
                solutionLines.push(this.lines[i]);
            }
        }
        var block = solutionLines[0].block();
        solutionBlocks.push(block);
        for (let i = 1; i < solutionLines.length; i++) {
            var nextBlock = solutionLines[i].block();
            if (block !== nextBlock) {
                block = nextBlock;
                solutionBlocks.push(block);
            }
        }
        return solutionBlocks;
    }
    // Return array of codeblocks based on what is in the source field
    sourceBlocks() {
        var sourceBlocks = [];
        var children = this.sourceArea.childNodes;
        for (var i = 0; i < children.length; i++) {
            var child = children[i];
            if ($(child).hasClass("block")) {
                sourceBlocks.push(this.getBlockById(child.id));
            }
        }
        return sourceBlocks;
    }
    // Return array of enabled codeblocks based on what is in the source field
    enabledSourceBlocks() {
        var all = this.sourceBlocks();
        var enabled = [];
        for (var i = 0; i < all.length; i++) {
            var block = all[i];
            if (block.enabled()) {
                enabled.push(block);
            }
        }
        return enabled;
    }
    // Return array of codeblocks based on what is in the answer field
    answerBlocks() {
        var answerBlocks = [];
        var children = this.answerArea.childNodes;
        for (var i = 0; i < children.length; i++) {
            var block = this.getBlockById(children[i].id);
            if (block !== undefined) {
                answerBlocks.push(block);
            }
        }
        return answerBlocks;
    }
    // Return array of enabled codeblocks based on what is in the answer field
    enabledAnswerBlocks() {
        var all = this.answerBlocks();
        var enabled = [];
        for (var i = 0; i < all.length; i++) {
            var block = all[i];
            if (block.enabled()) {
                enabled.push(block);
            }
        }
        return enabled;
    }
    // Return array of codelines based on what is in the answer field
    answerLines() {
        var answerLines = [];
        var blocks = this.answerBlocks();
        for (var i = 0; i < blocks.length; i++) {
            var block = blocks[i];
            for (var j = 0; j < block.lines.length; j++) {
                answerLines.push(block.lines[j]);
            }
        }
        return answerLines;
    }
    // Go up the hierarchy until you get to a block; return that block element
    getBlockFor(element) {
        var check = element;
        while (!check.classList.contains("block")) {
            check = check.parentElement;
        }
        return check;
    }
    // Return the maximum indent for the solution
    solutionIndent() {
        var indent = 0;
        for (var i = 0; i < this.blocks.length; i++) {
            var block = this.blocks[i];
            indent = Math.max(indent, block.solutionIndent());
        }
        return indent;
    }
    /* =====================================================================
    ==== ACTION ============================================================
    ===================================================================== */
    // The "Check Me" button was pressed.
    checkCurrentAnswer() {
        if (!this.hasSolved) {
            this.checkCount++;
            this.clearFeedback();
            if (this.adaptiveId == undefined) {
                this.adaptiveId = this.storageId;
            }
            // TODO - rendering feedback is buried in the grader.grade method.
            // to disable feedback set this.grader.showfeedback boolean
            this.grader.showfeedback = false;
            this.grade = this.grader.grade();
            if (this.grade == "correct") {
                this.hasSolved = true;
                this.correct = true;
                localStorage.setItem(this.adaptiveId + "Solved", true);
                this.recentAttempts = this.checkCount;
                this.checkCount = 0;
                localStorage.setItem(
                    this.adaptiveId + "recentAttempts",
                    this.recentAttempts
                );
            }
            localStorage.setItem(
                this.adaptiveId + this.divid + "Count",
                this.checkCount
            );
            this.setLocalStorage();
            // if not solved and not too short then check if should provide help
            if (!this.hasSolved && this.grade !== "incorrectTooShort") {
                if (this.canHelp) {
                    // only count the attempt if the answer is different (to prevent gaming)
                    var answerHash = this.answerHash();
                    if (this.lastAnswerHash !== answerHash) {
                        this.numDistinct++;
                        this.lastAnswerHash = answerHash;
                    }
                    // if time to offer help
                    if (this.numDistinct == 3 && !this.gotHelp) {
                        alert($.i18n("msg_parson_help_info"));
                    } // end if
                } // end if can help
            } // end if not solved
        } // end outer if not solved
    }

    renderFeedback() {
        this.grader.showfeedback = true;
        this.grade = this.grader.graderState;
        var feedbackArea;
        var answerArea = $(this.answerArea);

        if (this.showfeedback === true) {
            feedbackArea = $(this.messageDiv);
        } else {
            feedbackArea = $("#doesnotexist");
        }

        if (this.grade === "correct") {
            answerArea.addClass("correct");
            feedbackArea.fadeIn(100);
            feedbackArea.attr("class", "alert alert-info");
            if (this.checkCount > 1) {
                feedbackArea.html(
                    $.i18n("msg_parson_correct", this.checkCount)
                );
            } else {
                feedbackArea.html($.i18n("msg_parson_correct_first_try"));
            }
        }

        if (this.grade === "incorrectTooShort") {
            // too little code
            answerArea.addClass("incorrect");
            feedbackArea.fadeIn(500);
            feedbackArea.attr("class", "alert alert-danger");
            feedbackArea.html($.i18n("msg_parson_too_short"));
        }

        if (this.grade === "incorrectIndent") {
            var incorrectBlocks = [];
            for (let i = 0; i < this.grader.indentLeft.length; i++) {
                block = this.grader.indentLeft[i].block();
                if (incorrectBlocks.indexOf(block) == -1) {
                    incorrectBlocks.push(block);
                    $(block.view).addClass("indentLeft");
                }
            }
            for (let i = 0; i < this.grader.indentRight.length; i++) {
                block = this.grader.indentRight[i].block();
                if (incorrectBlocks.indexOf(block) == -1) {
                    incorrectBlocks.push(block);
                    $(block.view).addClass("indentRight");
                }
            }
            feedbackArea.fadeIn(500);
            feedbackArea.attr("class", "alert alert-danger");
            if (incorrectBlocks.length == 1) {
                feedbackArea.html($.i18n("msg_parson_wrong_indent"));
            } else {
                feedbackArea.html($.i18n("msg_parson_wrong_indents"));
            }
        }

        if (this.grade === "incorrectMoveBlocks") {
            var answerBlocks = this.answerBlocks();
            var inSolution = [];
            var inSolutionIndexes = [];
            var notInSolution = [];
            for (let i = 0; i < answerBlocks.length; i++) {
                var block = answerBlocks[i];
                var index = this.solution.indexOf(block.lines[0]);
                if (index == -1) {
                    notInSolution.push(block);
                } else {
                    inSolution.push(block);
                    inSolutionIndexes.push(index);
                }
            }
            var lisIndexes = this.grader.inverseLISIndices(inSolutionIndexes);
            for (let i = 0; i < lisIndexes.length; i++) {
                notInSolution.push(inSolution[lisIndexes[i]]);
            }
            answerArea.addClass("incorrect");
            feedbackArea.fadeIn(500);
            feedbackArea.attr("class", "alert alert-danger");
            if (this.showfeedback === true) {
                for (let i = 0; i < notInSolution.length; i++) {
                    $(notInSolution[i].view).addClass("incorrectPosition");
                }
            }
            feedbackArea.html($.i18n("msg_parson_wrong_order"));
        }
    }

    /* =====================================================================
    ==== ADAPTIVE ==========================================================
    ===================================================================== */
    // Initialize this problem as adaptive
    //    helpCount = number of checks before help is given (negative)
    //    canHelp = boolean as to whether help can be provided
    //    checkCount = how many times it has been checked before correct
    //    userRating = 0..100 how good the person is at solving problems
    initializeAdaptive() {
        this.adaptiveId = super.localStorageKey();
        this.canHelp = true;
        //this.helpCount = -3; // Number of checks before help is offered
        this.checkCount = 0;
        this.numDistinct = 0; // number of distinct solution attempts (different from previous)
        this.gotHelp = false;
        // Initialize the userRating
        var storageProblem = localStorage.getItem(this.adaptiveId + "Problem");
        if (storageProblem == this.divid) {
            // Already in this problem
            this.checkCount = localStorage.getItem(
                this.adaptiveId + this.divid + "Count"
            );
            if (this.checkCount == undefined) {
                this.checkCount = 0;
            }
            return this;
        }
        var count = localStorage.getItem(
            this.adaptiveId + this.divid + "Count"
        );
        if (count == undefined || count == "NaN") {
            count = 0;
        }
        this.checkCount = count;
        this.recentAttempts = localStorage.getItem(
            this.adaptiveId + "recentAttempts"
        );
        if (this.recentAttempts == undefined || this.recentAttempts == "NaN") {
            this.recentAttempts = 3;
        }
        localStorage.setItem(
            this.adaptiveId + "recentAttempts",
            this.recentAttempts
        );
        localStorage.setItem(this.adaptiveId + "Problem", this.divid);
        localStorage.setItem(
            this.adaptiveId + this.divid + "Count",
            this.checkCount
        );
        localStorage.setItem(this.adaptiveId + "Solved", false);
    }
    // Return a boolean of whether the user must deal with indentation
    usesIndentation() {
        if (this.noindent || this.solutionIndent() == 0) {
            // was $(this.answerArea).hasClass("answer") - bje changed
            return false;
        } else {
            return true;
        }
    }
    // Find a distractor to remove to make the problem easier
    //  * try first in the answer area
    //  * if not, try the source area
    //  * if not, return undefined
    distractorToRemove() {
        var blocks = this.enabledAnswerBlocks();
        var block;
        for (let i = 0; i < blocks.length; i++) {
            block = blocks[i];
            if (block.isDistractor()) {
                return block;
            }
        }
        blocks = this.enabledSourceBlocks();
        for (let i = 0; i < blocks.length; i++) {
            block = blocks[i];
            if (block.isDistractor()) {
                return block;
            }
        }
        return undefined;
    }
    // Return the number of blocks that exist
    numberOfBlocks(fIncludeDistractors = true) {
        var numberOfBlocks = 0;
        for (var i = 0; i < this.blocks.length; i++) {
            if (
                this.blocks[i].enabled() &&
                (fIncludeDistractors || !this.blocks[i].isDistractor())
            ) {
                numberOfBlocks += 1;
            }
        }
        return numberOfBlocks;
    }
    // Remove this distractors to make the problem easier
    removeDistractor(block) {
        // Alert the user to what is happening
        var feedbackArea = $(this.messageDiv);
        feedbackArea.fadeIn(500);
        feedbackArea.attr("class", "alert alert-info");
        feedbackArea.html($.i18n("msg_parson_not_solution"));
        // Stop ability to select
        block.disable();
        // If in answer area, move to source area
        if (!block.inSourceArea()) {
            var sourceRect = this.sourceArea.getBoundingClientRect();
            var startX = block.pageXCenter() - 1;
            var startY = block.pageYCenter();
            var endX =
                sourceRect.left + window.pageXOffset + sourceRect.width / 2;
            var endY =
                sourceRect.top +
                window.pageYOffset +
                block.view.getBoundingClientRect().height / 2;
            var slideUnderBlock = block.slideUnderBlock();
            if (slideUnderBlock !== undefined) {
                endY +=
                    slideUnderBlock.view.getBoundingClientRect().height + 20;
                endY += parseInt($(slideUnderBlock.view).css("top"));
            }
            var that = this;
            $(block.view).css({
                "border-color": "#000",
                "background-color": "#fff",
            });
            $(block.view).animate(
                {
                    opacity: 1.0,
                },
                {
                    duration:
                        Math.sqrt(
                            Math.pow(endY - startY, 2) +
                            Math.pow(endX - startX, 2)
                        ) *
                        4 +
                        500,
                    start: function () {
                        that.moving = block;
                        that.movingX = startX;
                        that.movingY = startY;
                        that.updateView();
                    },
                    progress: function (a, p, c) {
                        that.movingX = startX * (1 - p) + endX * p;
                        that.movingY = startY * (1 - p) + endY * p;
                        that.updateView();
                    },
                    complete: function () {
                        delete that.moving;
                        delete that.movingX;
                        delete that.movingY;
                        that.updateView();
                        $(block.view).animate(
                            {
                                opacity: 0.3,
                                "border-color": "#d3d3d3",
                                "background-color": "#efefef",
                            },
                            {
                                duration: 1000,
                                complete: function () {
                                    $(block.view).css({
                                        opacity: "",
                                        "border-color": "",
                                        "background-color": "",
                                    });
                                    $(block.view).addClass("disabled");
                                },
                            }
                        );
                    },
                }
            );
        } else {
            $(block.view).css({
                "border-color": "#000",
                "background-color": "#fff",
            });
            $(block.view).animate(
                {
                    opacity: 0.3,
                    "border-color": "#d3d3d3",
                    "background-color": "#efefef",
                },
                {
                    duration: 2000,
                    complete: function () {
                        $(block.view).css({
                            "border-color": "",
                            "background-color": "",
                        });
                    },
                }
            );
        }
    }
    // Give the user the indentation
    removeIndentation() {
        // Alert the user to what is happening
        var feedbackArea = $(this.messageDiv);
        feedbackArea.fadeIn(500);
        feedbackArea.attr("class", "alert alert-info");
        feedbackArea.html($.i18n("msg_parson_provided_indent"));
        // Move and resize blocks
        var blockWidth = 200;
        for (var i = 0; i < this.lines.length; i++) {
            var line = this.lines[i];
            var expandedWidth =
                line.width + line.indent * this.options.pixelsPerIndent + 30;
            blockWidth = Math.max(blockWidth, expandedWidth);
        }
        if (this.options.numbered != undefined) {
            blockWidth += 25;
        }
        this.areaWidth = blockWidth + 22;
        var block, indent;
        var sourceBlocks = this.sourceBlocks();
        for (let i = 0; i < sourceBlocks.length; i++) {
            block = sourceBlocks[i];
            indent = block.solutionIndent();
            if (indent == 0) {
                $(block.view).animate(
                    {
                        width: blockWidth,
                    },
                    {
                        duration: 1000,
                    }
                );
            } else {
                $(block.view).animate(
                    {
                        width:
                            blockWidth - indent * this.options.pixelsPerIndent,
                        "padding-left":
                            indent * this.options.pixelsPerIndent + 10,
                    },
                    {
                        duration: 1000,
                    }
                );
            }
        }
        for (let i = 0; i < this.pairedDivs.length; i++) {
            $(this.pairedDivs[i]).animate(
                {
                    width: blockWidth + 34,
                },
                {
                    duration: 1000,
                }
            );
        }
        var answerBlocks = this.answerBlocks();
        for (let i = 0; i < answerBlocks.length; i++) {
            block = answerBlocks[i];
            indent = block.solutionIndent();
            if (indent == 0) {
                $(block.view).animate(
                    {
                        left: 0,
                        width: blockWidth,
                    },
                    {
                        duration: 1000,
                    }
                );
            } else {
                $(block.view).animate(
                    {
                        left: 0,
                        width:
                            blockWidth - indent * this.options.pixelsPerIndent,
                        "padding-left":
                            indent * this.options.pixelsPerIndent + 10,
                    },
                    {
                        duration: 1000,
                    }
                );
            }
        }
        // Resize answer and source area
        $(this.answerArea).removeClass("answer1 answer2 answer3 answer4");
        $(this.answerArea).addClass("answer");
        this.indent = 0;
        this.noindent = true;
        $(this.sourceArea).animate(
            {
                width: this.areaWidth + 2,
            },
            {
                duration: 1000,
            }
        );
        $(this.answerArea).animate(
            {
                width: this.areaWidth + 2,
            },
            {
                duration: 1000,
            }
        );
        // Change the model (with view)
        $(this.answerArea).animate(
            {
                opacity: 1.0,
            },
            {
                duration: 1100,
                complete: function () {
                    $(this.answerArea).css({
                        opacity: "",
                    });
                    // Update the model
                    for (let i = 0; i < sourceBlocks.length; i++) {
                        sourceBlocks[i].addIndent();
                    }
                    for (let i = 0; i < answerBlocks.length; i++) {
                        answerBlocks[i].addIndent();
                    }
                },
            }
        );
    }

    // first check if any solution blocks are in the source still (left side) and not
    // in the answer
    getSolutionBlockInSource() {
        var solutionBlocks = this.solutionBlocks();
        var answerBlocks = this.answerBlocks();
        var sourceBlocks = this.sourceBlocks();
        var solBlock = null;
        var currBlock = null;

        // loop through sourceBlocks and return a block if it is not in the solution
        for (var i = 0; i < sourceBlocks.length; i++) {
            // get the current block from the source
            currBlock = sourceBlocks[i];

            // if currBlock is in the solution and isn't the first block and isn't in the answer
            if (
                solutionBlocks.indexOf(currBlock) > 0 &&
                answerBlocks.indexOf(currBlock) < 0
            ) {
                return currBlock;
            }
        }
        // didn't find any block in the source that is in the solution
        return null;
    }

    // Find a block2 that is furthest from block1 in the answer
    // don't use the very first block in the solution as block2
    getFurthestBlock() {
        var solutionBlocks = this.solutionBlocks();
        var answerBlocks = this.answerBlocks();
        var maxDist = 0;
        var dist = 0;
        var maxBlock = null;
        var currBlock = null;
        var indexSol = 0;
        var prevBlock = null;
        var indexPrev = 0;

        // loop through the blocks in the answer
        for (var i = 0; i < answerBlocks.length; i++) {
            currBlock = answerBlocks[i];
            indexSol = solutionBlocks.indexOf(currBlock);
            if (indexSol > 0) {
                prevBlock = solutionBlocks[indexSol - 1];
                indexPrev = answerBlocks.indexOf(prevBlock);
                //alert("my index " + i + " index prev " + indexPrev);

                // calculate the distance in the answer
                dist = Math.abs(i - indexPrev);
                if (dist > maxDist) {
                    maxDist = dist;
                    maxBlock = currBlock;
                }
            }
        }
        return maxBlock;
    }

    // Combine blocks together
    combineBlocks() {
        var solutionBlocks = this.solutionBlocks();
        var answerBlocks = this.answerBlocks();
        var sourceBlocks = this.sourceBlocks();

        // Alert the user to what is happening
        var feedbackArea = $(this.messageDiv);
        feedbackArea.fadeIn(500);
        feedbackArea.attr("class", "alert alert-info");
        feedbackArea.html($.i18n("msg_parson_combined_blocks"));
        var block1 = null;
        var block2 = null;

        // get a solution block that is still in source (not answer), if any
        block2 = this.getSolutionBlockInSource();

        // if none in source get block that is furthest from block1
        if (block2 == null) {
            block2 = this.getFurthestBlock();
        }

        // get block1 (above block2) in solution
        var index = solutionBlocks.indexOf(block2);
        block1 = solutionBlocks[index - 1];

        // get index of each in answer
        var index1 = answerBlocks.indexOf(block1);
        var index2 = answerBlocks.indexOf(block2);
        var move = false;

        // if both in answer set move based on if directly above each other
        if (index1 >= 0 && index2 >= 0) {
            move = index1 + 1 !== index2;

            // else if both in source set move again based on if above each other
        } else if (index1 < 0 && index2 < 0) {
            index1 = sourceBlocks.indexOf(block1);
            index2 = sourceBlocks.indexOf(block2);
            move = index1 + 1 !== index2;

            // one in source and one in answer so must move
        } else {
            move = true;
            if (index1 < 0) {
                index1 = sourceBlocks.indexOf(block1);
            }
            if (index2 < 0) {
                index2 = sourceBlocks.indexOf(block2);
            }
        }

        var subtract = index2 < index1; // is block2 higher

        if (move) {
            // Move the block
            var startX = block2.pageXCenter() - 1;
            var startY = block2.pageYCenter();
            var endX = block1.pageXCenter() - 1;
            var endY =
                block1.pageYCenter() +
                block1.view.getBoundingClientRect().height / 2 +
                5;
            if (subtract) {
                endY -= block2.view.getBoundingClientRect().height / 2;
            } else {
                endY += block2.view.getBoundingClientRect().height / 2;
            }
            var that = this;
            $(block2.view).animate(
                {
                    opacity: 1,
                },
                {
                    duration: 1000, // 1 seccond
                    start: function () {
                        $(block1.view).css({
                            "border-color": "#000",
                            "background-color": "#fff",
                        });
                        $(block2.view).css({
                            "border-color": "#000",
                            "background-color": "#fff",
                        });
                        block2.lines[0].index += 1000;
                        that.moving = block2;
                        that.movingX = startX;
                        that.movingY = startY;
                        that.updateView();
                    },
                    progress: function (a, p, c) {
                        that.movingX = startX * (1 - p) + endX * p;
                        that.movingY = startY * (1 - p) + endY * p;
                        that.updateView();
                    },
                    complete: function () {
                        delete that.moving;
                        delete that.movingX;
                        delete that.movingY;
                        that.updateView();
                        block2.lines[0].index -= 1000;
                        block1.consumeBlock(block2);
                        $(block1.view).animate(
                            {
                                "border-color": "#d3d3d3",
                                "background-color": "#efefef",
                            },
                            {
                                duration: 1000,
                                complete: function () {
                                    $(block1.view).css({
                                        "border-color": "",
                                        "background-color": "",
                                    });
                                },
                            }
                        );
                    },
                }
            );
        } else {
            $(block2.view).animate(
                {
                    opacity: 1,
                },
                {
                    duration: 1000,
                    start: function () {
                        $(block1.view).css({
                            "border-color": "#000",
                            "background-color": "#fff",
                        });
                        $(block2.view).css({
                            "border-color": "#000",
                            "background-color": "#fff",
                        });
                    },
                    complete: function () {
                        block1.consumeBlock(block2);
                        $(block1.view).animate(
                            {
                                "border-color": "#d3d3d3",
                                "background-color": "#efefef",
                            },
                            {
                                duration: 1000,
                                complete: function () {
                                    $(block1.view).css({
                                        "border-color": "",
                                        "background-color": "",
                                    });
                                },
                            }
                        );
                    },
                }
            );
        }
    }
    // Adapt the problem to be easier
    //  * remove a distractor until none are present
    //  * combine blocks until 3 are left
    makeEasier() {
        var distractorToRemove = this.distractorToRemove();
        if (
            distractorToRemove !== undefined &&
            !distractorToRemove.inSourceArea()
        ) {
            alert($.i18n("msg_parson_remove_incorrect"));
            this.removeDistractor(distractorToRemove);
            this.logMove("removedDistractor-" + distractorToRemove.hash());
        } else {
            var numberOfBlocks = this.numberOfBlocks(false);
            if (numberOfBlocks > 3) {
                alert($.i18n("msg_parson_will_combine"));
                this.combineBlocks();
                this.logMove("combinedBlocks");
            } /*else if(this.numberOfBlocks(true) > 3 && distractorToRemove !==  undefined) {
                alert("Will remove an incorrect code block from source area");
                this.removeDistractor(distractorToRemove);
                this.logMove("removedDistractor-" + distractorToRemove.hash());
            } */ else {
                alert($.i18n("msg_parson_three_blocks_left"));
                this.canHelp = false;
            }
            //if (numberOfBlocks < 5) {
            //	this.canHelp = false;
            //	this.helpButton.disabled = true;
            //}
        }
    }
    // The "Help Me" button was pressed and the problem should be simplified
    helpMe() {
        this.clearFeedback();
        //this.helpCount = -1; // amount to allow for multiple helps in a row
        //if (this.helpCount < 0) {
        //	this.helpCount = Math.max(this.helpCount, -1); // min 1 attempt before more help
        //this.helpButton.disabled = true;
        //}
        // if less than 3 attempts
        if (this.numDistinct < 3) {
            alert($.i18n("msg_parson_atleast_three_attempts"));
        }
        // otherwise give help
        else {
            this.gotHelp = true;
            this.makeEasier();
        }
    }
    /* =====================================================================
    ==== UTILITY ===========================================================
    ===================================================================== */
    // Return a date from a timestamp (either mySQL or JS format)
    dateFromTimestamp(timestamp) {
        var date = new Date(timestamp);
        if (isNaN(date.getTime())) {
            var t = timestamp.split(/[- :]/);
            date = new Date(t[0], t[1] - 1, t[2], t[3], t[4], t[5]);
        }
        return date;
    }
    // A function for returning a shuffled version of an array
    shuffled(array) {
        var currentIndex = array.length;
        var returnArray = array.slice();
        var temporaryValue, randomIndex;
        // While there remain elements to shuffle...
        while (0 !== currentIndex) {
            // Pick a remaining element...
            randomIndex = Math.floor(Math.random() * currentIndex);
            currentIndex -= 1;
            // And swap it with the current element.
            temporaryValue = returnArray[currentIndex];
            returnArray[currentIndex] = returnArray[randomIndex];
            returnArray[randomIndex] = temporaryValue;
        }
        return returnArray;
    }
    /* =====================================================================
    ==== KEYBOARD INTERACTION ==============================================
    ===================================================================== */
    // When the user has entered the Parsons problem via keyboard mode
    enterKeyboardMode() {
        $(this.keyboardTip).show();
        $(this.sourceLabel).hide();
        $(this.answerLabel).hide();
        this.clearFeedback();
    }
    // When the user leaves the Parsons problem via keyboard mode
    exitKeyboardMode() {
        $(this.keyboardTip).hide();
        $(this.sourceLabel).show();
        $(this.answerLabel).show();
    }
    /* =====================================================================
    ==== VIEW ==============================================================
    ===================================================================== */
    // Clear any feedback from the answer area
    clearFeedback() {
        $(this.answerArea).removeClass("incorrect correct");
        var children = this.answerArea.childNodes;
        for (var i = 0; i < children.length; i++) {
            $(children[i]).removeClass(
                "correctPosition incorrectPosition indentLeft indentRight"
            );
        }
        $(this.messageDiv).hide();
    }
    // Disable the interface
    async disableInteraction() {
        // Disable blocks
        await this.checkServerComplete;
        console.log("disabling blocks");
        if (this.blocks !== undefined) {
            for (var i = 0; i < this.blocks.length; i++) {
                var block = this.blocks[i];
                block.disable();
            }
        }
        // Hide buttons
        $(this.checkButton).hide();
        $(this.resetButton).hide();
    }
    // Based on the moving element, etc., establish the moving state
    //   rest: not moving
    //   source: moving inside source area
    //   answer: moving inside answer area
    //   moving: moving outside areas
    movingState() {
        if (this.moving == undefined) {
            return "rest";
        }
        var x = this.movingX - window.pageXOffset;
        var y = this.movingY - window.pageYOffset;
        // Check if in answer area
        var bounds = this.answerArea.getBoundingClientRect();
        if (
            x >= bounds.left &&
            x <= bounds.right &&
            y >= bounds.top &&
            y <= bounds.bottom
        ) {
            return "answer";
        }
        // Check if in source area
        bounds = this.sourceArea.getBoundingClientRect();
        if (
            x >= bounds.left &&
            x <= bounds.right &&
            y >= bounds.top &&
            y <= bounds.bottom
        ) {
            return "source";
        }
        return "moving";
    }
    // Update the Parsons view
    // This gets called when dragging a block
    updateView() {
        // Based on the new and the old state, figure out what to update
        var state = this.state;
        var newState = this.movingState();
        var updateSource = true;
        var updateAnswer = true;
        var updateMoving = newState == "moving";
        if (state == newState) {
            if (newState == "rest") {
                updateSource = false;
                updateAnswer = false;
            } else if (newState == "source") {
                updateAnswer = false;
            } else if (newState == "answer") {
                updateSource = false;
            } else if (newState == "moving") {
                updateAnswer = false;
                updateSource = false;
            }
        }
        var movingHeight;
        if (this.moving !== undefined) {
            // Must get height here as detached items don't have height
            movingHeight = $(this.moving.view).outerHeight(true);
            $(this.moving.view).detach();
        }
        var positionTop, width;
        var baseWidth = this.areaWidth - 22;
        // Update the Source Area
        if (updateSource) {
            positionTop = 0;
            var blocks = this.sourceBlocks();
            if (newState == "source") {
                var hasInserted = false;
                var movingBin = this.moving.pairedBin();
                var binForBlock = [];
                for (i = 0; i < blocks.length; i++) {
                    binForBlock.push(blocks[i].pairedBin());
                }
                if (!binForBlock.includes(movingBin)) {
                    movingBin = -1;
                }
                var insertPositions = [];
                if (binForBlock.length == 0) {
                    insertPositions.push(0);
                } else {
                    if (movingBin == -1) {
                        insertPositions.push(0);
                    } else if (binForBlock[0] == movingBin) {
                        insertPositions.push(0);
                    }
                    for (i = 1; i < blocks.length; i++) {
                        if (binForBlock[i - 1] == movingBin) {
                            insertPositions.push(i);
                        } else if (binForBlock[i] == movingBin) {
                            insertPositions.push(i);
                        } else if (
                            movingBin == -1 &&
                            binForBlock[i - 1] != binForBlock[i]
                        ) {
                            insertPositions.push(i);
                        }
                    }
                    if (movingBin == -1) {
                        insertPositions.push(binForBlock.length);
                    } else if (
                        binForBlock[binForBlock.length - 1] == movingBin
                    ) {
                        insertPositions.push(binForBlock.length);
                    }
                }
                var x =
                    this.movingX -
                    this.sourceArea.getBoundingClientRect().left -
                    window.pageXOffset -
                    baseWidth / 2 -
                    11;
                var y =
                    this.movingY -
                    this.sourceArea.getBoundingClientRect().top -
                    window.pageYOffset;
                for (i = 0; i < blocks.length; i++) {
                    var item = blocks[i];
                    var j;
                    if (!hasInserted && insertPositions.includes(i)) {
                        var testHeight = $(item.view).outerHeight(true);
                        for (j = i + 1; j < blocks.length; j++) {
                            if (insertPositions.includes(j)) {
                                break;
                            }
                            testHeight += $(blocks[j].view).outerHeight(true);
                        }
                        if (
                            y - positionTop < movingHeight + testHeight / 2 ||
                            i == insertPositions[insertPositions.length - 1]
                        ) {
                            hasInserted = true;
                            this.sourceArea.insertBefore(
                                this.moving.view,
                                item.view
                            );
                            $(this.moving.view).css({
                                left: x,
                                top: y - movingHeight / 2,
                                width: baseWidth,
                                "z-index": 3,
                            });
                            positionTop = positionTop + movingHeight;
                        }
                    }
                    $(item.view).css({
                        left: 0,
                        top: positionTop,
                        width: baseWidth,
                        "z-index": 2,
                    });
                    positionTop = positionTop + $(item.view).outerHeight(true);
                }
                if (!hasInserted) {
                    $(this.moving.view).appendTo(
                        "#" + this.counterId + "-source"
                    );
                    $(this.moving.view).css({
                        left: x,
                        top: y - $(this.moving.view).outerHeight(true) / 2,
                        width: baseWidth,
                        "z-index": 3,
                    });
                }
            } else {
                for (var i = 0; i < blocks.length; i++) {
                    item = blocks[i];
                    $(item.view).css({
                        left: 0,
                        top: positionTop,
                        width: baseWidth,
                        "z-index": 2,
                    });
                    positionTop = positionTop + $(item.view).outerHeight(true);
                }
            }
            // Update the Paired Distractor Indicators
            for (i = 0; i < this.pairedBins.length; i++) {
                var bin = this.pairedBins[i];
                var matching = [];
                for (j = 0; j < blocks.length; j++) {
                    block = blocks[j];
                    if (block.matchesBin(bin)) {
                        matching.push(block);
                    }
                }
                var div = this.pairedDivs[i];
                if (matching.length == 0) {
                    $(div).hide();
                } else {
                    $(div).show();
                    var height = -5;
                    height += parseInt(
                        $(matching[matching.length - 1].view).css("top")
                    );
                    height -= parseInt($(matching[0].view).css("top"));
                    height += $(matching[matching.length - 1].view).outerHeight(
                        true
                    );
                    $(div).css({
                        left: -6,
                        top: $(matching[0].view).css("top"),
                        width: baseWidth + 34,
                        height: height,
                        "z-index": 1,
                        "text-indent": -30,
                        "padding-top": (height - 70) / 2,
                        overflow: "visible",
                        "font-size": 43,
                        "vertical-align": "middle",
                        color: "#7e7ee7",
                    });
                    $(div).html(
                        "<span id= 'st' style = 'vertical-align: middle; font-weight: bold; font-size: 15px'>or</span>{"
                    );
                }
                if (matching.length == 1) {
                    $(div).html("");
                }
            }
        }
        // Update the Answer Area
        if (updateAnswer) {
            var block, indent;
            positionTop = 0;
            width =
                this.areaWidth +
                this.indent * this.options.pixelsPerIndent -
                22;
            var blocks = this.answerBlocks();
            if (newState == "answer") {
                var hasInserted = false;
                var x =
                    this.movingX -
                    this.answerArea.getBoundingClientRect().left -
                    window.pageXOffset -
                    baseWidth / 2 -
                    11;
                var movingIndent = Math.round(x / this.options.pixelsPerIndent);
                if (movingIndent < 0) {
                    movingIndent = 0;
                } else if (movingIndent > this.indent) {
                    movingIndent = this.indent;
                } else {
                    x = movingIndent * this.options.pixelsPerIndent;
                }
                var y =
                    this.movingY -
                    this.answerArea.getBoundingClientRect().top -
                    window.pageYOffset;
                this.moving.indent = movingIndent;
                for (i = 0; i < blocks.length; i++) {
                    block = blocks[i];
                    if (!hasInserted) {
                        if (
                            y - positionTop <
                            (movingHeight + $(block.view).outerHeight(true)) / 2
                        ) {
                            hasInserted = true;
                            this.answerArea.insertBefore(
                                this.moving.view,
                                block.view
                            );
                            $(this.moving.view).css({
                                left: x,
                                top: y - movingHeight / 2,
                                width: baseWidth,
                                "z-index": 3,
                            });
                            positionTop = positionTop + movingHeight;
                        }
                    }
                    indent = block.indent * this.options.pixelsPerIndent;
                    $(block.view).css({
                        left: indent,
                        top: positionTop,
                        width: width - indent,
                        "z-index": 2,
                    });
                    positionTop = positionTop + $(block.view).outerHeight(true);
                }
                if (!hasInserted) {
                    $(this.moving.view).appendTo(
                        "#" + this.counterId + "-answer"
                    );
                    $(this.moving.view).css({
                        left: x,
                        top: y - $(this.moving.view).outerHeight(true) / 2,
                        width: baseWidth,
                        "z-index": 3,
                    });
                }
            } else {
                for (let i = 0; i < blocks.length; i++) {
                    block = blocks[i];
                    indent = block.indent * this.options.pixelsPerIndent;
                    $(block.view).css({
                        left: indent,
                        top: positionTop,
                        width: width - indent,
                        "z-index": 2,
                    });
                    positionTop = positionTop + $(block.view).outerHeight(true);
                }
            }
        }
        // Update the Moving Area
        if (updateMoving) {
            // Add it to the lowest place in the source area
            movingBin = this.moving.pairedBin();
            if (movingBin == -1) {
                $(this.moving.view).appendTo("#" + this.counterId + "-source");
            } else {
                var before;
                blocks = this.sourceBlocks;
                for (i = 0; i < blocks.length; i++) {
                    block = blocks[i];
                    if (block.pairedBin() == movingBin) {
                        before = i + 1;
                    }
                }
                if (before == undefined || before == blocks.length) {
                    $(this.moving.view).appendTo(
                        "#" + this.counterId + "-source"
                    );
                } else {
                    this.sourceArea.insertBefore(
                        this.moving.view,
                        blocks[before].view
                    );
                }
            }
            // Place in the middle of the mouse cursor
            $(this.moving.view).css({
                left:
                    this.movingX -
                    this.sourceArea.getBoundingClientRect().left -
                    window.pageXOffset -
                    $(this.moving.view).outerWidth(true) / 2,
                top:
                    this.movingY -
                    this.sourceArea.getBoundingClientRect().top -
                    window.pageYOffset -
                    movingHeight / 2,
                width: baseWidth,
                "z-index": 3,
            });
        }
        state = newState;
        this.state = state;
    }
    addBlockLabels(blocks) {
        var bin = -1;
        var binCount = 0;
        var binChildren = 0;
        var blocksNotInBins = 0;
        for (let i = 0; i < blocks.length; i++) {
            if (blocks[i].pairedBin() == -1) {
                blocksNotInBins++;
            }
        }
        for (let i = 0; i < blocks.length; i++) {
            var currentBin = blocks[i].pairedBin();
            if (currentBin == -1 || currentBin != bin) {
                bin = currentBin;
                binChildren = 0;
                binCount++;
            }
            var label =
                "" +
                binCount +
                (currentBin != -1
                    ? String.fromCharCode(97 + binChildren)
                    : " ");
            if (
                binCount < 10 &&
                blocksNotInBins + this.pairedBins.length >= 10
            ) {
                label += " ";
            }
            blocks[i].addLabel(label, 0);
            binChildren++;
        }
        if (blocksNotInBins + this.pairedBins.length >= 10) {
            this.areaWidth += 5;
            $(this.sourceArea).css({
                width: $(this.sourceArea).width() + 5,
            });
            $(this.answerArea).css({
                width: $(this.answerArea).width() + 5,
            });
        }
    }
    // Put all the blocks back into the source area, reshuffling as necessary
    resetView() {
        // Clear everything
        this.clearFeedback();
        var scrollTop = document.body.scrollTop;
        var block;
        for (let i = 0; i < this.blocks.length; i++) {
            block = this.blocks[i];
            for (var j = 0; j < block.lines.length; j++) {
                var children = $(block.lines[j].view).find(".block-label");
                for (var c = 0; c < children.length; c++) {
                    children[c].remove();
                }
            }
            block.destroy();
            $(this.blocks[i].view).detach();
        }
        delete this.blocks;
        this.blockIndex = 0;
        for (let i = 0; i < this.pairedDivs.length; i++) {
            $(this.pairedDivs[i]).detach();
        }
        $(this.sourceArea).attr("style", "");
        $(this.answerArea).removeClass();
        $(this.answerArea).attr("style", "");
        this.noindent = this.options.noindent;
        // Reinitialize
        if (this.hasSolved) {
            this.checkCount = 0;
            this.numDistinct = 0;
            this.hasSolved = false;
            if (this.options.adaptive) {
                this.canHelp = true;
            }
            //this.helpCount = -3; // enable after 3 failed attempts
            //this.helpButton.disabled = true;
            localStorage.setItem(this.adaptiveId + "Problem", this.divid);
            localStorage.setItem(
                this.adaptiveId + this.divid + "Count",
                this.checkCount
            );
            localStorage.setItem(this.adaptiveId + "Solved", false);
        }
        this.initializeAreas(this.blocksFromSource(), [], {});
        this.initializeInteractivity();
        document.body.scrollTop = scrollTop;
    }
}

Parsons.counter = 0;

$(document).bind("runestone:login-complete", function () {
    $("[data-component=parsons]").each(function (index) {
        if ($(this).closest("[data-component=timedAssessment]").length == 0) {
            try {
                prsList[this.id] = new Parsons({
                    orig: this,
                    useRunestoneServices: eBookConfig.useRunestoneServices,
                });
            } catch (err) {
                console.log(`Error rendering Parsons Problem ${this.id}
                             Details: ${err}`);
                console.log(err.stack);
            }
        }
    });
});


/***/ }),

/***/ "./runestone/parsons/js/parsonsBlock.js":
/*!**********************************************!*\
  !*** ./runestone/parsons/js/parsonsBlock.js ***!
  \**********************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return ParsonsBlock; });
/* harmony import */ var _hammer_min_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./hammer.min.js */ "./runestone/parsons/js/hammer.min.js");
/* harmony import */ var _hammer_min_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_hammer_min_js__WEBPACK_IMPORTED_MODULE_0__);
/* =====================================================================
==== ParsonsBlock Object ===============================================
======== The model and view of a code block.
==== PROPERTIES ========================================================
======== problem: the Parsons problem
======== lines: an array of ParsonsLine in this block
======== indent: indent based on movement
======== view: an element for viewing this object
======== labels: [label, line] the labels numbering the block and the lines they go on
======== hammer: the controller based on hammer.js
===================================================================== */



// Initialize based on the problem and the lines
class ParsonsBlock {
    constructor(problem, lines) {
        this.problem = problem;
        this.lines = lines;
        this.indent = 0;
        this.labels = [];
        // Create view, adding view of lines and updating indent
        var view = document.createElement("div");
        view.id = problem.counterId + "-block-" + problem.blockIndex;
        problem.blockIndex += 1;
        $(view).addClass("block");
        var sharedIndent = lines[0].indent;
        for (let i = 1; i < lines.length; i++) {
            sharedIndent = Math.min(sharedIndent, lines[i].indent);
        }
        var lineDiv = document.createElement("div");
        $(lineDiv).addClass("lines");
        $(view).append(lineDiv);
        for (let i = 0; i < lines.length; i++) {
            var line = lines[i];
            var lineIndent;
            if (problem.noindent) {
                lineIndent = line.indent;
            } else {
                lineIndent = line.indent - sharedIndent;
            }
            $(line.view).removeClass("indent1 indent2 indent3 indent4");
            if (lineIndent > 0) {
                $(line.view).addClass("indent" + lineIndent);
            }
            lineDiv.appendChild(line.view);
        }
        var labelDiv = document.createElement("div");
        $(labelDiv).addClass("labels");
        if (this.problem.options.numbered == "left") {
            $(lineDiv).addClass("border_left");
            $(view).prepend(labelDiv);
            $(view).css({
                "justify-content": "flex-start",
            });
        } else if (this.problem.options.numbered == "right") {
            $(labelDiv).addClass("border_left");
            $(labelDiv).css({
                float: "right",
            });
            $(view).css({
                "justify-content": "space-between",
            });
            $(view).append(labelDiv);
        }
        this.view = view;
    }
    // Add a line (from another block) to this block
    addLine(line) {
        $(line.view).removeClass("indent1 indent2 indent3 indent4");
        if (this.problem.noindent) {
            if (line.indent > 0) {
                $(line.view).addClass("indent" + line.indent);
            }
        } else {
            var lines = this.lines;
            var sharedIndent = lines[0].indent;
            for (let i = 1; i < lines.length; i++) {
                sharedIndent = Math.min(sharedIndent, lines[i].indent);
            }
            if (sharedIndent < line.indent) {
                $(line.view).addClass("indent" + (line.indent - sharedIndent));
            } else if (sharedIndent > line.indent) {
                for (let i = 0; i < lines.length; i++) {
                    $(lines[i].view).removeClass(
                        "indent1 indent2 indent3 indent4"
                    );
                    $(lines[i].view).addClass(
                        "indent" + (lines[i].indent - line.indent)
                    );
                }
            }
        }
        this.lines.push(line);
        $(this.view).children(".lines")[0].appendChild(line.view);
    }
    // Add the contents of that block to myself and then delete that block
    consumeBlock(block) {
        for (let i = 0; i < block.lines.length; i++) {
            this.addLine(block.lines[i]);
        }
        if ($(block.view).attr("tabindex") == "0") {
            this.makeTabIndex();
        }
        $(block.view).detach();
        var newBlocks = [];
        for (let i = 0; i < this.problem.blocks.length; i++) {
            if (this.problem.blocks[i] !== block) {
                newBlocks.push(this.problem.blocks[i]);
            }
        }
        for (let i = 0; i < block.labels.length; i++) {
            this.addLabel(
                block.labels[i][0],
                this.lines.length - block.lines.length + block.labels[i][1]
            );
        }
        this.problem.blocks = newBlocks;
        this.problem.state = undefined;
        this.problem.updateView();
    }
    // Update the model and view when block is converted to contain indent
    addIndent() {
        // Update the lines model / view
        for (let i = 0; i < this.lines.length; i++) {
            var line = this.lines[i];
            if (line.indent > 0) {
                $(line.view).removeClass("indent1 indent2 indent3 indent4");
                $(line.view).addClass("indent" + line.indent);
            }
        }
        // Update the block model / view
        this.indent = 0;
        $(this.view).css({
            "padding-left": "",
            width: this.problem.areaWidth - 22,
        });
    }
    // Add a label to block and update its view
    addLabel(label, line) {
        var div = document.createElement("div");
        $(div).addClass("block-label");
        if (this.problem.options.numbered == "right") {
            $(div).addClass("right-label");
        }
        if (this.problem.options.numbered == "left") {
            $(div).addClass("left-label");
        }
        $(div).append(document.createTextNode(label));
        $(this.view).children(".labels")[0].append(div);
        if (this.labels.length != 0) {
            $(div).css(
                "margin-top",
                (line - this.labels[this.labels.length - 1][1] - 1) *
                    this.lines[line].view.offsetHeight
            );
        }
        this.labels.push([label, line]);
    }
    // Initialize Interactivity
    initializeInteractivity() {
        if ($(this.view).hasClass("disabled")) {
            return this;
        }
        $(this.view).attr("tabindex", "-1");
        this.hammer = new _hammer_min_js__WEBPACK_IMPORTED_MODULE_0___default.a.Manager(this.view, {
            recognizers: [
                [
                    _hammer_min_js__WEBPACK_IMPORTED_MODULE_0___default.a.Pan,
                    {
                        direction: _hammer_min_js__WEBPACK_IMPORTED_MODULE_0___default.a.DIRECTION_ALL,
                        threshold: 0,
                        pointers: 1,
                    },
                ],
            ],
        });
        var that = this;
        this.hammer.on("panstart", function (event) {
            that.panStart(event);
        });
        this.hammer.on("panend", function (event) {
            that.panEnd(event);
        });
        this.hammer.on("panmove", function (event) {
            that.panMove(event);
        });
    }
    // Return a boolean as to whether this block is able to be selected
    enabled() {
        return $(this.view).attr("tabindex") !== undefined;
    }
    // Return a boolean as to whether this block is a distractor
    isDistractor() {
        return this.lines[0].distractor;
    }
    // Return a boolean as to whether this block is in the source area
    inSourceArea() {
        var children = this.problem.sourceArea.childNodes;
        for (let i = 0; i < children.length; i++) {
            var item = children[i];
            if (item.id == this.view.id) {
                return true;
            }
        }
        return false;
    }
    // Return the page X position of the center of the view
    pageXCenter() {
        var boundingRect = this.view.getBoundingClientRect();
        var pageXCenter =
            window.pageXOffset + boundingRect.left + boundingRect.width / 2;
        return pageXCenter;
    }
    // Return the page Y position of the center of the view
    pageYCenter() {
        var boundingRect = this.view.getBoundingClientRect();
        var pageYCenter =
            window.pageYOffset + boundingRect.top + boundingRect.height / 2;
        return pageYCenter;
    }
    // Of all the line indents, return the minimum value
    minimumLineIndent() {
        var minimumLineIndent = this.lines[0].indent;
        for (let i = 1; i < this.lines.length; i++) {
            minimumLineIndent = Math.min(
                this.lines[i].indent,
                minimumLineIndent
            );
        }
        return minimumLineIndent;
    }
    // Return the last block in the source area that matches the paired bin it is in
    slideUnderBlock() {
        var sourceBlocks = this.problem.sourceBlocks();
        if (sourceBlocks.length == 0) {
            return undefined;
        }
        var pairedBin = this.pairedBin();
        if (pairedBin == -1) {
            return sourceBlocks[sourceBlocks.length - 1];
        }
        for (let i = sourceBlocks.length - 1; i >= 0; i--) {
            var block = sourceBlocks[i];
            if (block.pairedBin() == pairedBin) {
                return block;
            }
        }
        return sourceBlocks[sourceBlocks.length - 1];
    }
    // Return which paired bin it is in (-1) if not
    pairedBin() {
        var pairedBins = this.problem.pairedBins;
        for (var i = 0; i < pairedBins.length; i++) {
            if (this.matchesBin(pairedBins[i])) {
                return i;
            }
        }
        return -1;
    }
    // Return true if all lines are in that bin
    matchesBin(bin) {
        for (var i = 0; i < this.lines.length; i++) {
            var test = this.lines[i].index;
            if (bin.indexOf(test) == -1) {
                return false;
            }
        }
        return true;
    }
    // Return a list of indexes where this block could be inserted before
    validSourceIndexes() {
        var blocks = this.problem.sourceBlocks();
        var indexes = [];
        var pairedBin = this.pairedBin();
        var i, lastBin;
        if (pairedBin >= 0) {
            for (i = 0; i < blocks.length; i++) {
                var block = blocks[i];
                if (block.view.id !== this.view.id) {
                    var blockBin = block.pairedBin();
                    if (blockBin == pairedBin) {
                        indexes.push(i);
                    } else if (lastBin == pairedBin) {
                        indexes.push(i);
                    }
                    lastBin = blockBin;
                }
            }
            if (lastBin == pairedBin) {
                indexes.push(blocks.length);
            }
            if (indexes.length > 0) {
                return indexes;
            }
        }
        for (i = 0; i < blocks.length; i++) {
            let block = blocks[i];
            if (block.view.id !== this.view.id) {
                let blockBin = block.pairedBin();
                if (blockBin !== lastBin) {
                    indexes.push(i);
                } else if (blockBin == -1) {
                    indexes.push(i);
                }
                lastBin = blockBin;
            }
        }
        indexes.push(blocks.length);
        return indexes;
    }
    // A measure of how far the middle of this block is vertically positioned
    verticalOffset() {
        var verticalOffset;
        if (this.inSourceArea()) {
            verticalOffset = this.problem.sourceArea.getBoundingClientRect()
                .top;
        } else {
            verticalOffset = this.problem.answerArea.getBoundingClientRect()
                .top;
        }
        verticalOffset =
            this.view.getBoundingClientRect().top +
            this.view.getBoundingClientRect().bottom -
            verticalOffset * 2;
        return verticalOffset;
    }
    // This block just gained textual focus
    newFocus() {
        if (this.problem.textFocus == undefined) {
            this.problem.enterKeyboardMode();
            this.problem.textFocus = this;
            this.problem.textMove = false;
            $(this.view).addClass("down");
        } else if (this.problem.textFocus == this) {
            if (this.problem.textMove) {
                $(this.view).addClass("up");
            } else {
                $(this.view).addClass("down");
            }
        } else {
            // already in keyboard mode
            this.problem.textFocus = this;
            this.problem.textMove = false;
            $(this.view).addClass("down");
        }
        this.problem.textMoving = false;
    }
    // This block just lost textual focus
    releaseFocus() {
        $(this.view).removeClass("down up");
        if (this.problem.textFocus == this) {
            if (!this.problem.textMoving) {
                // exit out of problem but stay way into problem
                this.problem.textFocus = undefined;
                if (this.problem.textMove) {
                    this.problem.logMove("kmove");
                    this.problem.textMove = false;
                }
                this.problem.exitKeyboardMode();
            }
        } else {
            // become selectable, but not active
            $(this.view).attr("tabindex", "-1");
            $(this.view).unbind("focus");
            $(this.view).unbind("blur");
            $(this.view).unbind("keydown");
        }
    }
    // Make this block into the keyboard entry point
    makeTabIndex() {
        $(this.view).attr("tabindex", "0");
        var that = this;
        $(this.view).focus(function () {
            that.newFocus();
        });
        $(this.view).blur(function () {
            that.releaseFocus();
        });
        $(this.view).keydown(function (event) {
            that.keyDown(event);
        });
    }
    // Called to disable interaction for the future
    disable() {
        if (this.hammer !== undefined) {
            this.hammer.set({ enable: false });
        }
        if ($(this.view).attr("tabindex") == "0") {
            this.releaseFocus();
            $(this.view).removeAttr("tabindex");
            this.problem.initializeTabIndex();
        } else {
            $(this.view).removeAttr("tabindex");
        }
    }
    // Enable functionality after reset button has been pressed
    resetView() {
        if (this.hammer !== undefined) {
            this.hammer.set({ enable: true });
        }
        if (!$(this.view)[0].hasAttribute("tabindex")) {
            $(this.view).attr("tabindex", "-1");
        }
        $(this.view).css({ opacity: "" });
    }
    // Called to destroy interaction for the future
    destroy() {
        if (this.hammer !== undefined) {
            this.hammer.destroy();
            delete this.hammer;
        }
        if ($(this.view).attr("tabindex") == "0") {
            this.releaseFocus();
        }
        $(this.view).removeAttr("tabindex");
    }
    // Called when a block is picked up
    panStart(event) {
        this.problem.clearFeedback();
        if (this.problem.started == undefined) {
            // log the first time that something gets moved
            this.problem.started = true;
            this.problem.logMove("start");
        }
        if (this.problem.textFocus !== undefined) {
            // stop text focus when dragging
            this.problem.textFocus.releaseFocus();
        }
        this.problem.moving = this;
        // Update the view
        this.problem.movingX = event.srcEvent.pageX;
        this.problem.movingY = event.srcEvent.pageY;
        this.problem.updateView();
    }
    // Called when a block is dropped
    panEnd(event) {
        this.problem.isAnswered = true;
        delete this.problem.moving;
        delete this.problem.movingX;
        delete this.problem.movingY;
        this.problem.updateView();
        this.problem.logMove("move");
    }
    // Called when a block is moved
    panMove(event) {
        // Update the view
        this.problem.movingX = event.srcEvent.pageX;
        this.problem.movingY = event.srcEvent.pageY;
        this.problem.updateView();
    }
    // Handle a keypress event when in focus
    keyDown(event) {
        if (this.problem.started == undefined) {
            // log the first time that something gets moved
            this.problem.started = true;
            this.problem.logMove("kstart");
        }
        switch (event.keyCode) {
            case 37: // left
                if (this.problem.textMove) {
                    this.moveLeft();
                } else {
                    this.selectLeft();
                }
                break;
            case 38: // up
                if (this.problem.textMove) {
                    this.moveUp();
                } else {
                    this.selectUp();
                }
                event.preventDefault();
                break;
            case 39: // right
                if (this.problem.textMove) {
                    this.moveRight();
                } else {
                    this.selectRight();
                }
                event.preventDefault();
                break;
            case 40: // down
                if (this.problem.textMove) {
                    this.moveDown();
                } else {
                    this.selectDown();
                }
                event.preventDefault();
                break;
            case 32: // space
                this.toggleMove();
                event.preventDefault();
                break;
            case 13: // enter
                this.toggleMove();
                event.preventDefault();
                break;
        }
    }
    // Move block left
    moveLeft() {
        var index, block;
        if (!this.inSourceArea()) {
            if (this.indent == 0) {
                // move to source area
                var blocks = this.problem.sourceBlocks();
                var offset = this.verticalOffset();
                var validSourceIndexes = this.validSourceIndexes();
                for (var i = 0; i < validSourceIndexes.length; i++) {
                    index = validSourceIndexes[i];
                    if (index == blocks.length) {
                        this.problem.textMoving = true;
                        this.problem.sourceArea.appendChild(this.view);
                        $(this.view).focus();
                        this.problem.state = undefined;
                        this.problem.updateView();
                        return this;
                    } else {
                        block = blocks[index];
                        if (block.verticalOffset() >= offset) {
                            break;
                        }
                    }
                }
                this.problem.textMoving = true;
                this.problem.sourceArea.insertBefore(this.view, block.view);
                $(this.view).focus();
            } else {
                // reduce indent
                this.indent = this.indent - 1;
            }
            this.problem.state = undefined;
            this.problem.updateView();
        }
    }
    // Move block up
    moveUp() {
        if (this.inSourceArea()) {
            let blocks = this.problem.sourceBlocks();
            var offset = this.verticalOffset();
            var validSourceIndexes = this.validSourceIndexes();
            for (let i = 0; i < validSourceIndexes.length; i++) {
                var index =
                    validSourceIndexes[validSourceIndexes.length - 1 - i];
                if (index < blocks.length) {
                    var block = blocks[index];
                    if (block.verticalOffset() < offset) {
                        this.problem.textMoving = true;
                        this.problem.sourceArea.insertBefore(
                            this.view,
                            block.view
                        );
                        $(this.view).focus();
                        this.problem.state = undefined;
                        this.problem.updateView();
                        return this;
                    }
                }
            }
        } else {
            let blocks = this.problem.answerBlocks();
            for (let i = 0; i < blocks.length; i++) {
                if (blocks[i].view.id == this.view.id) {
                    if (i == 0) {
                        return this;
                    }
                    this.problem.textMoving = true;
                    this.problem.answerArea.insertBefore(
                        this.view,
                        blocks[i - 1].view
                    );
                    $(this.view).focus();
                    this.problem.state = undefined;
                    this.problem.updateView();
                }
            }
        }
    }
    // Move block right
    moveRight() {
        if (this.inSourceArea()) {
            // move to answer area
            this.indent = 0;
            var offset = this.verticalOffset();
            var answerBlocks = this.problem.answerBlocks();
            for (var i = 0; i < answerBlocks.length; i++) {
                var item = answerBlocks[i];
                var itemOffset = item.verticalOffset();
                if (itemOffset >= offset) {
                    this.problem.textMoving = true;
                    this.problem.answerArea.insertBefore(this.view, item.view);
                    $(this.view).focus();
                    this.problem.state = undefined;
                    this.problem.updateView();
                    return this;
                }
            }
            this.problem.textMoving = true;
            this.problem.answerArea.appendChild(this.view);
            $(this.view).focus();
            this.problem.state = undefined;
            this.problem.updateView();
        } else {
            // in answer area: increase the indent
            if (this.indent !== this.problem.indent) {
                this.indent = this.indent + 1;
                this.problem.state = undefined;
                this.problem.updateView();
            }
        }
    }
    // Move block down
    moveDown() {
        if (this.inSourceArea()) {
            let blocks = this.problem.sourceBlocks();
            var validSourceIndexes = this.validSourceIndexes();
            for (let i = 0; i < blocks.length; i++) {
                if (blocks[i].view.id == this.view.id) {
                    var myIndex = i;
                }
            }
            for (let i = 0; i < validSourceIndexes.length; i++) {
                var index = validSourceIndexes[i];
                if (index == blocks.length) {
                    this.problem.textMoving = true;
                    this.problem.sourceArea.appendChild(this.view);
                    $(this.view).focus();
                    this.problem.state = undefined;
                    this.problem.updateView();
                    return this;
                } else if (index - myIndex > 1) {
                    this.problem.textMoving = true;
                    this.problem.sourceArea.insertBefore(
                        this.view,
                        blocks[index].view
                    );
                    $(this.view).focus();
                    this.problem.state = undefined;
                    this.problem.updateView();
                    return this;
                }
            }
        } else {
            let blocks = this.problem.answerBlocks();
            for (var i = 0; i < blocks.length; i++) {
                if (blocks[i].view.id == this.view.id) {
                    if (i == blocks.length - 1) {
                        return this;
                    } else if (i == blocks.length - 2) {
                        this.problem.textMoving = true;
                        this.problem.answerArea.appendChild(this.view);
                    } else {
                        this.problem.textMoving = true;
                        this.problem.answerArea.insertBefore(
                            this.view,
                            blocks[i + 2].view
                        );
                    }
                    $(this.view).focus();
                    this.problem.state = undefined;
                    this.problem.updateView();
                }
            }
        }
    }
    // Move selection left
    selectLeft() {
        if (!this.inSourceArea()) {
            var offset = this.verticalOffset();
            var sourceBlocks = this.problem.enabledSourceBlocks();
            if (sourceBlocks.length == 0) {
                return this;
            }
            var chooseNext = sourceBlocks[0];
            var chooseOffset = chooseNext.verticalOffset() - offset;
            for (var i = 1; i < sourceBlocks.length; i++) {
                var item = sourceBlocks[i];
                var itemOffset = item.verticalOffset() - offset;
                if (Math.abs(itemOffset) < Math.abs(chooseOffset)) {
                    chooseNext = item;
                    chooseOffset = itemOffset;
                }
            }
            this.problem.textFocus = chooseNext;
            chooseNext.makeTabIndex();
            $(chooseNext.view).focus();
        }
    }
    // Move selection up
    selectUp() {
        var chooseNext = false;
        var blocks;
        if (this.inSourceArea()) {
            blocks = this.problem.enabledSourceBlocks();
        } else {
            blocks = this.problem.enabledAnswerBlocks();
        }
        for (var i = blocks.length - 1; i >= 0; i--) {
            var item = blocks[i];
            if (chooseNext) {
                this.problem.textFocus = item;
                item.makeTabIndex();
                $(item.view).focus();
                return this;
            } else {
                if (item.view.id == this.view.id) {
                    chooseNext = true;
                }
            }
        }
    }
    // Move selection right
    selectRight() {
        if (this.inSourceArea()) {
            var offset = this.verticalOffset();
            var blocks = this.problem.enabledAnswerBlocks();
            if (blocks.length == 0) {
                return this;
            }
            var chooseNext = blocks[0];
            var chooseOffset = chooseNext.verticalOffset() - offset;
            for (var i = 1; i < blocks.length; i++) {
                var item = blocks[i];
                var itemOffset = item.verticalOffset() - offset;
                if (Math.abs(itemOffset) < Math.abs(chooseOffset)) {
                    chooseNext = item;
                    chooseOffset = itemOffset;
                }
            }
            this.problem.textFocus = chooseNext;
            chooseNext.makeTabIndex();
            $(chooseNext.view).focus();
        }
    }
    // Move selection down
    selectDown() {
        var chooseNext = false;
        var blocks;
        if (this.inSourceArea()) {
            blocks = this.problem.enabledSourceBlocks();
        } else {
            blocks = this.problem.enabledAnswerBlocks();
        }
        for (var i = 0; i < blocks.length; i++) {
            var item = blocks[i];
            if (chooseNext) {
                this.problem.textFocus = item;
                item.makeTabIndex();
                $(item.view).focus();
                return this;
            } else {
                if (item.view.id == this.view.id) {
                    chooseNext = true;
                }
            }
        }
    }
    // Toggle whether to move this block
    toggleMove() {
        if (this.problem.textMove) {
            $(this.view).removeClass("up");
            $(this.view).addClass("down");
            this.problem.textMove = false;
            this.problem.logMove("kmove");
        } else {
            $(this.view).removeClass("down");
            $(this.view).addClass("up");
            this.problem.textMove = true;
        }
    }
    // Answer a string that represents this codeblock for saving
    hash() {
        var hash = "";
        for (var i = 0; i < this.lines.length; i++) {
            hash += this.lines[i].index + "_";
        }
        hash += this.indent;
        return hash;
    }
    // Answer what the indent should be for the solution
    solutionIndent() {
        var sharedIndent = this.lines[0].indent;
        for (var i = 1; i < this.lines.length; i++) {
            sharedIndent = Math.min(sharedIndent, this.lines[i].indent);
        }
        return sharedIndent;
    }
}


/***/ }),

/***/ "./runestone/parsons/js/parsonsLine.js":
/*!*********************************************!*\
  !*** ./runestone/parsons/js/parsonsLine.js ***!
  \*********************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return ParsonsLine; });
/* =====================================================================
==== ParsonsLine Object ================================================
======== The model and view of a line of code.
======== Based on what is specified in the problem.
======== ParsonBlock objects have one or more of these.
==== PROPERTIES ========================================================
======== problem: the Parsons problem
======== index: the index of the line in the problem
======== text: the text of the code line
======== indent: the indent level
======== view: an element for viewing this object
======== distractor: whether it is a distractor
======== paired: whether it is a paired distractor
======== groupWithNext: whether it is grouped with the following line
======== width: the pixel width when rendered
============ in the initial grouping
===================================================================== */
// Initialize from codestring

class ParsonsLine {
    constructor(problem, codestring) {
        this.problem = problem;
        this.index = problem.lines.length;
        var trimmed = codestring.replace(/\s*$/, "");
        this.text = trimmed.replace(/^\s*/, "");
        this.indent = trimmed.length - this.text.length;
        // Create the View
        var view;
        if (problem.options.language == "natural") {
            view = document.createElement("p");
        } else {
            view = document.createElement("code");
            $(view).addClass(problem.options.prettifyLanguage);
        }
        view.id = problem.counterId + "-line-" + this.index;
        view.innerHTML += this.text;
        this.view = view;
        problem.lines.push(this);
    }
    // Initialize what width the line would naturally have (without indent)
    initializeWidth() {
        // this.width does not appear to be used anywhere later
        // since changing the value of this.width appears to have no effect. - Vincent Qiu (September 2020)
        this.width =
            $(this.view).outerWidth(true) -
            this.problem.options.pixelsPerIndent * this.indent;

        // Pass this information on to be used in class Parsons function initializeAreas
        // to manually determine appropriate widths - Vincent Qiu (September 2020)
        this.view.fontSize = window
            .getComputedStyle(this.view, null)
            .getPropertyValue("font-size");
        this.view.pixelsPerIndent = this.problem.options.pixelsPerIndent;
        this.view.indent = this.indent;

        // Figure out which typeface will be rendered by comparing text widths to browser default - Vincent Qiu (September 2020)
        var tempCanvas = document.createElement("canvas");
        var tempCanvasCtx = tempCanvas.getContext("2d");
        var possibleFonts = window
            .getComputedStyle(this.view, null)
            .getPropertyValue("font-family")
            .split(", ");
        var fillerText = "abcdefghijklmnopqrstuvwxyz0123456789,./!@#$%^&*-+";
        tempCanvasCtx.font = this.view.fontSize + " serif";
        var serifWidth = tempCanvasCtx.measureText(fillerText).width;
        for (let i = 0; i < possibleFonts.length; i++) {
            if (possibleFonts[i].includes('"')) {
                possibleFonts[i] = possibleFonts[i].replaceAll('"', "");
            }
            if (possibleFonts[i].includes("'")) {
                possibleFonts[i] = possibleFonts[i].replaceAll("'", "");
            }
            tempCanvasCtx.font = this.view.fontSize + " " + possibleFonts[i];
            if (tempCanvasCtx.measureText(fillerText).width !== serifWidth) {
                this.view.fontFamily = possibleFonts[i];
                break;
            }
        }
    }
    // Answer the block that this line is currently in
    block() {
        for (let i = 0; i < this.problem.blocks.length; i++) {
            var block = this.problem.blocks[i];
            for (var j = 0; j < block.lines.length; j++) {
                if (block.lines[j] === this) {
                    return block;
                }
            }
        }
        return undefined;
    }
    // Answer the indent based on the view
    viewIndent() {
        if (this.problem.noindent) {
            return this.indent;
        } else {
            var block = this.block();
            return this.indent - block.solutionIndent() + block.indent;
        }
    }
}


/***/ }),

/***/ "./runestone/parsons/js/prettify.js":
/*!******************************************!*\
  !*** ./runestone/parsons/js/prettify.js ***!
  \******************************************/
/*! no static exports found */
/***/ (function(module, exports) {

function H() {
    var x =
        navigator &&
        navigator.userAgent &&
        /\bMSIE 6\./.test(navigator.userAgent);
    H = function() {
        return x;
    };
    return x;
}
(function() {
    function x(b) {
        b = b.split(/ /g);
        var a = {};
        for (var c = b.length; --c >= 0; ) {
            var d = b[c];
            if (d) a[d] = null;
        }
        return a;
    }
    var y = "break continue do else for if return while ",
        U =
            y +
            "auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile ",
        D =
            U +
            "catch class delete false import new operator private protected public this throw true try ",
        I =
            D +
            "alignof align_union asm axiom bool concept concept_map const_cast constexpr decltype dynamic_cast explicit export friend inline late_check mutable namespace nullptr reinterpret_cast static_assert static_cast template typeid typename typeof using virtual wchar_t where ",
        J =
            D +
            "boolean byte extends final finally implements import instanceof null native package strictfp super synchronized throws transient ",
        V =
            J +
            "as base by checked decimal delegate descending event fixed foreach from group implicit in interface internal into is lock object out override orderby params readonly ref sbyte sealed stackalloc string select uint ulong unchecked unsafe ushort var ",
        K =
            D +
            "debugger eval export function get null set undefined var with Infinity NaN ",
        L =
            "caller delete die do dump elsif eval exit foreach for goto if import last local my next no our print package redo require sub undef unless until use wantarray while BEGIN END ",
        M =
            y +
            "and as assert class def del elif except exec finally from global import in is lambda nonlocal not or pass print raise try with yield False True None ",
        N =
            y +
            "alias and begin case class def defined elsif end ensure false in module next nil not or redo rescue retry self super then true undef unless until when yield BEGIN END ",
        O = y + "case done elif esac eval fi function in local set then until ",
        W = I + V + K + L + M + N + O;
    function X(b) {
        return (b >= "a" && b <= "z") || (b >= "A" && b <= "Z");
    }
    function u(b, a, c, d) {
        b.unshift(c, d || 0);
        try {
            a.splice.apply(a, b);
        } finally {
            b.splice(0, 2);
        }
    }
    var Y = (function() {
            var b = [
                    "!",
                    "!=",
                    "!==",
                    "#",
                    "%",
                    "%=",
                    "&",
                    "&&",
                    "&&=",
                    "&=",
                    "(",
                    "*",
                    "*=",
                    "+=",
                    ",",
                    "-=",
                    "->",
                    "/",
                    "/=",
                    ":",
                    "::",
                    ";",
                    "<",
                    "<<",
                    "<<=",
                    "<=",
                    "=",
                    "==",
                    "===",
                    ">",
                    ">=",
                    ">>",
                    ">>=",
                    ">>>",
                    ">>>=",
                    "?",
                    "@",
                    "[",
                    "^",
                    "^=",
                    "^^",
                    "^^=",
                    "{",
                    "|",
                    "|=",
                    "||",
                    "||=",
                    "~",
                    "break",
                    "case",
                    "continue",
                    "delete",
                    "do",
                    "else",
                    "finally",
                    "instanceof",
                    "return",
                    "throw",
                    "try",
                    "typeof"
                ],
                a =
                    "(?:(?:(?:^|[^0-9.])\\.{1,3})|(?:(?:^|[^\\+])\\+)|(?:(?:^|[^\\-])-)";
            for (var c = 0; c < b.length; ++c) {
                var d = b[c];
                a += X(d.charAt(0))
                    ? "|\\b" + d
                    : "|" + d.replace(/([^=<>:&])/g, "\\$1");
            }
            a += "|^)\\s*$";
            return new RegExp(a);
        })(),
        P = /&/g,
        Q = /</g,
        R = />/g,
        Z = /\"/g;
    function $(b) {
        return b
            .replace(P, "&amp;")
            .replace(Q, "&lt;")
            .replace(R, "&gt;")
            .replace(Z, "&quot;");
    }
    function E(b) {
        return b
            .replace(P, "&amp;")
            .replace(Q, "&lt;")
            .replace(R, "&gt;");
    }
    var aa = /&lt;/g,
        ba = /&gt;/g,
        ca = /&apos;/g,
        da = /&quot;/g,
        ea = /&amp;/g,
        fa = /&nbsp;/g;
    function ga(b) {
        var a = b.indexOf("&");
        if (a < 0) return b;
        for (--a; (a = b.indexOf("&#", a + 1)) >= 0; ) {
            var c = b.indexOf(";", a);
            if (c >= 0) {
                var d = b.substring(a + 3, c),
                    g = 10;
                if (d && d.charAt(0) === "x") {
                    d = d.substring(1);
                    g = 16;
                }
                var e = parseInt(d, g);
                if (!isNaN(e))
                    b =
                        b.substring(0, a) +
                        String.fromCharCode(e) +
                        b.substring(c + 1);
            }
        }
        return b
            .replace(aa, "<")
            .replace(ba, ">")
            .replace(ca, "'")
            .replace(da, '"')
            .replace(ea, "&")
            .replace(fa, " ");
    }
    function S(b) {
        return "XMP" === b.tagName;
    }
    function z(b, a) {
        switch (b.nodeType) {
            case 1:
                var c = b.tagName.toLowerCase();
                a.push("<", c);
                for (var d = 0; d < b.attributes.length; ++d) {
                    var g = b.attributes[d];
                    if (!g.specified) continue;
                    a.push(" ");
                    z(g, a);
                }
                a.push(">");
                for (var e = b.firstChild; e; e = e.nextSibling) z(e, a);
                if (b.firstChild || !/^(?:br|link|img)$/.test(c))
                    a.push("</", c, ">");
                break;
            case 2:
                a.push(b.name.toLowerCase(), '="', $(b.value), '"');
                break;
            case 3:
            case 4:
                a.push(E(b.nodeValue));
                break;
        }
    }
    var F = null;
    function ha(b) {
        if (null === F) {
            var a = document.createElement("PRE");
            a.appendChild(
                document.createTextNode(
                    '<!DOCTYPE foo PUBLIC "foo bar">\n<foo />'
                )
            );
            F = !/</.test(a.innerHTML);
        }
        if (F) {
            var c = b.innerHTML;
            if (S(b)) c = E(c);
            return c;
        }
        var d = [];
        for (var g = b.firstChild; g; g = g.nextSibling) z(g, d);
        return d.join("");
    }
    function ia(b) {
        var a = 0;
        return function(c) {
            var d = null,
                g = 0;
            for (var e = 0, h = c.length; e < h; ++e) {
                var f = c.charAt(e);
                switch (f) {
                    case "\t":
                        if (!d) d = [];
                        d.push(c.substring(g, e));
                        var i = b - (a % b);
                        a += i;
                        for (; i >= 0; i -= "                ".length)
                            d.push("                ".substring(0, i));
                        g = e + 1;
                        break;
                    case "\n":
                        a = 0;
                        break;
                    default:
                        ++a;
                }
            }
            if (!d) return c;
            d.push(c.substring(g));
            return d.join("");
        };
    }
    var ja = /(?:[^<]+|<!--[\s\S]*?--\>|<!\[CDATA\[([\s\S]*?)\]\]>|<\/?[a-zA-Z][^>]*>|<)/g,
        ka = /^<!--/,
        la = /^<\[CDATA\[/,
        ma = /^<br\b/i;
    function na(b) {
        var a = b.match(ja),
            c = [],
            d = 0,
            g = [];
        if (a)
            for (var e = 0, h = a.length; e < h; ++e) {
                var f = a[e];
                if (f.length > 1 && f.charAt(0) === "<") {
                    if (ka.test(f)) continue;
                    if (la.test(f)) {
                        c.push(f.substring(9, f.length - 3));
                        d += f.length - 12;
                    } else if (ma.test(f)) {
                        c.push("\n");
                        ++d;
                    } else g.push(d, f);
                } else {
                    var i = ga(f);
                    c.push(i);
                    d += i.length;
                }
            }
        return {
            source: c.join(""),
            tags: g
        };
    }
    function v(b, a) {
        var c = {};
        (function() {
            var g = b.concat(a);
            for (var e = g.length; --e >= 0; ) {
                var h = g[e],
                    f = h[3];
                if (f) for (var i = f.length; --i >= 0; ) c[f.charAt(i)] = h;
            }
        })();
        var d = a.length;
        return function(g, e) {
            e = e || 0;
            var h = [e, "pln"],
                f = "",
                i = 0,
                j = g;
            while (j.length) {
                var o,
                    m = null,
                    k,
                    l = c[j.charAt(0)];
                if (l) {
                    k = j.match(l[1]);
                    m = k[0];
                    o = l[0];
                } else {
                    for (var n = 0; n < d; ++n) {
                        l = a[n];
                        var p = l[2];
                        if (p && !p.test(f)) continue;
                        k = j.match(l[1]);
                        if (k) {
                            m = k[0];
                            o = l[0];
                            break;
                        }
                    }
                    if (!m) {
                        o = "pln";
                        m = j.substring(0, 1);
                    }
                }
                h.push(e + i, o);
                i += m.length;
                j = j.substring(m.length);
                if (o !== "com" && /\S/.test(m)) f = m;
            }
            return h;
        };
    }
    var oa = v(
        [],
        [
            ["pln", /^[^<]+/, null],
            ["dec", /^<!\w[^>]*(?:>|$)/, null],
            ["com", /^<!--[\s\S]*?(?:--\>|$)/, null],
            ["src", /^<\?[\s\S]*?(?:\?>|$)/, null],
            ["src", /^<%[\s\S]*?(?:%>|$)/, null],
            ["src", /^<(script|style|xmp)\b[^>]*>[\s\S]*?<\/\1\b[^>]*>/i, null],
            ["tag", /^<\/?\w[^<>]*>/, null]
        ]
    );
    function pa(b) {
        var a = oa(b);
        for (var c = 0; c < a.length; c += 2)
            if (a[c + 1] === "src") {
                var d, g;
                d = a[c];
                g = c + 2 < a.length ? a[c + 2] : b.length;
                var e = b.substring(d, g),
                    h = e.match(/^(<[^>]*>)([\s\S]*)(<\/[^>]*>)$/);
                if (h)
                    a.splice(
                        c,
                        2,
                        d,
                        "tag",
                        d + h[1].length,
                        "src",
                        d + h[1].length + (h[2] || "").length,
                        "tag"
                    );
            }
        return a;
    }
    var qa = v(
        [
            ["atv", /^\'[^\']*(?:\'|$)/, null, "'"],
            ["atv", /^\"[^\"]*(?:\"|$)/, null, '"'],
            ["pun", /^[<>\/=]+/, null, "<>/="]
        ],
        [
            ["tag", /^[\w:\-]+/, /^</],
            ["atv", /^[\w\-]+/, /^=/],
            ["atn", /^[\w:\-]+/, null],
            ["pln", /^\s+/, null, " \t\r\n"]
        ]
    );
    function ra(b, a) {
        for (var c = 0; c < a.length; c += 2) {
            var d = a[c + 1];
            if (d === "tag") {
                var g, e;
                g = a[c];
                e = c + 2 < a.length ? a[c + 2] : b.length;
                var h = b.substring(g, e),
                    f = qa(h, g);
                u(f, a, c, 2);
                c += f.length - 2;
            }
        }
        return a;
    }
    function r(b) {
        var a = [],
            c = [];
        if (b.tripleQuotedStrings)
            a.push([
                "str",
                /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
                null,
                "'\""
            ]);
        else if (b.multiLineStrings)
            a.push([
                "str",
                /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
                null,
                "'\"`"
            ]);
        else
            a.push([
                "str",
                /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
                null,
                "\"'"
            ]);
        c.push(["pln", /^(?:[^\'\"\`\/\#]+)/, null, " \r\n"]);
        if (b.hashComments) a.push(["com", /^#[^\r\n]*/, null, "#"]);
        if (b.cStyleComments) c.push(["com", /^\/\/[^\r\n]*/, null]);
        if (b.regexLiterals)
            c.push([
                "str",
                /^\/(?:[^\\\*\/\[]|\\[\s\S]|\[(?:[^\]\\]|\\.)*(?:\]|$))+(?:\/|$)/,
                Y
            ]);
        if (b.cStyleComments) c.push(["com", /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
        var d = x(b.keywords);
        b = null;
        var g = v(a, c),
            e = v(
                [],
                [
                    ["pln", /^\s+/, null, " \r\n"],
                    ["pln", /^[a-z_$@][a-z_$@0-9]*/i, null],
                    ["lit", /^0x[a-f0-9]+[a-z]/i, null],
                    [
                        "lit",
                        /^(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d+)(?:e[+\-]?\d+)?[a-z]*/i,
                        null,
                        "123456789"
                    ],
                    ["pun", /^[^\s\w\.$@]+/, null]
                ]
            );
        function h(f, i) {
            for (var j = 0; j < i.length; j += 2) {
                var o = i[j + 1];
                if (o === "pln") {
                    var m, k, l, n;
                    m = i[j];
                    k = j + 2 < i.length ? i[j + 2] : f.length;
                    l = f.substring(m, k);
                    n = e(l, m);
                    for (var p = 0, t = n.length; p < t; p += 2) {
                        var w = n[p + 1];
                        if (w === "pln") {
                            var A = n[p],
                                B = p + 2 < t ? n[p + 2] : l.length,
                                s = f.substring(A, B);
                            if (s === ".") n[p + 1] = "pun";
                            else if (s in d) n[p + 1] = "kwd";
                            else if (/^@?[A-Z][A-Z$]*[a-z][A-Za-z$]*$/.test(s))
                                n[p + 1] = s.charAt(0) === "@" ? "lit" : "typ";
                        }
                    }
                    u(n, i, j, 2);
                    j += n.length - 2;
                }
            }
            return i;
        }
        return function(f) {
            var i = g(f);
            i = h(f, i);
            return i;
        };
    }
    var G = r({
        keywords: W,
        hashComments: true,
        cStyleComments: true,
        multiLineStrings: true,
        regexLiterals: true
    });
    function sa(b, a) {
        for (var c = 0; c < a.length; c += 2) {
            var d = a[c + 1];
            if (d === "src") {
                var g, e;
                g = a[c];
                e = c + 2 < a.length ? a[c + 2] : b.length;
                var h = G(b.substring(g, e));
                for (var f = 0, i = h.length; f < i; f += 2) h[f] += g;
                u(h, a, c, 2);
                c += h.length - 2;
            }
        }
        return a;
    }
    function ta(b, a) {
        var c = false;
        for (var d = 0; d < a.length; d += 2) {
            var g = a[d + 1],
                e,
                h;
            if (g === "atn") {
                e = a[d];
                h = d + 2 < a.length ? a[d + 2] : b.length;
                c = /^on|^style$/i.test(b.substring(e, h));
            } else if (g === "atv") {
                if (c) {
                    e = a[d];
                    h = d + 2 < a.length ? a[d + 2] : b.length;
                    var f = b.substring(e, h),
                        i = f.length,
                        j =
                            i >= 2 &&
                            /^[\"\']/.test(f) &&
                            f.charAt(0) === f.charAt(i - 1),
                        o,
                        m,
                        k;
                    if (j) {
                        m = e + 1;
                        k = h - 1;
                        o = f;
                    } else {
                        m = e + 1;
                        k = h - 1;
                        o = f.substring(1, f.length - 1);
                    }
                    var l = G(o);
                    for (var n = 0, p = l.length; n < p; n += 2) l[n] += m;
                    if (j) {
                        l.push(k, "atv");
                        u(l, a, d + 2, 0);
                    } else u(l, a, d, 2);
                }
                c = false;
            }
        }
        return a;
    }
    function ua(b) {
        var a = pa(b);
        a = ra(b, a);
        a = sa(b, a);
        a = ta(b, a);
        return a;
    }
    function va(b, a, c) {
        var d = [],
            g = 0,
            e = null,
            h = null,
            f = 0,
            i = 0,
            j = ia(8);
        function o(k) {
            if (k > g) {
                if (e && e !== h) {
                    d.push("</span>");
                    e = null;
                }
                if (!e && h) {
                    e = h;
                    d.push('<span class="', e, '">');
                }
                var l = E(j(b.substring(g, k)))
                    .replace(/(\r\n?|\n| ) /g, "$1&nbsp;")
                    .replace(/\r\n?|\n/g, "<br />");
                d.push(l);
                g = k;
            }
        }
        while (true) {
            var m;
            m = f < a.length ? (i < c.length ? a[f] <= c[i] : true) : false;
            if (m) {
                o(a[f]);
                if (e) {
                    d.push("</span>");
                    e = null;
                }
                d.push(a[f + 1]);
                f += 2;
            } else if (i < c.length) {
                o(c[i]);
                h = c[i + 1];
                i += 2;
            } else break;
        }
        o(b.length);
        if (e) d.push("</span>");
        return d.join("");
    }
    var C = {};
    function q(b, a) {
        for (var c = a.length; --c >= 0; ) {
            var d = a[c];
            if (!C.hasOwnProperty(d)) C[d] = b;
            else if ("console" in window)
                console.log("cannot override language handler %s", d);
        }
    }
    q(G, ["default-code"]);
    q(ua, ["default-markup", "html", "htm", "xhtml", "xml", "xsl"]);
    q(
        r({
            keywords: I,
            hashComments: true,
            cStyleComments: true
        }),
        ["c", "cc", "cpp", "cs", "cxx", "cyc"]
    );
    q(
        r({
            keywords: J,
            cStyleComments: true
        }),
        ["java"]
    );
    q(
        r({
            keywords: O,
            hashComments: true,
            multiLineStrings: true
        }),
        ["bsh", "csh", "sh"]
    );
    q(
        r({
            keywords: M,
            hashComments: true,
            multiLineStrings: true,
            tripleQuotedStrings: true
        }),
        ["cv", "py"]
    );
    q(
        r({
            keywords: L,
            hashComments: true,
            multiLineStrings: true,
            regexLiterals: true
        }),
        ["perl", "pl", "pm"]
    );
    q(
        r({
            keywords: N,
            hashComments: true,
            multiLineStrings: true,
            regexLiterals: true
        }),
        ["rb"]
    );
    q(
        r({
            keywords: K,
            cStyleComments: true,
            regexLiterals: true
        }),
        ["js"]
    );
    function T(b, a) {
        try {
            var c = na(b),
                d = c.source,
                g = c.tags;
            if (!C.hasOwnProperty(a))
                a = /^\s*</.test(d) ? "default-markup" : "default-code";
            var e = C[a].call({}, d);
            return va(d, g, e);
        } catch (h) {
            if ("console" in window) {
                console.log(h);
                console.trace();
            }
            return b;
        }
    }
    function wa(b) {
        var a = H(),
            c = [
                document.getElementsByTagName("pre"),
                document.getElementsByTagName("code"),
                document.getElementsByTagName("li"),
                document.getElementsByTagName("xmp")
            ],
            d = [];
        for (var g = 0; g < c.length; ++g)
            for (var e = 0; e < c[g].length; ++e) d.push(c[g][e]);
        c = null;
        var h = 0;
        function f() {
            var i = new Date().getTime() + 250;
            for (; h < d.length && new Date().getTime() < i; h++) {
                var j = d[h];
                if (j.className && j.className.indexOf("prettyprint") >= 0) {
                    var o = j.className.match(/\blang-(\w+)\b/);
                    if (o) o = o[1];
                    var m = false;
                    for (var k = j.parentNode; k; k = k.parentNode)
                        if (
                            (k.tagName === "pre" ||
                                k.tagName === "code" ||
                                k.tagName === "xmp") &&
                            k.className &&
                            k.className.indexOf("prettyprint") >= 0
                        ) {
                            m = true;
                            break;
                        }
                    if (!m) {
                        var l = ha(j);
                        l = l.replace(/(?:\r\n?|\n)$/, "");
                        var n = T(l, o);
                        if (!S(j)) j.innerHTML = n;
                        else {
                            var p = document.createElement("PRE");
                            for (var t = 0; t < j.attributes.length; ++t) {
                                var w = j.attributes[t];
                                if (w.specified)
                                    p.setAttribute(w.name, w.value);
                            }
                            p.innerHTML = n;
                            j.parentNode.replaceChild(p, j);
                            p = j;
                        }
                        if (a && j.tagName === "PRE") {
                            var A = j.getElementsByTagName("br");
                            for (var B = A.length; --B >= 0; ) {
                                var s = A[B];
                                s.parentNode.replaceChild(
                                    document.createTextNode("\r\n"),
                                    s
                                );
                            }
                        }
                    }
                }
            }
            if (h < d.length) setTimeout(f, 250);
            else if (b) b();
        }
        f();
    }
    window.PR_normalizedHtml = z;
    window.prettyPrintOne = T;
    window.prettyPrint = wa;
    window.PR = {
        createSimpleLexer: v,
        registerLangHandler: q,
        sourceDecorator: r,
        PR_ATTRIB_NAME: "atn",
        PR_ATTRIB_VALUE: "atv",
        PR_COMMENT: "com",
        PR_DECLARATION: "dec",
        PR_KEYWORD: "kwd",
        PR_LITERAL: "lit",
        PR_PLAIN: "pln",
        PR_PUNCTUATION: "pun",
        PR_SOURCE: "src",
        PR_STRING: "str",
        PR_TAG: "tag",
        PR_TYPE: "typ"
    };
})();


/***/ }),

/***/ "./runestone/parsons/js/timedparsons.js":
/*!**********************************************!*\
  !*** ./runestone/parsons/js/timedparsons.js ***!
  \**********************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return TimedParsons; });
/* harmony import */ var _parsons__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./parsons */ "./runestone/parsons/js/parsons.js");


class TimedParsons extends _parsons__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(opts) {
        super(opts);
        // todo -- make this configurable
        if (opts.timedfeedback) {
            this.showfeedback = true;
        } else {
            this.showfeedback = false;
        }
        this.grader.showfeedback = this.showfeedback;
        this.hideFeedback();
        $(this.checkButton).hide();
        $(this.helpButton).hide();
        $(this.resetButton).hide();
    }
    checkCorrectTimed() {
        return this.correct ? "T" : "F";
    }
    hideFeedback() {
        $(this.messageDiv).hide();
    }
}

if (typeof window.component_factory === "undefined") {
    window.component_factory = {};
}
window.component_factory["parsons"] = function (opts) {
    if (opts.timed) {
        return new TimedParsons(opts);
    }
    return new _parsons__WEBPACK_IMPORTED_MODULE_0__["default"](opts);
};


/***/ }),

/***/ "./runestone/poll/css/poll.css":
/*!*************************************!*\
  !*** ./runestone/poll/css/poll.css ***!
  \*************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var api = __webpack_require__(/*! ../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
            var content = __webpack_require__(/*! !../../../node_modules/css-loader/dist/cjs.js!./poll.css */ "./node_modules/css-loader/dist/cjs.js!./runestone/poll/css/poll.css");

            content = content.__esModule ? content.default : content;

            if (typeof content === 'string') {
              content = [[module.i, content, '']];
            }

var options = {};

options.insert = "head";
options.singleton = false;

var update = api(content, options);



module.exports = content.locals || {};

/***/ }),

/***/ "./runestone/poll/js/poll.js":
/*!***********************************!*\
  !*** ./runestone/poll/js/poll.js ***!
  \***********************************/
/*! exports provided: pollList, default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "pollList", function() { return pollList; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Poll; });
/* harmony import */ var _common_js_runestonebase__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/js/runestonebase */ "./runestone/common/js/runestonebase.js");
/* harmony import */ var _css_poll_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../css/poll.css */ "./runestone/poll/css/poll.css");
/* harmony import */ var _css_poll_css__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_poll_css__WEBPACK_IMPORTED_MODULE_1__);
/*
__author__ = Kirby Olson
__date__ = 6/12/2015  */





var pollList = {};

class Poll extends _common_js_runestonebase__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(opts) {
        super(opts);
        var orig = opts.orig; //entire <p> element
        this.origElem = orig;
        this.divid = orig.id;
        this.children = this.origElem.childNodes;
        this.optionList = [];
        this.optsArray = [];
        this.comment = false;
        if ($(this.origElem).is("[data-comment]")) {
            this.comment = true;
        }
        this.resultsViewer = $(orig).data("results");
        this.getQuestionText();
        this.getOptionText(); //populates optionList
        this.renderPoll(); //generates HTML
        this.checkPollStorage(); //checks localStorage to see if this poll has already been completed by this user
        this.caption = "Poll";
        this.addCaption("runestone");
    }
    getQuestionText() {
        //finds the text inside the parent tag, but before the first <li> tag and sets it as the question
        var _this = this;
        var firstAnswer;
        for (var i = 0; i < this.children.length; i++) {
            if (this.children[i].tagName == "LI") {
                firstAnswer = _this.children[i];
                break;
            }
        }
        var delimiter = firstAnswer.outerHTML;
        var fulltext = $(this.origElem).html();
        var temp = fulltext.split(delimiter);
        this.question = temp[0];
    }
    getOptionText() {
        //Gets the text from each <li> tag and places it in this.optionList
        var _this = this;
        for (var i = 0; i < this.children.length; i++) {
            if (_this.children[i].tagName == "LI") {
                _this.optionList.push($(_this.children[i]).text());
            }
        }
    }
    renderPoll() {
        //generates the HTML that the user interacts with
        var _this = this;
        this.containerDiv = document.createElement("div");
        this.pollForm = document.createElement("form");
        this.resultsDiv = document.createElement("div");
        this.containerDiv.id = this.divid + "_container";
        $(this.containerDiv).addClass(this.origElem.getAttribute("class"));
        $(this.pollForm).html(
            `<span style='font-size: Large'>${this.question}</span>`
        );
        $(this.pollForm).attr({
            id: this.divid + "_form",
            method: "get",
            action: "",
            onsubmit: "return false;",
        });
        this.pollForm.appendChild(document.createElement("br"));
        for (var i = 0; i < this.optionList.length; i++) {
            var radio = document.createElement("input");
            var tmpid = _this.divid + "_opt_" + i;
            $(radio).attr({
                id: tmpid,
                name: this.divid + "_group1",
                type: "radio",
                value: i,
            });
            $(radio).click(this.submitPoll.bind(this));
            var label = document.createElement("label");
            $(label).attr("for", tmpid);
            $(label).text(this.optionList[i]);
            this.pollForm.appendChild(radio);
            this.optsArray.push(radio);
            this.pollForm.appendChild(label);
            this.pollForm.appendChild(document.createElement("br"));
        }
        if (this.comment) {
            this.renderTextField();
        }
        this.resultsDiv.id = this.divid + "_results";
        this.containerDiv.appendChild(this.pollForm);
        this.containerDiv.appendChild(this.resultsDiv);
        $(this.origElem).replaceWith(this.containerDiv);
    }
    renderTextField() {
        this.textfield = document.createElement("input");
        this.textfield.type = "text";
        $(this.textfield).addClass("form-control");
        this.textfield.style.width = "300px";
        this.textfield.name = this.divid + "_comment";
        this.textfield.placeholder = "Any comments?";
        this.pollForm.appendChild(this.textfield);
        this.pollForm.appendChild(document.createElement("br"));
    }
    submitPoll() {
        //checks the poll, sets localstorage and submits to the server
        var poll_val = null;
        for (var i = 0; i < this.optsArray.length; i++) {
            if (this.optsArray[i].checked) {
                poll_val = this.optsArray[i].value;
                break;
            }
        }
        if (poll_val === null) return;
        var comment_val = "";
        if (this.comment) {
            comment_val = this.textfield.value;
        }
        var act = "";
        if (comment_val !== "") {
            act = poll_val + ":" + comment_val;
        } else {
            act = poll_val;
        }
        var eventInfo = { event: "poll", act: act, div_id: this.divid };
        // log the response to the database
        this.logBookEvent(eventInfo); // in bookfuncs.js
        // log the fact that the user has answered the poll to local storage
        localStorage.setItem(this.divid, "true");
        if (!document.getElementById(`${this.divid}_sent`)) {
            $(this.pollForm).append(
                `<span id=${this.divid}_sent><strong>Thanks, your response has been recorded</strong></span>`
            );
        } else {
            $(`#${this.divid}_sent`).html(
                "<strong>Only Your last reponse is recorded</strong>"
            );
        }
        // show the results of the poll
        if (this.resultsViewer === "all") {
            var data = {};
            data.div_id = this.divid;
            data.course = eBookConfig.course;
            jQuery.get(
                eBookConfig.ajaxURL + "getpollresults",
                data,
                this.showPollResults
            );
        }
    }
    showPollResults(data) {
        //displays the results returned by the server
        var results = eval(data);
        var total = results[0];
        var opt_list = results[1];
        var count_list = results[2];
        var div_id = results[3];
        var my_vote = results[4];
        // resture current users vote
        if (my_vote > -1) {
            this.optsArray[my_vote].checked = "checked";
        }
        // show results summary if appropriate
        if (
            (this.resultsViewer === "all" &&
                localStorage.getItem(this.divid === "true")) ||
            eBookConfig.isInstructor
        ) {
            $(this.resultsDiv).html(
                `<b>Results:</b> ${total} responses <br><br>`
            );
            var list = $(document.createElement("div"));
            $(list).addClass("results-container");
            for (var i = 0; i < this.optionList.length; i++) {
                var count;
                var percent;
                if (count_list[i]) {
                    count = count_list[i];
                    percent = (count / total) * 100;
                } else {
                    count = 0;
                    percent = 0;
                }
                var text = count + " (" + Math.round(10 * percent) / 10 + "%)"; // round percent to 10ths
                var html;
                if (percent > 10) {
                    html =
                        `<div class="progresscounter">${i + 1}. </div>` +
                        "<div class='progress'>" +
                        "<div class='progress-bar progress-bar-success'" +
                        `style="width: ${percent}%; min-width: 2em;">` +
                        "<span class='poll-text'>" +
                        text +
                        "</span></div></div>";
                } else {
                    html =
                        `<div class="progresscounter">${i + 1}. </div>` +
                        "<div class='progress'>" +
                        "<div class='progress-bar progress-bar-success'" +
                        `style="width: ${percent}%; min-width: 2em;"></div>` +
                        "<span class='poll-text' style='margin: 0 0 0 10px;'>" +
                        text +
                        "</span></div>";
                }
                var el = $(html);
                list.append(el);
            }
            $(this.resultsDiv).append(list);
        }
    }
    disableOptions() {}
    checkPollStorage() {
        //checks the localstorage to see if the poll has been completed already
        var _this = this;
        var len = localStorage.length;
        if (len > 0) {
            //If the poll has already been completed, show the results
            var data = {};
            data.div_id = this.divid;
            data.course = eBookConfig.course;
            jQuery.get(
                eBookConfig.ajaxURL + "getpollresults",
                data,
                this.showPollResults.bind(this)
            );
        }
    }
}

// Do not render poll data until login-complete event so we know instructor status
$(document).bind("runestone:login-complete", function () {
    $("[data-component=poll]").each(function (index) {
        try {
            pollList[this.id] = new Poll({ orig: this });
        } catch (err) {
            console.log(`Error rendering Poll Problem ${this.id}
                         Details: ${err}`);
            console.log(err.stack);
        }
    });
});

if (typeof window.component_factory === "undefined") {
    window.component_factory = {};
}
window.component_factory.poll = function (opts) {
    return new Poll(opts);
};


/***/ }),

/***/ "./runestone/reveal/css/reveal.css":
/*!*****************************************!*\
  !*** ./runestone/reveal/css/reveal.css ***!
  \*****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var api = __webpack_require__(/*! ../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
            var content = __webpack_require__(/*! !../../../node_modules/css-loader/dist/cjs.js!./reveal.css */ "./node_modules/css-loader/dist/cjs.js!./runestone/reveal/css/reveal.css");

            content = content.__esModule ? content.default : content;

            if (typeof content === 'string') {
              content = [[module.i, content, '']];
            }

var options = {};

options.insert = "head";
options.singleton = false;

var update = api(content, options);



module.exports = content.locals || {};

/***/ }),

/***/ "./runestone/reveal/js/reveal.js":
/*!***************************************!*\
  !*** ./runestone/reveal/js/reveal.js ***!
  \***************************************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _common_js_runestonebase__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/js/runestonebase */ "./runestone/common/js/runestonebase.js");
/* harmony import */ var _css_reveal_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../css/reveal.css */ "./runestone/reveal/css/reveal.css");
/* harmony import */ var _css_reveal_css__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_reveal_css__WEBPACK_IMPORTED_MODULE_1__);
/*==========================================
=======      Master reveal.js       ========
============================================
===     This file contains the JS for    ===
===     the Runestone reval component.   ===
============================================
===              Created by              ===
===           Isaiah Mayerchak           ===
===               06/12/15               ===
==========================================*/


var RevealList = {}; // Dictionary that contains all instances of Reveal objects




// Define Reveal object
class Reveal extends _common_js_runestonebase__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(opts) {
        super(opts);
        var orig = opts.orig; // entire <div> element that will be replaced by new HTML
        this.origElem = orig;
        this.divid = orig.id;
        this.dataModal = false; // is a model dialog vs. inline
        if ($(this.origElem).is("[data-modal]")) {
            this.dataModal = true;
        }
        if ($(this.origElem).is("[data-instructoronly]")) {
            this.instructorOnly = true;
        } else {
            this.instructorOnly = false;
        }
        this.modalTitle = null;
        this.showtitle = null; // Title of button that shows the concealed data
        this.hidetitle = null;
        this.origContent = $(this.origElem).html();
        this.children = [];
        this.adoptChildren();
        this.checkForTitle();
        this.getButtonTitles();
        // Delay creating instructoronly buttons until login
        if (!this.instructorOnly) {
            this.createShowButton();
            if (!this.dataModal) {
                this.createHideButton(); // Hide button is already implemented in modal dialog
            }
        }
    }
    /*====================================
    == Get text for buttons/modal title ==
    ====================================*/
    adoptChildren() {
        for (var i = 0; i < this.origElem.childNodes.length; i++) {
            this.children.push(this.origElem.childNodes[i]);
        }
    }
    getButtonTitles() {
        this.showtitle = $(this.origElem).data("showtitle");
        if (this.showtitle === undefined) {
            this.showtitle = "Show"; // default
        }
        if (this.instructorOnly) {
            this.showtitle += " IG";
        }
        this.hidetitle = $(this.origElem).data("hidetitle");
        if (this.hidetitle === undefined) {
            this.hidetitle = "Hide"; // default
        }
    }
    checkForTitle() {
        this.modalTitle = $(this.origElem).data("title");
        if (this.modalTitle === undefined) {
            this.modalTitle = "Message from the author"; // default
        }
    }
    /*============================
    == Create show/hide buttons ==
    ============================*/
    createShowButton() {
        var _this = this;
        this.wrapDiv = document.createElement("div"); // wrapper div
        if (!this.dataModal) {
            this.revealDiv = document.createElement("div"); // Div that is hidden that contains content
            this.revealDiv.id = this.divid;
            // Get original content, put it inside revealDiv and replace original div with revealDiv
            //$(this.revealDiv).html(this.origContent);
            for (var i = 0; i < this.children.length; i++) {
                this.revealDiv.appendChild(this.children[i]);
            }
            $(this.revealDiv).hide();
            this.wrapDiv.appendChild(this.revealDiv);
        }
        if (this.instructorOnly) {
            $(this.revealDiv).addClass("iguide");
        }
        this.sbutt = document.createElement("button");
        $(this.sbutt).addClass("btn reveal_button");
        if (this.instructorOnly) {
            $(this.sbutt).addClass("btn-info");
        } else {
            $(this.sbutt).addClass("btn-default");
        }
        $(this.sbutt).css("margin-bottom", "10px");
        this.sbutt.textContent = this.showtitle;
        this.sbutt.id = this.divid + "_show";
        if (!this.dataModal) {
            this.sbutt.onclick = function () {
                _this.showInline();
                $(this).hide();
            };
        } else {
            this.createModal();
            $(this.sbutt).attr({
                "data-toggle": "modal",
                "data-target": "#" + this.divid + "_modal",
            });
        }
        this.wrapDiv.appendChild(this.sbutt);
        $(this.origElem).replaceWith(this.wrapDiv);
    }
    createHideButton() {
        var _this = this;
        this.hbutt = document.createElement("button");
        $(this.hbutt).hide();
        this.hbutt.textContent = this.hidetitle;
        this.hbutt.className = "btn btn-default reveal_button";
        $(this.hbutt).css("margin-bottom", "10px");
        this.hbutt.id = this.divid + "_hide";
        this.hbutt.onclick = function () {
            _this.hideInline();
            $(this).hide();
        };
        this.wrapDiv.appendChild(this.hbutt);
    }
    createInstructorButtons() {
        this.createShowButton();
        if (!this.dataModal) {
            this.createHideButton();
        }
    }
    /*=================
    === Modal logic ===
    =================*/
    createModal() {
        this.modalContainerDiv = document.createElement("div");
        $(this.modalContainerDiv).addClass("modal fade");
        this.modalContainerDiv.id = this.divid + "_modal";
        $(this.modalContainerDiv).attr("role", "dialog");
        document.body.appendChild(this.modalContainerDiv);
        this.modalDialogDiv = document.createElement("div");
        $(this.modalDialogDiv).addClass("modal-dialog");
        this.modalContainerDiv.appendChild(this.modalDialogDiv);
        this.modalContentDiv = document.createElement("div");
        $(this.modalContentDiv).addClass("modal-content");
        this.modalDialogDiv.appendChild(this.modalContentDiv);
        this.modalHeaderDiv = document.createElement("div");
        $(this.modalHeaderDiv).addClass("modal-header");
        this.modalContentDiv.appendChild(this.modalHeaderDiv);
        this.modalButton = document.createElement("button");
        this.modalButton.type = "button";
        $(this.modalButton).addClass("close");
        $(this.modalButton).attr({
            "aria-hidden": "true",
            "data-dismiss": "modal",
        });
        this.modalButton.innerHTML = "&times";
        this.modalHeaderDiv.appendChild(this.modalButton);
        this.modalTitleE = document.createElement("h4");
        $(this.modalTitleE).addClass("modal-title");
        this.modalTitleE.innerHTML = this.modalTitle;
        this.modalHeaderDiv.appendChild(this.modalTitleE);
        this.modalBody = document.createElement("div");
        $(this.modalBody).addClass("modal-body");
        for (var i = 0; i < this.children.length; i++) {
            this.modalBody.appendChild(this.children[i]);
        }
        this.modalContentDiv.appendChild(this.modalBody);
        /*var html = "<div class='modal fade'>" +
                    "    <div class='modal-dialog compare-modal'>" +
                    "        <div class='modal-content'>" +
                    "            <div class='modal-header'>" +
                    "                <button type='button' class='close' data-dismiss='modal' aria-hidden='true'>&times;</button>" +
                    "                <h4 class='modal-title'>" + this.modalTitle + "</h4>" +
                    "            </div>" +
                    "            <div class='modal-body'>" +
                    this.origContent +
                    "            </div>" +
                    "        </div>" +
                    "    </div>" +
                    "</div>";*/
        //var el = $(this.modalContainerDiv);
        //el.modal();
    }
    /*==================
    === Inline logic ===
    ==================*/
    showInline() {
        $(this.revealDiv).show();
        $(this.hbutt).show();
        $(this.revealDiv)
            .find(".CodeMirror")
            .each(function (i, el) {
                el.CodeMirror.refresh();
            });
    }
    hideInline() {
        $(this.revealDiv).hide();
        $(this.sbutt).show();
    }
}

/*=================================
== Find the custom HTML tags and ==
==     execute our code on them        ==
=================================*/

$(document).bind("runestone:login-complete", function () {
    $("[data-component=reveal]").each(function (index) {
        try {
            RevealList[this.id] = new Reveal({ orig: this });
        } catch (err) {
            console.log(`Error rendering Reveal ${this.id}`);
        }
    });

    if (eBookConfig.isInstructor) {
        for (const divid of Object.keys(RevealList)) {
            if (RevealList[divid].instructorOnly) {
                RevealList[divid].createInstructorButtons();
            }
        }
    }
});


/***/ }),

/***/ "./runestone/selectquestion/js/selectone.js":
/*!**************************************************!*\
  !*** ./runestone/selectquestion/js/selectone.js ***!
  \**************************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return SelectOne; });
/* harmony import */ var _common_js_renderComponent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/js/renderComponent */ "./runestone/common/js/renderComponent.js");
/* harmony import */ var _common_js_runestonebase__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../common/js/runestonebase */ "./runestone/common/js/runestonebase.js");
/**
 * *******************************
 * |docname| - SelectOne Component
 * *******************************
 */



class SelectOne extends _common_js_runestonebase__WEBPACK_IMPORTED_MODULE_1__["default"] {
    /**
     * constructor --
     * Making an instance of a selectone is a bit more complicated because it is
     * a kind of meta directive.  That is, go to the server and randomly select
     * a question to display in this spot.  Or, if a student has already seen this question
     * in the context of an exam retrieve the question they saw in the first place.
     * Making an API call and waiting for the response is handled asynchronously.
     * But lots of code is not written with that assumption.  So we do the initialization in
     * two parts.
     * 1. Create the object with the usual constructor
     * 2. call initialize, which returns a promise.  When that promise is resolved
     * the "replacement" component will replace the original selectone component in the DOM.
     *
     * @param  {} opts
     */
    constructor(opts) {
        super(opts);
        this.origOpts = opts;
        this.questions = $(opts.orig).data("questionlist");
        this.proficiency = $(opts.orig).data("proficiency");
        this.minDifficulty = $(opts.orig).data("minDifficulty");
        this.maxDifficulty = $(opts.orig).data("maxDifficulty");
        this.points = $(opts.orig).data("points");
        this.autogradable = $(opts.orig).data("autogradable");
        this.not_seen_ever = $(opts.orig).data("not_seen_ever");
        this.selector_id = $(opts.orig).first().attr("id");
        this.primaryOnly = $(opts.orig).data("primary");
        this.ABExperiment = $(opts.orig).data("ab");
        opts.orig.id = this.selector_id;
    }
    /**
     * initialize --
     * initialize is used so that the constructor does not have to be async.
     * Constructors should definitely not return promises that would seriously
     * mess things up.
     * @return {Promise} Will resolve after component from DB is reified
     */
    async initialize() {
        let self = this;
        let data = { selector_id: this.selector_id };
        if (this.questions) {
            data.questions = this.questions;
        } else if (this.proficiency) {
            data.proficiency = this.proficiency;
        }
        if (this.minDifficulty) {
            data.minDifficulty = this.minDifficulty;
        }
        if (this.maxDifficulty) {
            data.maxDifficulty = this.maxDifficulty;
        }
        if (this.points) {
            data.points = this.points;
        }
        if (this.autogradable) {
            data.autogradable = this.autogradable;
        }
        if (this.not_seen_ever) {
            data.not_seen_ever = this.not_seen_ever;
        }
        if (this.primaryOnly) {
            data.primary = this.primaryOnly;
        }
        if (this.ABExperiment) {
            data.AB = this.ABExperiment;
        }
        let opts = this.origOpts;
        let selectorId = this.selector_id;
        console.log("getting question source");
        let request = new Request("/runestone/ajax/get_question_source", {
            method: "POST",
            headers: this.jsonHeaders,
            body: JSON.stringify(data),
        });
        let response = await fetch(request);
        let htmlsrc = await response.json();
        if (htmlsrc.indexOf("No preview") >= 0) {
            alert(
                `Error: Not able to find a question for ${selectorId} based on the criteria`
            );
            throw new Error(`Unable to find a question for ${selectorId}`);
        }
        let res;
        if (opts.timed) {
            // timed components are not rendered immediately, only when the student
            // starts the assessment and visits this particular entry.
            res = Object(_common_js_renderComponent__WEBPACK_IMPORTED_MODULE_0__["createTimedComponent"])(htmlsrc, {
                timed: true,
                selector_id: selectorId,
                assessmentTaken: opts.assessmentTaken,
            });
            // replace the entry in the timed assessment's list of components
            // with the component created by createTimedComponent
            for (let component of opts.rqa) {
                if (component.question == self) {
                    component.question = res.question;
                    break;
                }
            }
            self.realComponent = res.question;
            self.containerDiv = res.question.containerDiv;
            self.realComponent.selectorId = selectorId;
        } else {
            // just render this component on the page in its usual place
            res = Object(_common_js_renderComponent__WEBPACK_IMPORTED_MODULE_0__["renderRunestoneComponent"])(htmlsrc, selectorId, {
                selector_id: selectorId,
                useRunestoneServices: true,
            });
        }
        return response;
    }
}

/*
 * When the page is loaded and the login checks are complete find and render
 * each selectquestion component that is not part of a timedAssessment.
 **/
$(document).bind("runestone:login-complete", async function () {
    let selQuestions = document.querySelectorAll(
        "[data-component=selectquestion]"
    );
    for (let cq of selQuestions) {
        try {
            if ($(cq).closest("[data-component=timedAssessment]").length == 0) {
                // If this element exists within a timed component, don't render it here
                let tmp = new SelectOne({ orig: cq });
                await tmp.initialize();
            }
        } catch (err) {
            console.log(`Error rendering New Exercise ${cq.id}
                         Details: ${err}`);
            console.log(err.stack);
        }
    }
});


/***/ }),

/***/ "./runestone/shortanswer/css/shortanswer.css":
/*!***************************************************!*\
  !*** ./runestone/shortanswer/css/shortanswer.css ***!
  \***************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var api = __webpack_require__(/*! ../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
            var content = __webpack_require__(/*! !../../../node_modules/css-loader/dist/cjs.js!./shortanswer.css */ "./node_modules/css-loader/dist/cjs.js!./runestone/shortanswer/css/shortanswer.css");

            content = content.__esModule ? content.default : content;

            if (typeof content === 'string') {
              content = [[module.i, content, '']];
            }

var options = {};

options.insert = "head";
options.singleton = false;

var update = api(content, options);



module.exports = content.locals || {};

/***/ }),

/***/ "./runestone/shortanswer/js/shortanswer.js":
/*!*************************************************!*\
  !*** ./runestone/shortanswer/js/shortanswer.js ***!
  \*************************************************/
/*! exports provided: saList, default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "saList", function() { return saList; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return ShortAnswer; });
/* harmony import */ var _common_js_runestonebase_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/js/runestonebase.js */ "./runestone/common/js/runestonebase.js");
/* harmony import */ var _css_shortanswer_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./../css/shortanswer.css */ "./runestone/shortanswer/css/shortanswer.css");
/* harmony import */ var _css_shortanswer_css__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_shortanswer_css__WEBPACK_IMPORTED_MODULE_1__);
/*==========================================
=======    Master shortanswer.js    ========
============================================
===     This file contains the JS for    ===
=== the Runestone shortanswer component. ===
============================================
===              Created by              ===
===           Isaiah Mayerchak           ===
===                7/2/15                ===
===              Brad Miller             ===
===                2019                  ===
==========================================*/




var saList;
if (saList === undefined) saList = {}; // Dictionary that contains all instances of shortanswer objects

class ShortAnswer extends _common_js_runestonebase_js__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(opts) {
        super(opts);
        if (opts) {
            var orig = opts.orig; // entire <p> element that will be replaced by new HTML
            this.useRunestoneServices =
                opts.useRunestoneServices || eBookConfig.useRunestoneServices;
            this.origElem = orig;
            this.divid = orig.id;
            this.question = this.origElem.innerHTML;
            this.optional = false;
            if ($(this.origElem).is("[data-optional]")) {
                this.optional = true;
            }
            if ($(this.origElem).is("[data-mathjax]")) {
                this.mathjax = true;
            }
            this.renderHTML();
            this.checkServer("shortanswer");
            this.caption = "shortanswer";
            this.addCaption("runestone");
        }
    }

    renderHTML() {
        this.containerDiv = document.createElement("div");
        this.containerDiv.id = this.divid;
        $(this.containerDiv).addClass(this.origElem.getAttribute("class"));
        this.newForm = document.createElement("form");
        this.newForm.id = this.divid + "_journal";
        this.newForm.name = this.newForm.id;
        this.newForm.action = "";
        this.containerDiv.appendChild(this.newForm);
        this.fieldSet = document.createElement("fieldset");
        this.newForm.appendChild(this.fieldSet);
        this.legend = document.createElement("legend");
        this.legend.innerHTML = "Short Answer";
        this.fieldSet.appendChild(this.legend);
        this.firstLegendDiv = document.createElement("div");
        this.firstLegendDiv.innerHTML = this.question;
        $(this.firstLegendDiv).addClass("journal-question");
        this.fieldSet.appendChild(this.firstLegendDiv);
        this.jInputDiv = document.createElement("div");
        this.jInputDiv.id = this.divid + "_journal_input";
        this.fieldSet.appendChild(this.jInputDiv);
        this.jOptionsDiv = document.createElement("div");
        $(this.jOptionsDiv).addClass("journal-options");
        this.jInputDiv.appendChild(this.jOptionsDiv);
        this.jLabel = document.createElement("label");
        $(this.jLabel).addClass("radio-inline");
        this.jOptionsDiv.appendChild(this.jLabel);
        this.jTextArea = document.createElement("textarea");
        let self = this;
        this.jTextArea.onchange = function () {
            self.isAnswered = true;
        };
        this.jTextArea.id = this.divid + "_solution";
        $(this.jTextArea).attr("aria-label", "textarea");
        $(this.jTextArea).css("display:inline, width:530px");
        $(this.jTextArea).addClass("form-control");
        this.jTextArea.rows = 4;
        this.jTextArea.cols = 50;
        this.jLabel.appendChild(this.jTextArea);
        this.jTextArea.onchange = function () {
            this.feedbackDiv.innerHTML = "Your answer has not been saved yet!";
            $(this.feedbackDiv).removeClass("alert-success");
            $(this.feedbackDiv).addClass("alert alert-danger");
        }.bind(this);
        this.fieldSet.appendChild(document.createElement("br"));
        if (this.mathjax) {
            this.renderedAnswer = document.createElement("div");
            $(this.renderedAnswer).addClass("latexoutput");
            this.fieldSet.appendChild(this.renderedAnswer);
        }
        this.buttonDiv = document.createElement("div");
        this.fieldSet.appendChild(this.buttonDiv);
        this.submitButton = document.createElement("button");
        $(this.submitButton).addClass("btn btn-success");
        this.submitButton.type = "button";
        this.submitButton.textContent = "Save";
        this.submitButton.onclick = function () {
            this.checkCurrentAnswer();
            this.logCurrentAnswer();
            this.renderFeedback();
        }.bind(this);
        this.buttonDiv.appendChild(this.submitButton);
        this.randomSpan = document.createElement("span");
        this.randomSpan.innerHTML = "Instructor's Feedback";
        this.fieldSet.appendChild(this.randomSpan);
        this.otherOptionsDiv = document.createElement("div");
        $(this.otherOptionsDiv).css("padding-left:20px");
        $(this.otherOptionsDiv).addClass("journal-options");
        this.fieldSet.appendChild(this.otherOptionsDiv);
        // add a feedback div to give user feedback
        this.feedbackDiv = document.createElement("div");
        //$(this.feedbackDiv).addClass("bg-info form-control");
        //$(this.feedbackDiv).css("width:530px, background-color:#eee, font-style:italic");
        $(this.feedbackDiv).css("width:530px, font-style:italic");
        this.feedbackDiv.id = this.divid + "_feedback";
        this.feedbackDiv.innerHTML = "You have not answered this question yet.";
        $(this.feedbackDiv).addClass("alert alert-danger");
        //this.otherOptionsDiv.appendChild(this.feedbackDiv);
        this.fieldSet.appendChild(this.feedbackDiv);
        //this.fieldSet.appendChild(document.createElement("br"));
        $(this.origElem).replaceWith(this.containerDiv);
        // This is a stopgap measure for when MathJax is not loaded at all.  There is another
        // more difficult case that when MathJax is loaded asynchronously we will get here
        // before MathJax is loaded.  In that case we will need to implement something
        // like `the solution described here <https://stackoverflow.com/questions/3014018/how-to-detect-when-mathjax-is-fully-loaded>`_
        if (typeof MathJax !== "undefined") {
            MathJax.Hub.Queue(["Typeset", MathJax.Hub, this.containerDiv]);
        }
    }

    renderMath(value) {
        if (this.mathjax) {
            value = value.replace(/\$\$(.*?)\$\$/g, "\\[ $1 \\]");
            value = value.replace(/\$(.*?)\$/g, "\\( $1 \\)");
            $(this.renderedAnswer).text(value);
            MathJax.Hub.Queue(["Typeset", MathJax.Hub, this.renderedAnswer]);
        }
    }

    checkCurrentAnswer() {}

    logCurrentAnswer() {
        let value = $(document.getElementById(this.divid + "_solution")).val();
        this.renderMath(value);
        this.setLocalStorage({
            answer: value,
            timestamp: new Date(),
        });
        this.logBookEvent({
            event: "shortanswer",
            act: value,
            div_id: this.divid,
        });
    }

    renderFeedback() {
        this.feedbackDiv.innerHTML = "Your answer has been saved.";
        $(this.feedbackDiv).removeClass("alert-danger");
        $(this.feedbackDiv).addClass("alert alert-success");
    }
    setLocalStorage(data) {
        if (!this.graderactive) {
            let key = this.localStorageKey();
            localStorage.setItem(key, JSON.stringify(data));
        }
    }
    checkLocalStorage() {
        // Repopulates the short answer text
        // which was stored into local storage.
        var answer = "";
        if (this.graderactive) {
            return;
        }
        var len = localStorage.length;
        if (len > 0) {
            var ex = localStorage.getItem(this.localStorageKey());
            if (ex !== null) {
                try {
                    var storedData = JSON.parse(ex);
                    answer = storedData.answer;
                } catch (err) {
                    // error while parsing; likely due to bad value stored in storage
                    console.log(err.message);
                    localStorage.removeItem(this.localStorageKey());
                    return;
                }
                let solution = $("#" + this.divid + "_solution");
                solution.text(answer);
                this.renderMath(answer);
                this.feedbackDiv.innerHTML =
                    "Your current saved answer is shown above.";
                $(this.feedbackDiv).removeClass("alert-danger");
                $(this.feedbackDiv).addClass("alert alert-success");
            }
        }
    }
    restoreAnswers(data) {
        // Restore answers from storage retrieval done in RunestoneBase
        // sometimes data.answer can be null
        if (!data.answer) {
            data.answer = "";
        }
        this.answer = data.answer;
        this.jTextArea.value = this.answer;
        this.renderMath(this.answer);

        let p = document.createElement("p");
        this.jInputDiv.appendChild(p);
        var tsString = "";
        if (data.timestamp) {
            tsString = new Date(data.timestamp).toLocaleString();
        } else {
            tsString = "";
        }
        $(p).text(tsString);
        if (data.last_answer) {
            this.current_answer = "ontime";
            let toggle_answer_button = document.createElement("button");
            toggle_answer_button.type = "button";
            $(toggle_answer_button).text("Show Late Answer");
            $(toggle_answer_button).addClass("btn btn-warning");
            $(toggle_answer_button).css("margin-left", "5px");

            $(toggle_answer_button).click(
                function () {
                    var display_timestamp, button_text;
                    if (this.current_answer === "ontime") {
                        this.jTextArea.value = data.last_answer;
                        this.answer = data.last_answer;
                        display_timestamp = new Date(
                            data.last_timestamp
                        ).toLocaleString();
                        button_text = "Show on-Time Answer";
                        this.current_answer = "late";
                    } else {
                        this.jTextArea.value = data.answer;
                        this.answer = data.answer;
                        display_timestamp = tsString;
                        button_text = "Show Late Answer";
                        this.current_answer = "ontime";
                    }
                    this.renderMath(this.answer);
                    $(p).text(`Submitted: ${display_timestamp}`);
                    $(toggle_answer_button).text(button_text);
                }.bind(this)
            );

            this.buttonDiv.appendChild(toggle_answer_button);
        }
        let feedbackStr = "Your current saved answer is shown above.";
        if (typeof data.score !== "undefined") {
            feedbackStr = `Score: ${data.score}`;
        }
        if (data.comment) {
            feedbackStr += ` -- ${data.comment}`;
        }
        this.feedbackDiv.innerHTML = feedbackStr;

        $(this.feedbackDiv).removeClass("alert-danger");
        $(this.feedbackDiv).addClass("alert alert-success");
    }

    disableInteraction() {
        this.jTextArea.disabled = true;
    }
}

/*=================================
== Find the custom HTML tags and ==
==   execute our code on them    ==
=================================*/
$(document).ready(function () {
    $("[data-component=shortanswer]").each(function (index) {
        if ($(this).closest("[data-component=timedAssessment]").length == 0) {
            // If this element exists within a timed component, don't render it here
            try {
                saList[this.id] = new ShortAnswer({
                    orig: this,
                    useRunestoneServices: eBookConfig.useRunestoneServices,
                });
            } catch (err) {
                console.log(`Error rendering ClickableArea Problem ${this.id}
                Details: ${err}`);
            }
        }
    });
});


/***/ }),

/***/ "./runestone/shortanswer/js/timed_shortanswer.js":
/*!*******************************************************!*\
  !*** ./runestone/shortanswer/js/timed_shortanswer.js ***!
  \*******************************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return TimedShortAnswer; });
/* harmony import */ var _shortanswer_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./shortanswer.js */ "./runestone/shortanswer/js/shortanswer.js");


class TimedShortAnswer extends _shortanswer_js__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(opts) {
        super(opts);
        this.renderTimedIcon(this.containerDiv);
        this.hideButtons();
    }
    hideButtons() {
        $(this.submitButton).hide();
    }
    renderTimedIcon(component) {
        // renders the clock icon on timed components.    The component parameter
        // is the element that the icon should be appended to.
        var timeIconDiv = document.createElement("div");
        var timeIcon = document.createElement("img");
        $(timeIcon).attr({
            src: "../_static/clock.png",
            style: "width:15px;height:15px",
        });
        timeIconDiv.className = "timeTip";
        timeIconDiv.title = "";
        timeIconDiv.appendChild(timeIcon);
        $(component).prepend(timeIconDiv);
    }
    checkCorrectTimed() {
        return "I"; // we ignore this in the grading
    }
    hideFeedback() {
        $(this.feedbackDiv).hide();
    }
}

if (typeof window.component_factory === "undefined") {
    window.component_factory = {};
}

window.component_factory.shortanswer = function (opts) {
    if (opts.timed) {
        return new TimedShortAnswer(opts);
    }
    return new _shortanswer_js__WEBPACK_IMPORTED_MODULE_0__["default"](opts);
};


/***/ }),

/***/ "./runestone/showeval/css/showEval.css":
/*!*********************************************!*\
  !*** ./runestone/showeval/css/showEval.css ***!
  \*********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var api = __webpack_require__(/*! ../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
            var content = __webpack_require__(/*! !../../../node_modules/css-loader/dist/cjs.js!./showEval.css */ "./node_modules/css-loader/dist/cjs.js!./runestone/showeval/css/showEval.css");

            content = content.__esModule ? content.default : content;

            if (typeof content === 'string') {
              content = [[module.i, content, '']];
            }

var options = {};

options.insert = "head";
options.singleton = false;

var update = api(content, options);



module.exports = content.locals || {};

/***/ }),

/***/ "./runestone/showeval/js/showEval.js":
/*!*******************************************!*\
  !*** ./runestone/showeval/js/showEval.js ***!
  \*******************************************/
/*! exports provided: ShowEval */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ShowEval", function() { return ShowEval; });
/* harmony import */ var _common_js_runestonebase__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/js/runestonebase */ "./runestone/common/js/runestonebase.js");
/* harmony import */ var _css_showEval_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../css/showEval.css */ "./runestone/showeval/css/showEval.css");
/* harmony import */ var _css_showEval_css__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_showEval_css__WEBPACK_IMPORTED_MODULE_1__);
/*
This component is based on ...
ShowEval, a JS module for creating visualizations of expression evaluation. Mainly for programming tutorials.
0.9.1

Al Sweigart
al@inventwithpython.com
https://github.com/asweigart/
*/





var seList = [];

class ShowEval {
    constructor(opts) {
        this.divid = opts.orig.id;
        this.container = $(`#${this.divid}`);
        this.container.addClass("showEval");
        let steps = [];
        for (let s of opts.raw) {
            steps.push(s.replace(/\\/g, ""));
        }
        this.steps = steps.slice();
        this.currentStep = 0;
        this.createTrace = $(opts.orig).data("tracemode"); // TODO - reset doesn't work for traces
        this.rb = new _common_js_runestonebase__WEBPACK_IMPORTED_MODULE_0__["default"](opts);
        // create elements
        this.currentStepDiv = $("<div>").addClass("currentStepDiv");
        this.container.append(this.currentStepDiv);
        this.currentStepDiv.append($("<span>").addClass("pre"));
        this.currentStepDiv.append($("<span>").addClass("eval"));
        this.currentStepDiv.append($("<span>").addClass("post"));
        this.currentStepDiv.append($("<div>").addClass("anno"));
        this.setNextButton(`#${this.divid}_nextStep`);
        this.setResetButton(`#${this.divid}_reset`);

        // parse steps and turn into a 4-string array: ['pre', 'before eval', 'after eval', 'post']
        for (var i = 0; i < this.steps.length; i++) {
            var s = this.steps[i];
            let endpoint, pItem, comment;

            if (s.includes("##")) {
                // If there is an annotation
                endpoint = s.indexOf("##");
                comment = s.substring(endpoint + 2, s.length);
            } else {
                endpoint = s.length;
                comment = false;
            }
            this.steps[i] = [
                s.substring(0, s.indexOf("{{")), // 'pre'
                s.substring(s.indexOf("{{") + 2, s.indexOf("}}{{")), // 'before eval'
                s.substring(
                    s.indexOf("}}{{") + 4,
                    s.indexOf("}}", s.indexOf("}}{{") + 4)
                ), // 'after eval'
                s.substring(
                    s.indexOf("}}", s.indexOf("}}{{") + 4) + 2,
                    endpoint
                ),
            ]; // 'post'

            this.steps[i].push(comment); // 'anno'
        }
        this.reset();
        this.rb.caption = "ShowEval";
        this.rb.containerDiv = this.container[0].parentElement;
        this.rb.divid = this.container[0].id;
        this.rb.addCaption("runestone");
    }

    setNextButton(nextButtonSelector) {
        var thisObj = this; // uhg, javascript
        $(nextButtonSelector).click(function () {
            thisObj.evaluateStep(nextButtonSelector);
        });
    }

    setResetButton(resetButtonSelector) {
        var thisObj = this; // uhg, javascript
        $(resetButtonSelector).click(function () {
            thisObj.reset(0);
        });
    }

    reset() {
        this.container.find(".previousStep").remove();
        this.setStep(0);
        this.rb.logBookEvent({
            event: "showeval",
            act: "reset",
            div_id: this.container[0].id,
        });
    }

    setStep(step) {
        this.currentStep = step;
        let newWidth = this.getWidth(this.steps[this.currentStep][1]);
        if (this.steps[step][4]) {
            this.currentStepDiv.children(".anno").html(this.steps[step][4]);
            this.currentStepDiv.children(".anno").show();
        } else {
            this.currentStepDiv.children(".anno").hide();
        }
        this.currentStepDiv.children(".eval").width(newWidth);
        this.currentStepDiv.children(".pre").html(this.steps[step][0]);
        this.currentStepDiv.children(".eval").html(this.steps[step][1]);
        this.currentStepDiv.children(".post").html(this.steps[step][3]);
    }

    getWidth(text) {
        // TODO - class style must match or else width will be off.
        var newElem = $("<div>")
            .addClass("showEval evalCont")
            .hide()
            .html(text);
        $("body").append(newElem);
        var newWidth = newElem.width() + 1; // +1 is a hack
        newElem.remove();

        return newWidth;
    }

    createPreviousStepDiv(step) {
        this.currentStepDiv.before(
            $("<div>")
                .addClass("previousStep")
                .html(
                    this.steps[step][0] +
                        this.steps[step][1] +
                        this.steps[step][3]
                )
        );
    }

    evaluateStep(buttonId, step) {
        this.currentStepDiv.children(".anno").hide();
        $(buttonId).attr("disabled", true);
        if (step === undefined) {
            step = this.currentStep;
        }
        if (this.currentStep >= this.steps.length) {
            //this.currentStep = 0;
            //step = 0;
            $(buttonId).attr("disabled", false);
            return; // do nothing if on last step
        }
        //this.setStep(step);

        var fadeInSpeed = 0;
        if (this.createTrace) {
            this.createPreviousStepDiv(step);
            this.currentStepDiv.hide();
            fadeInSpeed = 200;
        }

        let newWidth = this.getWidth(this.steps[step][2]);
        var evalElem = this.currentStepDiv.children(".eval");

        var thisShowEval = this;

        evalElem.css("color", "red");

        this.currentStepDiv.fadeTo(fadeInSpeed, 1, function () {
            window.setTimeout(function () {
                evalElem.fadeTo(400, 0, function () {
                    //evalElem.css('overflow', 'hidden');
                    evalElem.animate(
                        { width: newWidth, duration: 400 },
                        function () {
                            evalElem.html(thisShowEval.steps[step][2]);
                            evalElem.fadeTo(400, 1, function () {
                                window.setTimeout(function () {
                                    //evalElem.css('overflow', 'visible');
                                    evalElem.css("color", "#333");
                                    thisShowEval.currentStep += 1;
                                    if (
                                        thisShowEval.currentStep <
                                        thisShowEval.steps.length
                                    ) {
                                        thisShowEval.setStep(
                                            thisShowEval.currentStep
                                        );
                                    }
                                    $(buttonId).attr("disabled", false);
                                }, 600);
                            });
                        }
                    );
                });
            }, 600);
        });

        this.rb.logBookEvent({
            event: "showeval",
            act: "next",
            div_id: this.container[0].id,
        });
    }
}

/*=================================
== Find the custom HTML tags and ==
==   execute our code on them    ==
=================================*/
$(document).bind("runestone:login-complete", function () {
    $("[data-childcomponent=showeval]").each(function (index) {
        // MC
        var opts = {
            orig: this,
            useRunestoneServices: eBookConfig.useRunestoneServices,
        };
        opts.raw = window.raw_steps[this.id];
        if ($(this).closest("[data-component=timedAssessment]").length == 0) {
            // If this element exists within a timed component, don't render it here
            seList[this.id] = new ShowEval(opts);
        }
    });
});

if (typeof window.component_factory === "undefined") {
    window.component_factory = {};
}
window.component_factory["showeval"] = function (opts) {
    return new ShowEval(opts);
};


/***/ }),

/***/ "./runestone/spreadsheet/css/spreadsheet.css":
/*!***************************************************!*\
  !*** ./runestone/spreadsheet/css/spreadsheet.css ***!
  \***************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var api = __webpack_require__(/*! ../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
            var content = __webpack_require__(/*! !../../../node_modules/css-loader/dist/cjs.js!./spreadsheet.css */ "./node_modules/css-loader/dist/cjs.js!./runestone/spreadsheet/css/spreadsheet.css");

            content = content.__esModule ? content.default : content;

            if (typeof content === 'string') {
              content = [[module.i, content, '']];
            }

var options = {};

options.insert = "head";
options.singleton = false;

var update = api(content, options);



module.exports = content.locals || {};

/***/ }),

/***/ "./runestone/spreadsheet/js/spreadsheet.js":
/*!*************************************************!*\
  !*** ./runestone/spreadsheet/js/spreadsheet.js ***!
  \*************************************************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _common_js_runestonebase__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/js/runestonebase */ "./runestone/common/js/runestonebase.js");
/* harmony import */ var jexcel__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! jexcel */ "./node_modules/jexcel/dist/jexcel.js");
/* harmony import */ var jexcel__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(jexcel__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _css_spreadsheet_css__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../css/spreadsheet.css */ "./runestone/spreadsheet/css/spreadsheet.css");
/* harmony import */ var _css_spreadsheet_css__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_css_spreadsheet_css__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var jexcel_dist_jexcel_css__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! jexcel/dist/jexcel.css */ "./node_modules/jexcel/dist/jexcel.css");
/* harmony import */ var jexcel_dist_jexcel_css__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(jexcel_dist_jexcel_css__WEBPACK_IMPORTED_MODULE_3__);








window.ssList = {};

class SpreadSheet extends _common_js_runestonebase__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(opts) {
        super(opts);
        let orig = opts.orig;
        this.div_id = orig.id;
        this.sheet_id = `${this.div_id}_sheet`;
        this.data = eval(`${this.div_id}_data`);
        this.autograde = $(orig).data("autograde");
        this.suffix = window[`${this.div_id}_asserts`];
        this.mindimensions = $(orig).data("mindimensions");
        this.colwidths = $(orig).data("colwidths");
        this.coltitles = eval($(orig).data("coltitles"));
        this.maxHeight = 50;
        // Render the components
        this.renderSheet();

        if (this.autograde) {
            this.addAutoGradeButton();
            this.addOutput();
        }

        this.caption = "Spreadsheet";
        this.divid = this.div_id;
        this.containerDiv = document.getElementById(this.div_id);
        this.addCaption("runestone");
    }

    renderSheet() {
        let div = document.getElementById(this.sheet_id);
        let opts = { data: this.data, tableHeight: this.maxHeight };
        if (this.mindimensions) {
            opts.minDimensions = this.mindimensions;
        }
        opts.columns = [];
        if (this.colwidths) {
            for (let w of this.colwidths) {
                opts.columns.push({ width: w });
            }
        }
        if (this.coltitles) {
            for (let i in this.coltitles) {
                if (opts.columns[i]) {
                    opts.columns[i].title = unescape(this.coltitles[i]);
                } else {
                    opts.columns.push({ title: this.coltitles[i] });
                }
            }
        }

        this.table = jexcel__WEBPACK_IMPORTED_MODULE_1___default()(div, opts);

        // Set background of cells that are autograded
        if (this.suffix) {
            for (let test of this.suffix) {
                let assert, loc, oper, expected;
                [assert, loc, oper, expected] = test.split(/\s+/);
                $(`#${this.div_id}_sheet`)
                    .find(this.getCellSelector(loc))
                    .css("background-color", "#d4e3ff");
            }
        }
    }

    addAutoGradeButton() {
        let div = document.getElementById(this.div_id);
        var butt = document.createElement("button");
        $(butt).text("Check");
        $(butt).addClass("btn btn-success run-button");
        div.appendChild(butt);
        this.gradeButton = butt;
        $(butt).click(this.doAutoGrade.bind(this));
        $(butt).attr("type", "button");
        $(butt).css("display", "block");
    }

    addOutput() {
        this.output = document.createElement("pre");
        this.output.id = `${this.div_id}_stdout`;
        $(this.output).css("visibility", "hidden");
        let div = document.getElementById(this.div_id);
        div.appendChild(this.output);
    }

    doAutoGrade() {
        let tests = this.suffix;
        this.passed = 0;
        this.failed = 0;
        // Tests should be of the form
        // assert cell oper value for example
        // assert A4 == 3
        let result = "";
        tests = tests.filter(function (s) {
            return s.indexOf("assert") > -1;
        });
        for (let test of tests) {
            let assert, loc, oper, expected;
            [assert, loc, oper, expected] = test.split(/\s+/);
            result += this.testOneAssert(loc, oper, expected);
            result += "\n";
        }
        let pct = (100 * this.passed) / (this.passed + this.failed);
        pct = pct.toLocaleString(undefined, { maximumFractionDigits: 2 });
        result += `You passed ${this.passed} out of ${
            this.passed + this.failed
        } tests for ${pct}%`;
        this.logBookEvent({
            event: "unittest",
            div_id: this.div_id,
            course: eBookConfig.course,
            act: `percent:${pct}:passed:${this.passed}:failed:${this.failed}`,
        });
        $(this.output).css("visibility", "visible");
        $(this.output).text(result);
    }

    testOneAssert(cell, oper, expected) {
        let actual = this.getCellDisplayValue(cell);
        const operators = {
            "==": function (operand1, operand2) {
                return operand1 == operand2;
            },
            "!=": function (operand1, operand2) {
                return operand1 != operand2;
            },
            ">": function (operand1, operand2) {
                return operand1 > operand2;
            },
            "<": function (operand1, operand2) {
                return operand1 > operand2;
            },
        };

        let res = operators[oper](actual, expected);
        let output = "";
        if (res) {
            output = `Pass: ${actual} ${oper} ${expected} in ${cell}`;
            $(`#${this.div_id}_sheet`)
                .find(this.getCellSelector(cell))
                .css("background-color", "#ccffcc");
            this.passed++;
        } else {
            output = `Failed ${actual} ${oper} ${expected} in cell ${cell}`;
            $(`#${this.div_id}_sheet`)
                .find(this.getCellSelector(cell))
                .css("background-color", "#ff9980");
            this.failed++;
        }
        return output;
    }

    // If the cell contains a formula, this call will return the formula not the computed value
    getCellSource(cell) {
        return this.table.getValue(cell);
    }

    // If the cell contains a formula this call will return the computed value
    getCellDisplayValue(cell) {
        let res = this.table.el.querySelector(this.getCellSelector(cell));
        return res.innerText;
    }

    getCellSelector(cell) {
        let parts = cell.match(/\$?([A-Z]+)\$?([0-9]+)/);
        let x = this.columnToIndex(parts[1]);
        let y = parts[2] - 1;
        return `[data-x="${x}"][data-y="${y}"]`;
    }
    columnToIndex(colName) {
        // Convert the column name to a number A = 0 AA = 26 BA = 52, etc
        let base = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        let result = 0;

        for (
            let i = 0, j = colName.length - 1;
            i < colName.length;
            i += 1, j -= 1
        ) {
            result += Math.pow(base.length, j) * (base.indexOf(colName[i]) + 1);
        }

        return result - 1;
    }
}

$(document).bind("runestone:login-complete", function () {
    $("[data-component=spreadsheet]").each(function (index) {
        // MC
        var opts = {
            orig: this,
            useRunestoneServices: eBookConfig.useRunestoneServices,
        };
        try {
            window.ssList[this.id] = new SpreadSheet(opts);
        } catch (err) {
            console.log(`Error rendering SpreadSheet Problem ${this.id}
                         Details: ${err}`);
            console.log(err.stack);
        }
    });
});

if (typeof window.component_factory === "undefined") {
    window.component_factory = {};
}
window.component_factory.spreadsheet = function (opts) {
    return new SpreadSheet(opts);
};


/***/ }),

/***/ "./runestone/tabbedStuff/css/tabbedstuff.css":
/*!***************************************************!*\
  !*** ./runestone/tabbedStuff/css/tabbedstuff.css ***!
  \***************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var api = __webpack_require__(/*! ../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
            var content = __webpack_require__(/*! !../../../node_modules/css-loader/dist/cjs.js!./tabbedstuff.css */ "./node_modules/css-loader/dist/cjs.js!./runestone/tabbedStuff/css/tabbedstuff.css");

            content = content.__esModule ? content.default : content;

            if (typeof content === 'string') {
              content = [[module.i, content, '']];
            }

var options = {};

options.insert = "head";
options.singleton = false;

var update = api(content, options);



module.exports = content.locals || {};

/***/ }),

/***/ "./runestone/tabbedStuff/js/tabbedstuff.js":
/*!*************************************************!*\
  !*** ./runestone/tabbedStuff/js/tabbedstuff.js ***!
  \*************************************************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _common_js_runestonebase__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/js/runestonebase */ "./runestone/common/js/runestonebase.js");
/* harmony import */ var _css_tabbedstuff_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../css/tabbedstuff.css */ "./runestone/tabbedStuff/css/tabbedstuff.css");
/* harmony import */ var _css_tabbedstuff_css__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_tabbedstuff_css__WEBPACK_IMPORTED_MODULE_1__);
/*==========================================
=======    Master tabbedstuff.js    ========
============================================
===     This file contains the JS for    ===
=== the Runestone tabbedStuff component. ===
============================================
===              Created by              ===
===           Isaiah Mayerchak           ===
===               06/15/15               ===
===             Brad Miller              ===
===               06/15/15               ===
==========================================*/


var TSList = {}; // Dictionary that contains all instances of TabbedStuff objects




// Define TabbedStuff object
class TabbedStuff extends _common_js_runestonebase__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(opts) {
        super(opts);
        var orig = opts.orig;
        this.origElem = orig; // entire original <div> element that will be replaced by new HTML
        this.divid = orig.id;
        this.inactive = false;
        if ($(this.origElem).is("[data-inactive]")) {
            this.inactive = true;
        }
        this.togglesList = []; // For use in Codemirror/Disqus
        this.childTabs = [];
        this.populateChildTabs();
        this.activeTab = 0; // default value--activeTab is the index of the tab that starts open
        this.findActiveTab();
        this.createTabContainer();
    }
    /*===========================================
    == Update attributes of instance variables ==
    ==    variables according to specifications    ==
    ===========================================*/
    populateChildTabs() {
        for (var i = 0; i < this.origElem.childNodes.length; i++) {
            if ($(this.origElem.childNodes[i]).data("component") === "tab") {
                this.childTabs.push(this.origElem.childNodes[i]);
            }
        }
    }
    findActiveTab() {
        for (var i = 0; i < this.childTabs.length; i++) {
            if ($(this.childTabs[i]).is("[data-active]")) {
                this.activeTab = i;
            }
        }
    }
    /*==========================================
    == Creating/appending final HTML elements ==
    ==========================================*/
    createTabContainer() {
        this.replacementDiv = document.createElement("div");
        this.replacementDiv.id = this.divid;
        $(this.replacementDiv).addClass(this.origElem.getAttribute("class"));
        $(this.replacementDiv).attr({ role: "tabpanel" });
        this.tabsUL = document.createElement("ul");
        this.tabsUL.id = this.divid + "_tab";
        $(this.tabsUL).addClass("nav nav-tabs");
        $(this.tabsUL).attr({ role: "tablist" });
        this.tabContentDiv = document.createElement("div"); // Create tab content container that holds tab panes w/content
        $(this.tabContentDiv).addClass("tab-content");
        this.createTabs(); // create and append tabs to the <ul>
        this.replacementDiv.appendChild(this.tabsUL);
        this.replacementDiv.appendChild(this.tabContentDiv);
        this.addCMD(); // Adds fuctionality for Codemirror/Disqus
        $(this.origElem).replaceWith(this.replacementDiv);
    }
    createTabs() {
        // Create tabs in format <li><a><span></span></a></li> to be appended to the <ul>
        for (var i = 0; i < this.childTabs.length; i++) {
            // First create tabname and tabfriendly name that has no spaces to be used for the id
            var tabListElement = document.createElement("li");
            $(tabListElement).attr({
                role: "presentation",
                "aria-controls": this.divid + "-" + i
            });
            // Using bootstrap tabs functionality
            var tabElement = document.createElement("a");
            $(tabElement).attr({
                "data-toggle": "tab",
                href: "#" + this.divid + "-" + i,
                role: "tab"
            });
            var tabTitle = document.createElement("span"); // Title of tab--what the user will see
            tabTitle.textContent = $(this.childTabs[i]).data("tabname");
            tabElement.appendChild(tabTitle);
            tabListElement.appendChild(tabElement);
            this.tabsUL.appendChild(tabListElement);
            // tabPane is what holds the contents of the tab
            var tabPaneDiv = document.createElement("div");
            tabPaneDiv.id = this.divid + "-" + i;
            $(tabPaneDiv).addClass("tab-pane");
            $(tabPaneDiv).attr({
                role: "tabpanel"
            });
            //var tabHTML = $(this.childTabs[i]).html();
            //$(tabPaneDiv).html(tabHTML);
            tabPaneDiv.appendChild(this.childTabs[i]);
            if (!this.inactive) {
                if (this.activeTab === i) {
                    $(tabListElement).addClass("active");
                    $(tabPaneDiv).addClass("active");
                }
            }
            this.togglesList.push(tabElement);
            this.tabContentDiv.appendChild(tabPaneDiv);
        }
    }
    /*===================================
    == Codemirror/Disqus functionality ==
    ===================================*/
    addCMD() {
        $(this.togglesList).on("shown.bs.tab", function(e) {
            var content_div = $(e.target.attributes.href.value);
            content_div.find(".disqus_thread_link").each(function() {
                $(this).click();
            });
            content_div.find(".CodeMirror").each(function(i, el) {
                el.CodeMirror.refresh();
            });
        });
    }
}

/*=================================
== Find the custom HTML tags and ==
==     execute our code on them        ==
=================================*/
$(document).ready(function() {
    $("[data-component=tabbedStuff]").each(function(index) {
        TSList[this.id] = new TabbedStuff({ orig: this });
    });
});


/***/ }),

/***/ "./runestone/timed/css/timed.css":
/*!***************************************!*\
  !*** ./runestone/timed/css/timed.css ***!
  \***************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var api = __webpack_require__(/*! ../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
            var content = __webpack_require__(/*! !../../../node_modules/css-loader/dist/cjs.js!./timed.css */ "./node_modules/css-loader/dist/cjs.js!./runestone/timed/css/timed.css");

            content = content.__esModule ? content.default : content;

            if (typeof content === 'string') {
              content = [[module.i, content, '']];
            }

var options = {};

options.insert = "head";
options.singleton = false;

var update = api(content, options);



module.exports = content.locals || {};

/***/ }),

/***/ "./runestone/timed/js/timed.js":
/*!*************************************!*\
  !*** ./runestone/timed/js/timed.js ***!
  \*************************************/
/*! exports provided: TimedList, default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TimedList", function() { return TimedList; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Timed; });
/* harmony import */ var _common_js_runestonebase_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/js/runestonebase.js */ "./runestone/common/js/runestonebase.js");
/* harmony import */ var _fitb_js_timedfitb_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../fitb/js/timedfitb.js */ "./runestone/fitb/js/timedfitb.js");
/* harmony import */ var _mchoice_js_timedmc_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../mchoice/js/timedmc.js */ "./runestone/mchoice/js/timedmc.js");
/* harmony import */ var _shortanswer_js_timed_shortanswer_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../shortanswer/js/timed_shortanswer.js */ "./runestone/shortanswer/js/timed_shortanswer.js");
/* harmony import */ var _activecode_js_acfactory_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../activecode/js/acfactory.js */ "./runestone/activecode/js/acfactory.js");
/* harmony import */ var _clickableArea_js_timedclickable__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../clickableArea/js/timedclickable */ "./runestone/clickableArea/js/timedclickable.js");
/* harmony import */ var _dragndrop_js_timeddnd_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../dragndrop/js/timeddnd.js */ "./runestone/dragndrop/js/timeddnd.js");
/* harmony import */ var _parsons_js_timedparsons_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../parsons/js/timedparsons.js */ "./runestone/parsons/js/timedparsons.js");
/* harmony import */ var _selectquestion_js_selectone__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../../selectquestion/js/selectone */ "./runestone/selectquestion/js/selectone.js");
/* harmony import */ var _css_timed_css__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../css/timed.css */ "./runestone/timed/css/timed.css");
/* harmony import */ var _css_timed_css__WEBPACK_IMPORTED_MODULE_9___default = /*#__PURE__*/__webpack_require__.n(_css_timed_css__WEBPACK_IMPORTED_MODULE_9__);
/*==========================================
========      Master timed.js     =========
============================================
===     This file contains the JS for    ===
===     the Runestone timed component.   ===
============================================
===              Created By              ===
===             Kirby Olson              ===
===               6/11/15                ===
==========================================*/













var TimedList = {}; // Timed dictionary

// Timed constructor
class Timed extends _common_js_runestonebase_js__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(opts) {
        super(opts);
        var orig = opts.orig;
        this.origElem = orig; // the entire element of this timed assessment and all of its children
        this.divid = orig.id;
        this.children = this.origElem.childNodes;
        this.visited = [];
        this.timeLimit = 0;
        this.limitedTime = false;
        if (!isNaN($(this.origElem).data("time"))) {
            this.timeLimit = parseInt($(this.origElem).data("time"), 10) * 60; // time in seconds to complete the exam
            this.startingTime = this.timeLimit;
            this.limitedTime = true;
        }
        this.showFeedback = true;
        if ($(this.origElem).is("[data-no-feedback]")) {
            this.showFeedback = false;
        }
        this.showResults = true;
        if ($(this.origElem).is("[data-no-result]")) {
            this.showResults = false;
        }
        this.random = false;
        if ($(this.origElem).is("[data-random]")) {
            this.random = true;
        }
        this.showTimer = true;
        if ($(this.origElem).is("[data-no-timer]")) {
            this.showTimer = false;
        }
        this.fullwidth = false;
        if ($(this.origElem).is("[data-fullwidth]")) {
            this.fullwidth = true;
        }
        this.nopause = false;
        if ($(this.origElem).is("[data-no-pause]")) {
            this.nopause = true;
        }

        this.running = 0;
        this.paused = 0;
        this.done = 0;
        this.taken = 0;
        this.score = 0;
        this.incorrect = 0;
        this.correctStr = "";
        this.incorrectStr = "";
        this.skippedStr = "";
        this.skipped = 0;
        this.currentQuestionIndex = 0; // Which question is currently displaying on the page
        this.renderedQuestionArray = []; // list of all problems
        this.getNewChildren();
        // One small step to eliminate students from doing view source
        // this won't stop anyone with determination but may prevent casual peeking
        if (!eBookConfig.enableDebug) {
            document.body.oncontextmenu = function () {
                return false;
            };
        }
        this.checkAssessmentStatus().then(
            function () {
                this.renderTimedAssess();
            }.bind(this)
        );
    }

    getNewChildren() {
        this.newChildren = [];
        for (var i = 0; i < this.origElem.childNodes.length; i++) {
            this.newChildren.push(this.origElem.childNodes[i]);
        }
    }

    async checkAssessmentStatus() {
        // Has the user taken this exam?  Inquiring minds want to know
        // If a user has not taken this exam then we want to make sure
        // that if a question has been seen by the student before we do
        // not populate previous answers.
        let sendInfo = {
            div_id: this.divid,
            course_name: eBookConfig.course,
        };
        console.log(sendInfo);
        if (eBookConfig.useRunestoneServices) {
            let request = new Request(
                eBookConfig.ajaxURL + "tookTimedAssessment",
                {
                    method: "POST",
                    headers: this.jsonHeaders,
                    body: JSON.stringify(sendInfo),
                }
            );
            let response = await fetch(request);
            let data = await response.json();
            this.taken = data.tookAssessment;
            this.assessmentTaken = this.taken;
            if (!this.taken) {
                localStorage.clear();
            }
            console.log("done with check status");
            return response;
        } else {
            this.taken = false;
            this.assessmentTaken = false;
            return Promise.resolve();
        }
    }

    /*===============================
    === Generating new Timed HTML ===
    ===============================*/
    async renderTimedAssess() {
        console.log("rendering timed ");
        // create renderedQuestionArray returns a promise
        //
        this.createRenderedQuestionArray();
        if (this.random) {
            this.randomizeRQA();
        }
        this.renderContainer();
        this.renderTimer();
        await this.renderControlButtons();
        this.assessDiv.appendChild(this.timedDiv); // This can't be appended in renderContainer because then it renders above the timer and control buttons.
        if (this.renderedQuestionArray.length > 1) this.renderNavControls();
        this.renderSubmitButton();
        this.renderFeedbackContainer();
        this.useRunestoneServices = eBookConfig.useRunestoneServices;
        // Replace intermediate HTML with rendered HTML
        $(this.origElem).replaceWith(this.assessDiv);
        // check if already taken and if so show results
        this.styleExamElements(); // rename to renderPossibleResults
        this.checkServer("timedExam");
    }

    renderContainer() {
        this.assessDiv = document.createElement("div"); // container for the entire Timed Component
        if (this.fullwidth) {
            // allow the container to fill the width - barb
            $(this.assessDiv).attr({
                style: "max-width:none",
            });
        }
        this.assessDiv.id = this.divid;
        this.timedDiv = document.createElement("div"); // div that will hold the questions for the timed assessment
        this.navDiv = document.createElement("div"); // For navigation control
        $(this.navDiv).attr({
            style: "text-align:center",
        });
        this.switchContainer = document.createElement("div");
        this.switchContainer.classList.add("switchcontainer");
        this.switchDiv = document.createElement("div"); // is replaced by the questions
        this.timedDiv.appendChild(this.navDiv);
        this.timedDiv.appendChild(this.switchContainer);
        this.switchContainer.appendChild(this.switchDiv);
        $(this.timedDiv).attr({
            id: "timed_Test",
            style: "display:none",
        });
    }

    renderTimer() {
        this.wrapperDiv = document.createElement("div");
        this.timerContainer = document.createElement("P");
        this.wrapperDiv.id = this.divid + "-startWrapper";
        this.timerContainer.id = this.divid + "-output";
        this.wrapperDiv.appendChild(this.timerContainer);
        this.showTime();
    }

    renderControlButtons() {
        this.controlDiv = document.createElement("div");
        $(this.controlDiv).attr({
            id: "controls",
            style: "text-align: center",
        });
        this.startBtn = document.createElement("button");
        this.pauseBtn = document.createElement("button");
        $(this.startBtn).attr({
            class: "btn btn-success",
            id: "start",
            tabindex: "0",
            role: "button",
        });
        this.startBtn.textContent = "Start";
        this.startBtn.addEventListener(
            "click",
            async function () {
                $(this.finishButton).hide(); // hide the finish button for now
                let mess = document.createElement("p");
                mess.innerHTML =
                    "<strong>Warning: You will lose all of your work if you close this tab or the browser.</strong>  Make sure you click the Finish Exam button to submit your work!";
                this.controlDiv.appendChild(mess);
                await this.renderTimedQuestion();
                this.startAssessment();
            }.bind(this),
            false
        );
        $(this.pauseBtn).attr({
            class: "btn btn-default",
            id: "pause",
            disabled: "true",
            tabindex: "0",
            role: "button",
        });
        this.pauseBtn.textContent = "Pause";
        this.pauseBtn.addEventListener(
            "click",
            function () {
                this.pauseAssessment();
            }.bind(this),
            false
        );
        if (!this.taken) {
            this.controlDiv.appendChild(this.startBtn);
        }
        if (!this.nopause) {
            this.controlDiv.appendChild(this.pauseBtn);
        }
        this.assessDiv.appendChild(this.wrapperDiv);
        this.assessDiv.appendChild(this.controlDiv);
    }

    renderNavControls() {
        this.pagNavList = document.createElement("ul");
        $(this.pagNavList).addClass("pagination");
        this.leftContainer = document.createElement("li");
        this.leftNavButton = document.createElement("button");
        this.leftNavButton.innerHTML = "&#8249; Prev";
        $(this.leftNavButton).attr("aria-label", "Previous");
        $(this.leftNavButton).attr("tabindex", "0");
        $(this.leftNavButton).attr("role", "button");
        $(this.rightNavButton).attr("id", "prev");
        $(this.leftNavButton).css("cursor", "pointer");
        this.leftContainer.appendChild(this.leftNavButton);
        this.pagNavList.appendChild(this.leftContainer);
        this.rightContainer = document.createElement("li");
        this.rightNavButton = document.createElement("button");
        $(this.rightNavButton).attr("aria-label", "Next");
        $(this.rightNavButton).attr("tabindex", "0");
        $(this.rightNavButton).attr("role", "button");
        $(this.rightNavButton).attr("id", "next");
        this.rightNavButton.innerHTML = "Next &#8250;";
        $(this.rightNavButton).css("cursor", "pointer");
        this.rightContainer.appendChild(this.rightNavButton);
        this.pagNavList.appendChild(this.rightContainer);
        this.ensureButtonSafety();
        this.navDiv.appendChild(this.pagNavList);
        this.break = document.createElement("br");
        this.navDiv.appendChild(this.break);
        // render the question number jump buttons
        this.qNumList = document.createElement("ul");
        $(this.qNumList).attr("id", "pageNums");
        this.qNumWrapperList = document.createElement("ul");
        $(this.qNumWrapperList).addClass("pagination");
        var tmpLi, tmpA;
        for (var i = 0; i < this.renderedQuestionArray.length; i++) {
            tmpLi = document.createElement("li");
            tmpA = document.createElement("a");
            tmpA.innerHTML = i + 1;
            $(tmpA).css("cursor", "pointer");
            if (i === 0) {
                $(tmpLi).addClass("active");
            }
            tmpLi.appendChild(tmpA);
            this.qNumWrapperList.appendChild(tmpLi);
        }
        this.qNumList.appendChild(this.qNumWrapperList);
        this.navDiv.appendChild(this.qNumList);
        this.navBtnListeners();
    }

    // when moving off of a question in an active exam:
    // 1. show that the question has been seen, or mark it broken if it is in error.
    // 2. check and log the current answer
    async navigateAway() {
        if (
            this.renderedQuestionArray[this.currentQuestionIndex].state ==
            "broken_exam"
        ) {
            $(
                "ul#pageNums > ul > li:eq(" + this.currentQuestionIndex + ")"
            ).addClass("broken");
        }
        if (
            this.renderedQuestionArray[this.currentQuestionIndex].question
                .isAnswered
        ) {
            $(
                "ul#pageNums > ul > li:eq(" + this.currentQuestionIndex + ")"
            ).addClass("answered");
            await this.renderedQuestionArray[
                this.currentQuestionIndex
            ].question.checkCurrentAnswer();
            if (!this.done) {
                this.renderedQuestionArray[
                    this.currentQuestionIndex
                ].question.logCurrentAnswer();
            }
        }
    }
    async handleNextPrev(event) {
        if (!this.taken) {
            await this.navigateAway();
        }
        var target = $(event.target).text();
        if (target.match(/Next/)) {
            if ($(this.rightContainer).hasClass("disabled")) {
                return;
            }
            this.currentQuestionIndex++;
        } else if (target.match(/Prev/)) {
            if ($(this.leftContainer).hasClass("disabled")) {
                return;
            }
            this.currentQuestionIndex--;
        }
        await this.renderTimedQuestion();
        this.ensureButtonSafety();
        for (var i = 0; i < this.qNumList.childNodes.length; i++) {
            for (
                var j = 0;
                j < this.qNumList.childNodes[i].childNodes.length;
                j++
            ) {
                $(this.qNumList.childNodes[i].childNodes[j]).removeClass(
                    "active"
                );
            }
        }
        $(
            "ul#pageNums > ul > li:eq(" + this.currentQuestionIndex + ")"
        ).addClass("active");
    }

    async handleNumberedNav(event) {
        if (!this.taken) {
            await this.navigateAway();
        }
        for (var i = 0; i < this.qNumList.childNodes.length; i++) {
            for (
                var j = 0;
                j < this.qNumList.childNodes[i].childNodes.length;
                j++
            ) {
                $(this.qNumList.childNodes[i].childNodes[j]).removeClass(
                    "active"
                );
            }
        }

        var target = $(event.target).text();
        let oldIndex = this.currentQuestionIndex;
        this.currentQuestionIndex = parseInt(target) - 1;
        if (this.currentQuestionIndex > this.renderedQuestionArray.length) {
            console.log(`Error: bad index for ${target}`);
            this.currentQuestionIndex = oldIndex;
        }
        $(
            "ul#pageNums > ul > li:eq(" + this.currentQuestionIndex + ")"
        ).addClass("active");
        await this.renderTimedQuestion();
        this.ensureButtonSafety();
    }

    // set up events for navigation
    navBtnListeners() {
        // Next and Prev Listener
        this.pagNavList.addEventListener(
            "click",
            this.handleNextPrev.bind(this),
            false
        );

        // Numbered Listener
        this.qNumList.addEventListener(
            "click",
            this.handleNumberedNav.bind(this),
            false
        );
    }

    renderSubmitButton() {
        this.buttonContainer = document.createElement("div");
        $(this.buttonContainer).attr({
            style: "text-align:center",
        });
        this.finishButton = document.createElement("button");
        $(this.finishButton).attr({
            id: "finish",
            class: "btn btn-primary",
        });
        this.finishButton.textContent = "Finish Exam";
        this.finishButton.addEventListener(
            "click",
            async function () {
                if (
                    window.confirm(
                        "Clicking OK means you are ready to submit your answers and are finished with this assessment."
                    )
                ) {
                    await this.finishAssessment();
                }
            }.bind(this),
            false
        );
        this.controlDiv.appendChild(this.finishButton);
        $(this.finishButton).hide();
        this.timedDiv.appendChild(this.buttonContainer);
    }
    ensureButtonSafety() {
        if (this.currentQuestionIndex === 0) {
            if (this.renderedQuestionArray.length != 1) {
                $(this.rightContainer).removeClass("disabled");
            }
            $(this.leftContainer).addClass("disabled");
        }
        if (
            this.currentQuestionIndex >=
            this.renderedQuestionArray.length - 1
        ) {
            if (this.renderedQuestionArray.length != 1) {
                $(this.leftContainer).removeClass("disabled");
            }
            $(this.rightContainer).addClass("disabled");
        }
        if (
            this.currentQuestionIndex > 0 &&
            this.currentQuestionIndex < this.renderedQuestionArray.length - 1
        ) {
            $(this.rightContainer).removeClass("disabled");
            $(this.leftContainer).removeClass("disabled");
        }
    }
    renderFeedbackContainer() {
        this.scoreDiv = document.createElement("P");
        this.scoreDiv.id = this.divid + "results";
        this.scoreDiv.style.display = "none";
        this.assessDiv.appendChild(this.scoreDiv);
    }

    createRenderedQuestionArray() {
        // this finds all the assess questions in this timed assessment
        // We need to make a list of all the questions up front so we can set up navigation
        // but we do not want to render the questions until the student has navigated
        // Also adds them to this.renderedQuestionArray

        // todo:  This needs to be updated to account for the runestone div wrapper.

        // To accommodate the selectquestion type -- which is async! we need to wrap
        // all of this in a promise, so that we don't continue to render the timed
        // exam until all of the questions have been realized.
        var opts;
        for (var i = 0; i < this.newChildren.length; i++) {
            var tmpChild = this.newChildren[i];
            opts = {
                state: "prepared",
                orig: tmpChild,
                question: {},
                useRunestoneServices: eBookConfig.useRunestoneServices,
                timed: true,
                assessmentTaken: this.taken,
            };
            if ($(tmpChild).children("[data-component]").length > 0) {
                tmpChild = $(tmpChild).children("[data-component]")[0];
                opts.orig = tmpChild;
            }
            if ($(tmpChild).is("[data-component]")) {
                this.renderedQuestionArray.push(opts);
            }
        }
    }

    randomizeRQA() {
        var currentIndex = this.renderedQuestionArray.length,
            temporaryValue,
            randomIndex;
        // While there remain elements to shuffle...
        while (currentIndex !== 0) {
            // Pick a remaining element...
            randomIndex = Math.floor(Math.random() * currentIndex);
            currentIndex -= 1;
            // And swap it with the current element.
            temporaryValue = this.renderedQuestionArray[currentIndex];
            this.renderedQuestionArray[
                currentIndex
            ] = this.renderedQuestionArray[randomIndex];
            this.renderedQuestionArray[randomIndex] = temporaryValue;
        }
    }

    async renderTimedQuestion() {
        if (this.currentQuestionIndex >= this.renderedQuestionArray.length) {
            // sometimes the user clicks in the event area for the qNumList
            // But misses a number in that case the text is the concatenation
            // of all the numbers in the list!
            return;
        }
        // check the renderedQuestionArray to see if it has been rendered.
        let opts = this.renderedQuestionArray[this.currentQuestionIndex];
        let currentQuestion;
        if (opts.state === "prepared" || opts.state === "forreview") {
            let tmpChild = opts.orig;
            if ($(tmpChild).is("[data-component=selectquestion]")) {
                // SelectOne is async and will replace itself in this array with
                // the actual selected question
                opts.rqa = this.renderedQuestionArray;
                let newq = new _selectquestion_js_selectone__WEBPACK_IMPORTED_MODULE_8__["default"](opts);
                this.renderedQuestionArray[this.currentQuestionIndex] = {
                    question: newq,
                };
                try {
                    await newq.initialize();
                } catch (e) {
                    this.renderedQuestionArray[
                        this.currentQuestionIndex
                    ].state = "broken_exam";
                }
            } else if ($(tmpChild).is("[data-component]")) {
                let componentKind = $(tmpChild).data("component");
                this.renderedQuestionArray[this.currentQuestionIndex] = {
                    question: window.component_factory[componentKind](opts),
                };
            }
        } else if (opts.state === "broken_exam") {
            return;
        }

        currentQuestion = this.renderedQuestionArray[this.currentQuestionIndex]
            .question;
        if (opts.state === "forreview") {
            await currentQuestion.checkCurrentAnswer();
            currentQuestion.renderFeedback();
            currentQuestion.disableInteraction();
        }

        if (!this.visited.includes(this.currentQuestionIndex)) {
            this.visited.push(this.currentQuestionIndex);
            if (this.visited.length === this.renderedQuestionArray.length) {
                $(this.finishButton).show();
            }
        }

        if (currentQuestion.containerDiv) {
            $(this.switchDiv).replaceWith(currentQuestion.containerDiv);
            this.switchDiv = currentQuestion.containerDiv;
        }

        // If the timed component has listeners, those might need to be reinitialized
        // This flag will only be set in the elements that need it--it will be undefined in the others and thus evaluate to false
        if (currentQuestion.needsReinitialization) {
            currentQuestion.reinitializeListeners(this.taken);
        }
    }

    /*=================================
    === Timer and control Functions ===
    =================================*/
    handlePrevAssessment() {
        $(this.startBtn).hide();
        $(this.pauseBtn).attr("disabled", true);
        $(this.finishButton).attr("disabled", true);
        this.running = 0;
        this.done = 1;
        // showFeedback sand showResults should both be true before we show the
        // questions and their state of correctness.
        if (this.showResults && this.showFeedback) {
            $(this.timedDiv).show();
            this.restoreAnsweredQuestions(); // do not log these results
        } else {
            $(this.pauseBtn).hide();
            $(this.timerContainer).hide();
        }
    }
    startAssessment() {
        if (!this.taken) {
            $("#relations-next").hide(); // hide the next page button for now
            $("#relations-prev").hide(); // hide the previous button for now
            $(this.startBtn).hide();
            $(this.pauseBtn).attr("disabled", false);
            if (this.running === 0 && this.paused === 0) {
                this.running = 1;
                $(this.timedDiv).show();
                this.increment();
                this.logBookEvent({
                    event: "timedExam",
                    act: "start",
                    div_id: this.divid,
                });
                var timeStamp = new Date();
                var storageObj = {
                    answer: [0, 0, this.renderedQuestionArray.length, 0],
                    timestamp: timeStamp,
                };
                localStorage.setItem(
                    this.localStorageKey(),
                    JSON.stringify(storageObj)
                );
            }
            $(window).on("beforeunload", function (event) {
                // this actual value gets ignored by newer browsers
                alert("foo");
                event.preventDefault();
                event.returnValue =
                    "Are you sure you want to leave?  Your work will be lost! And you will need your instructor to reset the exam!";
                return "Are you sure you want to leave?  Your work will be lost!";
            });
        } else {
            this.handlePrevAssessment();
        }
    }
    pauseAssessment() {
        if (this.done === 0) {
            if (this.running === 1) {
                this.logBookEvent({
                    event: "timedExam",
                    act: "pause",
                    div_id: this.divid,
                });
                this.running = 0;
                this.paused = 1;
                this.pauseBtn.innerHTML = "Resume";
                $(this.timedDiv).hide();
            } else {
                this.logBookEvent({
                    event: "timedExam",
                    act: "resume",
                    div_id: this.divid,
                });
                this.running = 1;
                this.paused = 0;
                this.increment();
                this.pauseBtn.innerHTML = "Pause";
                $(this.timedDiv).show();
            }
        }
    }

    showTime() {
        if (this.showTimer) {
            var mins = Math.floor(this.timeLimit / 60);
            var secs = Math.floor(this.timeLimit) % 60;
            var minsString = mins;
            var secsString = secs;
            if (mins < 10) {
                minsString = "0" + mins;
            }
            if (secs < 10) {
                secsString = "0" + secs;
            }
            var beginning = "Time Remaining    ";
            if (!this.limitedTime) {
                beginning = "Time Taken    ";
            }
            var timeString = beginning + minsString + ":" + secsString;
            if (this.done || this.taken) {
                var minutes = Math.floor(this.timeTaken / 60);
                var seconds = Math.floor(this.timeTaken % 60);
                if (minutes < 10) {
                    minutes = "0" + minutes;
                }
                if (seconds < 10) {
                    seconds = "0" + seconds;
                }
                timeString = "Time taken: " + minutes + ":" + seconds;
            }
            this.timerContainer.innerHTML = timeString;
            var timeTips = document.getElementsByClassName("timeTip");
            for (var i = 0; i <= timeTips.length - 1; i++) {
                timeTips[i].title = timeString;
            }
        } else {
            $(this.timerContainer).hide();
        }
    }

    increment() {
        // if running (not paused) and not taken
        if (this.running === 1 && !this.taken) {
            setTimeout(
                function () {
                    if (this.limitedTime) {
                        // If there's a time limit, count down to 0
                        this.timeLimit--;
                    } else {
                        this.timeLimit++; // Else count up to keep track of how long it took to complete
                    }
                    localStorage.setItem(
                        eBookConfig.email + ":" + this.divid + "-time",
                        this.timeLimit
                    );
                    this.showTime();
                    if (this.timeLimit > 0) {
                        this.increment();
                        // ran out of time
                    } else {
                        $(this.startBtn).attr({
                            disabled: "true",
                        });
                        $(this.finishButton).attr({
                            disabled: "true",
                        });
                        this.running = 0;
                        this.done = 1;
                        if (!this.taken) {
                            this.taken = true;
                            // embed the message in the page -- an alert actually prevents
                            // the answers from being submitted and if a student closes their
                            // laptop then the answers will not be submitted ever!  Even when they
                            // reopen the laptop their session cookie is likely invalid.
                            let mess = document.createElement("h1");
                            mess.innerHTML =
                                "Sorry but you ran out of time. Your answers are being submitted";
                            this.controlDiv.appendChild(mess);
                            this.finishAssessment();
                        }
                    }
                }.bind(this),
                1000
            );
        }
    }

    styleExamElements() {
        // Checks if this exam has been taken before
        $(this.timerContainer).css({
            width: "50%",
            margin: "0 auto",
            "background-color": "#DFF0D8",
            "text-align": "center",
            border: "2px solid #DFF0D8",
            "border-radius": "25px",
        });
        $(this.scoreDiv).css({
            width: "50%",
            margin: "0 auto",
            "background-color": "#DFF0D8",
            "text-align": "center",
            border: "2px solid #DFF0D8",
            "border-radius": "25px",
        });
        $(".tooltipTime").css({
            margin: "0",
            padding: "0",
            "background-color": "black",
            color: "white",
        });
    }

    async finishAssessment() {
        $("#relations-next").show(); // show the next page button for now
        $("#relations-prev").show(); // show the previous button for now
        if (!this.showFeedback) {
            // bje - changed from showResults
            $(this.timedDiv).hide();
            $(this.pauseBtn).hide();
            $(this.timerContainer).hide();
        }
        this.findTimeTaken();
        this.running = 0;
        this.done = 1;
        this.taken = 1;
        await this.finalizeProblems();
        this.checkScore();
        this.displayScore();
        this.storeScore();
        this.logScore();
        $(this.pauseBtn).attr("disabled", true);
        this.finishButton.disabled = true;
        $(window).off("beforeunload");
        //
        let assignment_id = this.divid;
        setTimeout(function () {
            jQuery.ajax({
                url: eBookConfig.app + "/assignments/student_autograde",
                type: "POST",
                dataType: "JSON",
                data: {
                    assignment_id: assignment_id,
                    is_timed: true,
                },
                success: function (retdata) {
                    if (retdata.success == false) {
                        console.log(retdata.message);
                    } else {
                        console.log("Autograder completed");
                    }
                },
            });
        }, 2000);
    }

    // finalizeProblems
    // ----------------
    async finalizeProblems() {
        // Because we have submitted each question as we navigate we only need to
        // send the final version of the question the student is on when they press the
        // finish exam button.

        var currentQuestion = this.renderedQuestionArray[
            this.currentQuestionIndex
        ].question;
        await currentQuestion.checkCurrentAnswer();
        currentQuestion.logCurrentAnswer();
        currentQuestion.renderFeedback();
        currentQuestion.disableInteraction();

        for (var i = 0; i < this.renderedQuestionArray.length; i++) {
            let currentQuestion = this.renderedQuestionArray[i];
            // set the state to forreview so we know that feedback may be appropriate
            if (currentQuestion.state !== "broken_exam") {
                currentQuestion.state = "forreview";
                currentQuestion.question.disableInteraction();
            }
        }

        if (!this.showFeedback) {
            this.hideTimedFeedback();
        }
    }

    // restoreAnswerdQuestions
    // -----------------------
    restoreAnsweredQuestions() {
        for (var i = 0; i < this.renderedQuestionArray.length; i++) {
            var currentQuestion = this.renderedQuestionArray[i];
            if (currentQuestion.state === "prepared") {
                currentQuestion.state = "forreview";
            }
        }
    }

    // hideTimedFeedback
    // -----------------
    hideTimedFeedback() {
        for (var i = 0; i < this.renderedQuestionArray.length; i++) {
            var currentQuestion = this.renderedQuestionArray[i].question;
            currentQuestion.hideFeedback();
        }
    }

    // checkScore
    // ----------
    // This is a simple all or nothing score of one point per question for
    // that includes our best guess if a question was skipped.
    checkScore() {
        this.correctStr = "";
        this.skippedStr = "";
        this.incorrectStr = "";
        // Gets the score of each problem
        for (var i = 0; i < this.renderedQuestionArray.length; i++) {
            var correct = this.renderedQuestionArray[
                i
            ].question.checkCorrectTimed();
            if (correct == "T") {
                this.score++;
                this.correctStr = this.correctStr + (i + 1) + ", ";
            } else if (correct == "F") {
                this.incorrect++;
                this.incorrectStr = this.incorrectStr + (i + 1) + ", ";
            } else if (correct === null || correct === "I") {
                this.skipped++;
                this.skippedStr = this.skippedStr + (i + 1) + ", ";
            } else {
                // ignored question; just do nothing
            }
        }
        // remove extra comma and space at end if any
        if (this.correctStr.length > 0)
            this.correctStr = this.correctStr.substring(
                0,
                this.correctStr.length - 2
            );
        else this.correctStr = "None";
        if (this.skippedStr.length > 0)
            this.skippedStr = this.skippedStr.substring(
                0,
                this.skippedStr.length - 2
            );
        else this.skippedStr = "None";
        if (this.incorrectStr.length > 0)
            this.incorrectStr = this.incorrectStr.substring(
                0,
                this.incorrectStr.length - 2
            );
        else this.incorrectStr = "None";
    }
    findTimeTaken() {
        if (this.limitedTime) {
            this.timeTaken = this.startingTime - this.timeLimit;
        } else {
            this.timeTaken = this.timeLimit;
        }
    }
    storeScore() {
        var storage_arr = [];
        storage_arr.push(
            this.score,
            this.correctStr,
            this.incorrect,
            this.incorrectStr,
            this.skipped,
            this.skippedStr,
            this.timeTaken
        );
        var timeStamp = new Date();
        var storageObj = JSON.stringify({
            answer: storage_arr,
            timestamp: timeStamp,
        });
        localStorage.setItem(this.localStorageKey(), storageObj);
    }
    logScore() {
        this.logBookEvent({
            event: "timedExam",
            act: "finish",
            div_id: this.divid,
            correct: this.score,
            incorrect: this.incorrect,
            skipped: this.skipped,
            time: this.timeTaken,
        });
    }
    shouldUseServer(data) {
        // We override the RunestoneBase version because there is no "correct" attribute, and there are 2 possible localStorage schemas
        // --we also want to default to local storage because it contains more information specifically which questions are correct, incorrect, and skipped.
        var storageDate;
        if (localStorage.length === 0) return true;
        var storageObj = localStorage.getItem(this.localStorageKey());
        if (storageObj === null) return true;
        try {
            var storedData = JSON.parse(storageObj).answer;
            if (storedData.length == 4) {
                if (
                    data.correct == storedData[0] &&
                    data.incorrect == storedData[1] &&
                    data.skipped == storedData[2] &&
                    data.timeTaken == storedData[3]
                )
                    return true;
            } else if (storedData.length == 7) {
                if (
                    data.correct == storedData[0] &&
                    data.incorrect == storedData[2] &&
                    data.skipped == storedData[4] &&
                    data.timeTaken == storedData[6]
                ) {
                    return false; // In this case, because local storage has more info, we want to use that if it's consistent
                }
            }
            storageDate = new Date(JSON.parse(storageObj[1]).timestamp);
        } catch (err) {
            // error while parsing; likely due to bad value stored in storage
            console.log(err.message);
            localStorage.removeItem(this.localStorageKey());
            return true;
        }
        var serverDate = new Date(data.timestamp);
        if (serverDate < storageDate) {
            this.logScore();
            return false;
        }
        return true;
    }

    checkLocalStorage() {
        var len = localStorage.length;
        if (len > 0) {
            if (localStorage.getItem(this.localStorageKey()) !== null) {
                this.taken = 1;
                this.restoreAnswers("");
            } else {
                this.taken = 0;
            }
        } else {
            this.taken = 0;
        }
    }
    async restoreAnswers(data) {
        this.taken = 1;
        var tmpArr;
        if (data === "") {
            try {
                tmpArr = JSON.parse(
                    localStorage.getItem(this.localStorageKey())
                ).answer;
            } catch (err) {
                // error while parsing; likely due to bad value stored in storage
                console.log(err.message);
                localStorage.removeItem(this.localStorageKey());
                this.taken = 0;
                return;
            }
        } else {
            // Parse results from the database
            tmpArr = [
                parseInt(data.correct),
                parseInt(data.incorrect),
                parseInt(data.skipped),
                parseInt(data.timeTaken),
                data.reset,
            ];
            this.setLocalStorage(tmpArr);
        }
        if (tmpArr.length == 1) {
            // Exam was previously reset
            this.reset = true;
            this.taken = 0;
            return;
        }
        if (tmpArr.length == 4) {
            // Accidental Reload OR Database Entry
            this.score = tmpArr[0];
            this.incorrect = tmpArr[1];
            this.skipped = tmpArr[2];
            this.timeTaken = tmpArr[3];
        } else if (tmpArr.length == 7) {
            // Loaded Completed Exam
            this.score = tmpArr[0];
            this.correctStr = tmpArr[1];
            this.incorrect = tmpArr[2];
            this.incorrectStr = tmpArr[3];
            this.skipped = tmpArr[4];
            this.skippedStr = tmpArr[5];
            this.timeTaken = tmpArr[6];
        } else {
            // Set localStorage in case of "accidental" reload
            this.score = 0;
            this.incorrect = 0;
            this.skipped = this.renderedQuestionArray.length;
            this.timeTaken = 0;
        }
        if (this.taken) {
            if (this.skipped === this.renderedQuestionArray.length) {
                this.showFeedback = false;
            }
            this.handlePrevAssessment();
        }
        await this.renderTimedQuestion();
        this.displayScore();
        this.showTime();
    }
    setLocalStorage(parsedData) {
        var timeStamp = new Date();
        var storageObj = {
            answer: parsedData,
            timestamp: timeStamp,
        };
        localStorage.setItem(
            this.localStorageKey(),
            JSON.stringify(storageObj)
        );
    }
    displayScore() {
        if (this.showResults) {
            var scoreString = "";
            var numQuestions;
            var percentCorrect;
            // if we have some information
            if (
                this.correctStr.length > 0 ||
                this.incorrectStr.length > 0 ||
                this.skippedStr.length > 0
            ) {
                scoreString = `Num Correct: ${this.score}. Questions: ${this.correctStr}<br>Num Wrong: ${this.incorrect}. Questions: ${this.incorrectStr}<br>Num Skipped: ${this.skipped}. Questions: ${this.skippedStr}<br>`;
                numQuestions = this.score + this.incorrect + this.skipped;
                percentCorrect = (this.score / numQuestions) * 100;
                scoreString += "Percent Correct: " + percentCorrect + "%";
                $(this.scoreDiv).html(scoreString);
                this.scoreDiv.style.display = "block";
            } else {
                scoreString = `Num Correct: ${this.score}<br>Num Wrong: ${this.incorrect}<br>Num Skipped: ${this.skipped}<br>`;
                numQuestions = this.score + this.incorrect + this.skipped;
                percentCorrect = (this.score / numQuestions) * 100;
                scoreString += "Percent Correct: " + percentCorrect + "%";
                $(this.scoreDiv).html(scoreString);
                this.scoreDiv.style.display = "block";
            }
            this.highlightNumberedList();
        } else {
            $(this.scoreDiv).html(
                "Thank you for taking the exam.  Your answers have been recorded."
            );
            this.scoreDiv.style.display = "block";
        }
    }
    highlightNumberedList() {
        var correctCount = this.correctStr;
        var incorrectCount = this.incorrectStr;
        var skippedCount = this.skippedStr;
        correctCount = correctCount.replace(/ /g, "").split(",");
        incorrectCount = incorrectCount.replace(/ /g, "").split(",");
        skippedCount = skippedCount.replace(/ /g, "").split(",");
        $(function () {
            var numberedBtns = $("ul#pageNums > ul > li");
            if (numberedBtns.hasClass("answered")) {
                numberedBtns.removeClass("answered");
            }
            for (var i = 0; i < correctCount.length; i++) {
                var test = parseInt(correctCount[i]) - 1;
                numberedBtns
                    .eq(parseInt(correctCount[i]) - 1)
                    .addClass("correctCount");
            }
            for (var j = 0; j < incorrectCount.length; j++) {
                numberedBtns
                    .eq(parseInt(incorrectCount[j]) - 1)
                    .addClass("incorrectCount");
            }
            for (var k = 0; k < skippedCount.length; k++) {
                numberedBtns
                    .eq(parseInt(skippedCount[k]) - 1)
                    .addClass("skippedCount");
            }
        });
    }
}

/*=======================================================
=== Function that calls the constructors on page load ===
=======================================================*/
$(document).bind("runestone:login-complete", function () {
    $("[data-component=timedAssessment]").each(function (index) {
        TimedList[this.id] = new Timed({
            orig: this,
            useRunestoneServices: eBookConfig.useRunestoneServices,
        });
    });
});


/***/ }),

/***/ "./runestone/video/css/video.css":
/*!***************************************!*\
  !*** ./runestone/video/css/video.css ***!
  \***************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var api = __webpack_require__(/*! ../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
            var content = __webpack_require__(/*! !../../../node_modules/css-loader/dist/cjs.js!./video.css */ "./node_modules/css-loader/dist/cjs.js!./runestone/video/css/video.css");

            content = content.__esModule ? content.default : content;

            if (typeof content === 'string') {
              content = [[module.i, content, '']];
            }

var options = {};

options.insert = "head";
options.singleton = false;

var update = api(content, options);



module.exports = content.locals || {};

/***/ }),

/***/ "./runestone/video/js/runestonevideo.js":
/*!**********************************************!*\
  !*** ./runestone/video/js/runestonevideo.js ***!
  \**********************************************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _common_js_runestonebase__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/js/runestonebase */ "./runestone/common/js/runestonebase.js");
/* harmony import */ var _css_video_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../css/video.css */ "./runestone/video/css/video.css");
/* harmony import */ var _css_video_css__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_video_css__WEBPACK_IMPORTED_MODULE_1__);





var vidList = [];
class RunestoneVideo extends _common_js_runestonebase__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(opts) {
        super(opts);
        this.divid = opts.orig.id;
        this.container = $(`#${this.divid}`);
        this.caption = "YouTube";
        if (document.getElementById("youtubescript") == null) {
            let script = document.createElement("script");
            script.id = "youtubescript";
            script.src = "https://www.youtube.com/player_api";
            document.body.appendChild(script);
        }
        this.containerDiv = this.container[0].parentElement;
        this.addCaption("runestone");
    }
}

window.onPlayerStateChange = function (event) {
    let rb = new _common_js_runestonebase__WEBPACK_IMPORTED_MODULE_0__["default"]();
    let videoTime = event.target.getCurrentTime();
    let data = {
        event: "video",
        div_id: event.target.getIframe().id,
    };
    if (event.data == YT.PlayerState.PLAYING) {
        console.log("playing " + event.target.getIframe().id);
        data.act = "play:" + videoTime;
    } else if (event.data == YT.PlayerState.ENDED) {
        console.log("ended " + event.target.getIframe().id);
        data.act = "complete";
    } else if (event.data == YT.PlayerState.PAUSED) {
        console.log("paused at " + videoTime);
        data.act = "pause:" + videoTime;
    }
    rb.logBookEvent(data);
};

//Callback function to load youtube videos once IFrame Player loads
window.onYouTubeIframeAPIReady = function () {
    let videolist = $(".youtube-video");
    videolist.each(function (i, video) {
        let playerVars = {};
        playerVars["start"] = $(video).data("video-start");
        if ($(video).data("video-end") != -1)
            playerVars["end"] = $(video).data("video-end");
        let player = new YT.Player($(video).data("video-divid"), {
            height: $(video).data("video-height"),
            width: $(video).data("video-width"),
            videoId: $(video).data("video-videoid"),
            playerVars: playerVars,
            events: {
                onStateChange: window.onPlayerStateChange,
            },
        });
    });
};

//Need to make sure the YouTube IFrame Player API is not loaded until after
// all YouTube videos are in the DOM. Add a script tag with it after document is loaded
$(document).ready(function () {
    let script = document.createElement("script");
    script.src = "https://www.youtube.com/player_api";
    document.body.appendChild(script);
});

$(document).bind("runestone:login-complete", function () {
    $("[data-component=youtube]").each(function (index) {
        // MC
        var opts = {
            orig: this,
            useRunestoneServices: eBookConfig.useRunestoneServices,
        };
        vidList[this.id] = new RunestoneVideo(opts);
    });
});

if (typeof window.component_factory === "undefined") {
    window.component_factory = {};
}
window.component_factory.youtube = function (opts) {
    return new RunestoneVideo(opts);
};

window.component_factory.vimeo = function (opts) {
    return new RunestoneVideo(opts);
};


/***/ }),

/***/ "./runestone/webwork/js/webwork.js":
/*!*****************************************!*\
  !*** ./runestone/webwork/js/webwork.js ***!
  \*****************************************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _common_js_runestonebase__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/js/runestonebase */ "./runestone/common/js/runestonebase.js");


class WebWork extends _common_js_runestonebase__WEBPACK_IMPORTED_MODULE_0__["default"] {
    constructor(opts) {
        super(opts);
    }
}

let rb = new WebWork();

function logWebWork(e, data) {
    var correct = false;
    let correctCount = 0;
    let qCount = 0;
    let actString = "check:";
    for (let k of Object.keys(data.rh_result.answers)) {
        qCount += 1;
        if (data.rh_result.answers[k].score == 1) {
            correctCount += 1;
        }
        actString += `actual:${data.rh_result.answers[k].student_ans}:expected:${data.rh_result.answers[k].correct_value}:`;
    }
    let pct = correctCount / qCount;
    actString += `correct:${correctCount}:count:${qCount}:pct:${pct}`;
    if (pct == 1.0) {
        correct = true;
    }
    rb.logBookEvent({
        event: "webwork",
        div_id: data.ww_id,
        act: actString,
        correct: correct,
    });
}

function logShowCorrect(e, data) {
    rb.logBookEvent({
        event: "webwork",
        div_id: data.ww_id,
        act: "show",
    });
}

if (typeof window.component_factory === "undefined") {
    window.component_factory = {};
}

window.component_factory.webwork = function (opts) {
    return new WebWork();
};

$(document).ready(function () {
    $("body").on("runestone_ww_check", logWebWork);
    $("body").on("runestone_show_correct", logShowCorrect);
});


/***/ }),

/***/ 0:
/*!*************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\
  !*** multi ./runestone/shortanswer/js/shortanswer.js ./runestone/activecode/js/acfactory.js ./runestone/mchoice/js/mchoice.js ./runestone/fitb/js/fitb.js ./runestone/clickableArea/js/clickable.js ./runestone/dragndrop/js/dragndrop.js ./runestone/timed/js/timed.js ./runestone/parsons/js/parsons.js ./runestone/poll/js/poll.js ./runestone/common/js/user-highlights.js ./runestone/spreadsheet/js/spreadsheet.js ./runestone/tabbedStuff/js/tabbedstuff.js ./runestone/reveal/js/reveal.js ./runestone/datafile/js/datafile.js ./runestone/showeval/js/showEval.js ./runestone/video/js/runestonevideo.js ./runestone/lp/js/lp.js ./runestone/codelens/js/codelens.js ./runestone/webwork/js/webwork.js ./runestone/selectquestion/js/selectone.js ***!
  \*************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

__webpack_require__(/*! ./runestone/shortanswer/js/shortanswer.js */"./runestone/shortanswer/js/shortanswer.js");
__webpack_require__(/*! ./runestone/activecode/js/acfactory.js */"./runestone/activecode/js/acfactory.js");
__webpack_require__(/*! ./runestone/mchoice/js/mchoice.js */"./runestone/mchoice/js/mchoice.js");
__webpack_require__(/*! ./runestone/fitb/js/fitb.js */"./runestone/fitb/js/fitb.js");
__webpack_require__(/*! ./runestone/clickableArea/js/clickable.js */"./runestone/clickableArea/js/clickable.js");
__webpack_require__(/*! ./runestone/dragndrop/js/dragndrop.js */"./runestone/dragndrop/js/dragndrop.js");
__webpack_require__(/*! ./runestone/timed/js/timed.js */"./runestone/timed/js/timed.js");
__webpack_require__(/*! ./runestone/parsons/js/parsons.js */"./runestone/parsons/js/parsons.js");
__webpack_require__(/*! ./runestone/poll/js/poll.js */"./runestone/poll/js/poll.js");
__webpack_require__(/*! ./runestone/common/js/user-highlights.js */"./runestone/common/js/user-highlights.js");
__webpack_require__(/*! ./runestone/spreadsheet/js/spreadsheet.js */"./runestone/spreadsheet/js/spreadsheet.js");
__webpack_require__(/*! ./runestone/tabbedStuff/js/tabbedstuff.js */"./runestone/tabbedStuff/js/tabbedstuff.js");
__webpack_require__(/*! ./runestone/reveal/js/reveal.js */"./runestone/reveal/js/reveal.js");
__webpack_require__(/*! ./runestone/datafile/js/datafile.js */"./runestone/datafile/js/datafile.js");
__webpack_require__(/*! ./runestone/showeval/js/showEval.js */"./runestone/showeval/js/showEval.js");
__webpack_require__(/*! ./runestone/video/js/runestonevideo.js */"./runestone/video/js/runestonevideo.js");
__webpack_require__(/*! ./runestone/lp/js/lp.js */"./runestone/lp/js/lp.js");
__webpack_require__(/*! ./runestone/codelens/js/codelens.js */"./runestone/codelens/js/codelens.js");
__webpack_require__(/*! ./runestone/webwork/js/webwork.js */"./runestone/webwork/js/webwork.js");
module.exports = __webpack_require__(/*! ./runestone/selectquestion/js/selectone.js */"./runestone/selectquestion/js/selectone.js");


/***/ })

/******/ });
});
//# sourceMappingURL=data:application/json;charset=utf-8;base64,[encoded data, 5156288 characters]