123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- // Utilities for basic tasks in javascript.
- $exists: function $exists(el) {
- return typeof el !== "undefined";
- };
- $fcd: function $fcd(obj, fn) {
- return function () { fn.apply(obj, arguments); }
- };
- $fcb: function $fcb(obj, fn, args) {
- return function () {
- args = args.concat(arguments);
- fn.apply(obj, args);
- }
- };
- $getEl = function $getEl(id) {
- return document.getElementById(id);
- };
- $addCssClass = function $addCssClass(el, cl) {
- //TODO: a spurious space sometimes appears at the beginning of className
- //avoid using classList for cross-browser support
- arr = el.className.split(" ");
- if (arr.indexOf(cl) === -1) {
- el.className += " " + cl;
- }
- };
- $removeCssClass = function $removeCssClass(el, cl) {
- //avoid using classList for cross-browser support
- arr = el.className.split(" ");
- index = arr.indexOf(cl);
- if (arr.indexOf(cl) !== -1) {
- arr.splice(index, 1);
- el.className = arr.join(" ");
- }
- };
- $addHandler = function $addHandler(el, event, del) {
- if (el.addEventListener) {
- el.addEventListener(event, del);
- }
- else if (el.attachEvent) {
- el.attachEvent(event, del);
- }
- else {
- throw "Element has no event listeners."
- }
- };
- $removeHandler = function $removeHandler(el, event, del) {
- if (el.removeEventListener) {
- el.removeEventListener(event, del);
- }
- else if (el.detachEvent) {
- el.detachEvent(event, del);
- }
- else {
- throw "Element has no event listeners."
- }
- };
- $setInheritance = function $setInheritance(base, child) {
- for (var key in base.prototype) {
- if (!child.prototype.key) {
- child.prototype.key = base.prototype.key
- }
- }
- };
- $getVerticalScroll = function $getVerticalScroll() {
- if (typeof (window.pageYOffset == 'number')) {
- return window.pageYOffset;
- }
- else if (document.body && document.body.scrollTop) { //note: sometimes when it just doesn't work, scrollTop is just permanently 0, so accept 0 as "false".
- return document.body.scrollTop;
- }
- else if (document.documentElement && document.documentElement.scrollTop) {
- return document.documentElement.scrollTop;
- }
- else {
- return 0;
- }
- };
- $setVerticalScroll = function $setVerticalScroll(val) {
- window.scrollTo(0, val);
- };
- $enableScrollBars = function $enableScrollBars() {
- if (document.documentElement) {
- document.documentElement.style.overflow = "auto";
- }
- if (document.body) {
- document.body.scroll = "yes";
- }
- };
- $disableScrollBars = function $disableScrollBars() {
- if (document.documentElement) {
- document.documentElement.style.overflow = "hidden";
- }
- if (document.body) {
- document.body.scroll = "no";
- }
- };
- $newDiv = function $newDiv(id, cssClasses) {
- var div = document.createElement("div");
- if ($exists(id)) {
- div.setAttribute("id", id);
- }
- if (Array.isArray(cssClasses)) {
- cssClasses.forEach(function (item) {
- $addCssClass(div, item);
- });
- }
- return div;
- };
- $arrayEquality = function $arrayEquality(arr1, arr2) {
- if (arr1.length !== arr2.length) { return false; }
- for (var i = 0; i < arr1.length; i++) {
- if (arr1[i] !== arr2[i]) { return false; }
- }
- return true;
- };
- $setCookie: function $setCookie(obj, expiration, path) {
- //adds the kvps from the object to the current cookie
- var kv;
- var cookie = "";
- for (var key in obj) { //set fresh values
- kv = key + "=" + encodeURIComponent(obj[key]) + ";";
- cookie += kv;
- }
- cookie += "expires=" + expiration + ";"; //need to handle these separately bc we don't want to encode
- cookie += "path=" + path + ";";
- document.cookie = cookie;
- };
- $readCookie: function $readCookie() {
- if (!document.cookie) { return {}; }
- var obj = {};
- var cookiePairs = document.cookie.split(";");
- var kv;
- for (var i = 0; i < cookiePairs.length; i++) {
- kv = cookiePairs[i].split("=");
- obj[kv[0]] = decodeURIComponent(kv[1]);
- }
- return obj;
- };
- $leftPad: function $leftPad(str) {
- str = str + "";
- return ("0000" + str).substring(str.length);
- }
- function Timer() {
- //handles tasks delayed by a fixed amount of time
- this._queue = [];
- this.__updateDel = $fcd(this, this._update);
- };
- Timer.prototype = {
- _queue: null,
- __timeout: 0,
- __timeoutId: null, //also doubles as a flag for whether this is currently running
- __updateDel: null,
- set: function set(callback, timeout) {
- this._queue.push({
- 'callback': callback,
- 'timeout': timeout
- });
- if (timeout < this.__timeout || this.__timeoutId === null) {
- this.__timeout = timeout;
- if (this.__timeoutId) {
- clearTimeout(this.__timeoutId);
- }
- this.__timeoutId = setTimeout(this.__updateDel, timeout);
- };
- },
- isQueued: function isQueued(callback) {
- var arr = this._queue.filter(el => el.callback === callback);
- return arr.length > 0;
- },
- clear: function clear() {
- this._queue = [];
- this.__timeout = 0;
- clearTimeout(this.__timeoutId);
- this.__timeoutId = null;
- },
- _update: function __update() {
- this.__runQueue();
- if (this._queue.length > 0) {
- timeout = this.__findTimeout();
- this.__timeout = timeout;
- this.__timeoutId = setTimeout(this.__updateDel, timeout);
- }
- else {
- this.__timeout = 0;
- this.__timeoutId = null;
- }
- },
- __runQueue: function __runQueue() {
- for (var i = 0; i < this._queue.length; i++) { //use boring old for fn bc we need "this" in scope
- var el = this._queue[i];
- el.timeout = el.timeout - this.__timeout;
- if (el.timeout <= 0) {
- el.callback();
- this._queue.splice(i, 1);
- }
- };
- },
- __findTimeout: function __findTimeout() {
- var timeouts = this._queue.map(el => el.timeout);
- return Math.min.apply(null, timeouts);
- },
- };
- function Sequencer() {
- //handles tasks that must be done in a certain order
- this._queue = [];
- this.__updateDel = $fcd(this, this._update);
- this.__listeners = [];
- };
- Sequencer.prototype = {
- _queue: null,
- __timeoutId: null, //also doubles as a flag for whether this is currently running
- __updateDel: null,
- __listeners: null,
- add: function add(callback, delay) {
- this._queue.push({
- 'callback': callback,
- 'delay': delay //time to wait after previous step is done before executing this one
- });
- if (this.__timeoutId === null) {
- this.__timeoutId = setTimeout(this.__updateDel, delay);
- };
- },
-
- declareBlock: function declareBlock(blockName) {
- this._queue.push(blockName);
- },
- removeBlock: function removeBlock(blockName) {
- // Removes the block of queued callbacks from the first block declaration of the given name, up to the next block declaration.
- // If no parameters are passed, removes the first block that isn't currently underway.
- // Returns true if a block was removed; false if no such block is queued.
- var start = -1;
- var end = -1;
- var i = 0;
- while (i < this._queue.length && start === -1) {
- if (blockName) {
- if (this._queue[i] === blockName) { start = i; }
- }
- else {
- if (this.__isBlockLabel(this._queue[i])) { start = i; }
- }
- i++;
- }
- while (i < this._queue.length && end === -1) {
- if (this.__isBlockLabel(this._queue[i])) { end = i; }
- i++;
- }
- if (start !== -1) {
- //block found
- if (end !== -1) {
- //there is a subsequent block declaration
- this._queue.splice(start, (end - start));
- }
- else {
- //this block takes up the whole rest of the queue
- this._queue = this._queue.slice(0, start);
- }
- return true;
- }
- else {
- return false;
- }
- },
- removeBlocksOtherThan: function removeBlocksOtherThan(blockName) {
- // Removes all queued blocks of callbacks other than the first one with the given name.
- // Note that any singletons at the front of the queue will still be processed.
- // Returns 1 if the block was the first block to appear; -1 if the block was not the first block to appear; 0 if nothing was removed
- debugger;
- var preserveStart = -1;
- var preserveEnd = -1;
- var cutStart = -1;
- var i = 0;
-
- while (i < this._queue.length && (preserveStart === -1 || cutStart === -1 || preserveEnd === -1)) {
- if (preserveStart === -1 && this._queue[i] === blockName) { preserveStart = i; }
- else if (this.__isBlockLabel(this._queue[i])) {
- if (preserveStart !== -1 && preserveEnd === -1) {
- preserveEnd = i;
- }
- if (cutStart === -1) {
- cutStart = i;
- }
- }
- i++;
- }
- // there are two distinct cases:
- // 1) xxx ______ xxxxx _______ <-- unwanted blocks exist between singletons and target block
- // 2) xxx xxxx _______________ <-- target block is right after singletons
- var somethingCut = 0;
- if (preserveEnd !== -1) { //either way, we can cut off the tail (if it exists)
- this._queue = this._queue.slice(0, preserveEnd);
- somethingCut = 1;
- } //if we're in case 2, we're done
- if (cutStart < preserveStart || preserveStart === -1) {
- this._queue.splice(cutStart, (preserveStart - cutStart));
- somethingCut = -1;
- }
- return somethingCut;
- },
- clear: function clear() {
- this._queue = [];
- clearTimeout(this.__timeoutId);
- this.__timeoutId = null;
- },
- addBlockListener: function addBlockListener(del) {
- //todo: theoretically this api should include something to remove the listener as well, but I have no need for that.
- this.__listeners.push(del);
- },
- hasQueuedItems: function hasQueuedItems() {
- return !!this.__nextQueueItem();
- },
- __raiseBlockEvent: function __raiseBlockEvent(blockName) {
- this.__listeners.forEach(function (el) {
- el.call(null, blockName);
- });
- },
- _update: function __update() {
- var timeout = 0;
-
- //handle next step
- while (timeout <= 0 && this._queue.length > 0) {
- if (this.__isBlockLabel(this._queue[0])) {
- this.__raiseBlockEvent(this._queue[0]);
- this._queue.splice(0, 1);
- //timeout = 0;
- continue;
- }
- else { //this is an actual item in the queue
- //execute step
- this._queue[0].callback();
- this._queue.splice(0, 1);
- //check next step's delay
- if (this._queue.length > 0) {
- var next = this.__nextQueueItem();
- if (next) {
- timeout = next.delay;
- } else {
- timeout = 0;
- }
- }
- }
- }
- if (timeout > 0 && this._queue.length > 0) {
- this.__timeoutId = setTimeout(this.__updateDel, timeout);
- }
- else {
- this.__timeoutId = null;
- }
- },
- __isBlockLabel: function __isBlockLabel(queueItem) {
- return typeof queueItem === "string";
- },
- __nextQueueItem: function __nextQueueItem() {
- for (var i = 0; i < this._queue.length; i++) {
- if (!this.__isBlockLabel(this._queue[i])) {
- return this._queue[i];
- }
- }
- return null;
- },
- };
|