Home Reference Source

scripts/screen/area_wrangler.js

//import * as nstructjs from './struct.js';
import {haveModal, _setModalAreaClass} from '../path-controller/util/simple_events.js';
import * as util from '../path-controller/util/util.js';

import '../path-controller/util/struct.js';

let ScreenClass = undefined;

import {ClassIdSymbol} from '../core/ui_base.js';


export function setScreenClass(cls) {
  ScreenClass = cls;
}

export function getAreaIntName(name) {
  let hash = 0;

  for (let i = 0; i < name.length; i++) {
    let c = name.charCodeAt(i);

    if (i%2 === 0) {
      hash += c<<8;
      hash *= 13;
      hash = hash & ((1<<15) - 1);
    } else {
      hash += c;
    }
  }

  return hash;
}

//XXX get rid of me
window.getAreaIntName = getAreaIntName;

//XXX get rid of me
export var AreaTypes = {
  TEST_CANVAS_EDITOR: 0
};

export function setAreaTypes(def) {
  for (let k in AreaTypes) {
    delete AreaTypes[k];
  }

  for (let k in def) {
    AreaTypes[k] = def[k];
  }
}

export let areaclasses = {};

/*hackish! store ref an active wrangler so simple_event's modal
* system can lock it!*/
let theWrangler = undefined;

export class AreaWrangler {
  constructor() {
    this.stacks = new Map();
    this.lasts = new Map();
    this.lastArea = undefined;
    this.stack = [];
    this.idgen = 0;
    this.locked = 0;
    this._last_screen_id = undefined;

    theWrangler = this;
  }

  /*Yeek this is particularly evil, it creates a context
  * that can be used by popups with the original context
  * area stack intact of the elements that spawned them.*/
  makeSafeContext(ctx) {
    let wrangler = this.copy();
    let this2 = this;

    return new Proxy(ctx, {
      get(target, key, rec) {
        wrangler.copyTo(contextWrangler);
        return target[key];
      }
    });
  }

  copyTo(ret) {
    for (let [key, stack1] of this.stacks) {
      ret.stack.set(key, util.list(stack1));
    }

    for (let [key, val] of this.lasts) {
      ret.lasts.set(key, val);
    }

    ret.stack = util.list(this.stack);
    ret.lastArea = this.lastArea;
  }

  copy(b) {
    let ret = new AreaWrangler();
    this.copyTo(ret);
    return ret;
  }

  _checkWrangler(ctx) {
    if (ctx === undefined) {
      return true;
    }

    if (this._last_screen_id === undefined) {
      this._last_screen_id = ctx.screen._id;
      return true;
    }

    if (ctx.screen._id !== this._last_screen_id) {
      this.reset();

      this._last_screen_id = ctx.screen._id;
      console.warn("contextWrangler detected a new screen; new file?");
      return false;
    }

    return true;
  }

  reset() {
    theWrangler = this;
    this.stacks = new Map();
    this.lasts = new Map();
    this.lastArea = undefined;
    this.stack = [];
    this.locked = 0;
    this._last_screen_id = undefined;

    return this;
  }

  static findInstance() {
    return theWrangler;
  }

  static lock() {
    return this.findInstance().lock();
  }

  static unlock() {
    return this.findInstance().unlock();
  }

  lock() {
    this.locked++;
    return this;
  }

  unlock() {
    this.locked = Math.max(this.locked - 1, 0);
    return this;
  }

  push(type, area, pushLastRef = true) {
    theWrangler = this;

    if (haveModal() || this.locked) {
      pushLastRef = false;
    }

    if (pushLastRef || !this.lasts.has(type[ClassIdSymbol])) {
      this.lasts.set(type[ClassIdSymbol], area);
      this.lastArea = area;
    }

    let stack = this.stacks.get(type[ClassIdSymbol]);
    if (stack === undefined) {
      stack = [];
      this.stacks.set(type[ClassIdSymbol], stack);
    }

    let last = this.lasts.get(type[ClassIdSymbol]);

    stack.push(last);
    stack.push(area);

    this.stack.push(area);
  }

  updateLastRef(type, area) {
    theWrangler = this;

    if ((this.locked || haveModal()) && this.lasts.has(type[ClassIdSymbol])) {
      return;
    }

    this.lasts.set(type[ClassIdSymbol], area);
    this.lastArea = area;
  }

  pop(type, area) {
    let stack = this.stacks.get(type[ClassIdSymbol]);

    if (stack === undefined) {
      console.warn("pop_ctx_area called in error");
      //throw new Error("pop_ctx_area called in error");
      return;
    }

    if (stack.length > 0) {
      stack.pop();
      let last = stack.pop();

      /* paranoia isConnected check to ensure stale elements don't
       * pollute the lasts stack */
      if (!this.locked && last && last.isConnected) {
        this.lasts.set(type[ClassIdSymbol], last);
      }
    } else {
      console.error("pop_ctx_area called in error");
    }

    if (this.stack.length > 0) {
      this.stack.pop();
    }
  }

  getLastArea(type) {
    //if (Math.random() > 0.9995) {
      //console.warn("getLastArea!", type, this.lasts.get(type[ClassIdSymbol]));
    //}

    if (type === undefined) {
      if (this.stack.length > 0) {
        return this.stack[this.stack.length - 1];
      } else {
        return this.lastArea;
      }
    } else {
      if (this.stacks.has(type[ClassIdSymbol])) {
        let stack = this.stacks.get(type[ClassIdSymbol]);

        if (stack.length > 0) {
          return stack[stack.length-1];
        }
      }

      return this.lasts.get(type[ClassIdSymbol]);
    }
  }
}
_setModalAreaClass(AreaWrangler);

export let contextWrangler = new AreaWrangler();