Программный интерфейс

Показывать:
/**
 * Содержит методы обработки событий __при запуске__ программы, __перед закрытием__,<br />
 * при обновлении файлов __ApplicationCache__, а так же, при переходе в __offline__ и __online__
 *
 *  События развиваются в такой последовательности:
 *
 *  1) выясняем, совместим ли браузер. В зависимости от параметров url и параметров по умолчанию,
 *   может произойти переход в ChromeStore или другие действия
 *
 *  2) анализируем AppCache, при необходимости обновляем скрипты и перезагружаем страницу
 *
 *   3) инициализируем $p.wsql и комбинируем параметры работы программы с параметрами url
 *
 *   4) если режим работы предполагает использование построителя, подключаем слушатель его событий.
 *   по событию построителя "ready", выполняем метод initMainLayout() объекта $p.iface.
 *   Метод initMainLayout() переопределяется во внешним, по отношению к ядру, модуле
 *
 * &copy; Evgeniy Malyarov http://www.oknosoft.ru 2014-2016
 *
 * @module common
 * @submodule events
 */

/**
 * ### Обработчики событий приложения
 *
 * Cм. так же, модули {{#crossLinkModule "events"}}{{/crossLinkModule}} и {{#crossLinkModule "events_browser"}}{{/crossLinkModule}}
 *
 * @class AppEvents
 * @static
 * @menuorder 30
 * @tooltip Движок событий
 */
function AppEvents() {

  this.__define({

    /**
     * ### Запускает процесс инициализаци параметров и метаданных
     *
     * @method run
     * @for AppEvents
     *
     */
    init: {
      value: function () {
        $p.__define("job_prm", {
          value: new JobPrm(),
          writable: false
        });
        $p.wsql.init_params();
      }
    },

    /**
     * ### Добавляет объекту методы генерации и обработки событий
     *
     * @method do_eventable
     * @for AppEvents
     * @param obj {Object} - объект, которому будут добавлены eventable свойства
     */
    do_eventable: {
      value: function (obj) {

        function attach(name, func) {
          name = String(name).toLowerCase();
          if (!this._evnts.data[name])
            this._evnts.data[name] = {};
          var eventId = $p.utils.generate_guid();
          this._evnts.data[name][eventId] = func;
          return eventId;
        }

        function detach(eventId) {

          if(!eventId){
            return detach_all.call(this);
          }

          for (var a in this._evnts.data) {
            var k = 0;
            for (var b in this._evnts.data[a]) {
              if (b == eventId) {
                this._evnts.data[a][b] = null;
                delete this._evnts.data[a][b];
              } else {
                k++;
              }
            }
            if (k == 0) {
              this._evnts.data[a] = null;
              delete this._evnts.data[a];
            }
          }
        }

         function detach_all() {
          for (var a in this._evnts.data) {
            for (var b in this._evnts.data[a]) {
              this._evnts.data[a][b] = null;
              delete this._evnts.data[a][b];
            }
            this._evnts.data[a] = null;
            delete this._evnts.data[a];
          }
        }

        function call(name, params) {
          name = String(name).toLowerCase();
          if (this._evnts.data[name] == null)
            return true;
          var r = true;
          for (var a in this._evnts.data[name]) {
            r = this._evnts.data[name][a].apply(this, params) && r;
          }
          return r;
        }

        function ontimer() {

          for(var name in this._evnts.evnts){
            var l = this._evnts.evnts[name].length;
            if(l){
              for(var i=0; i<l; i++){
                this.emit(name, this._evnts.evnts[name][i]);
              }
              this._evnts.evnts[name].length = 0;
            }
          }

          this._evnts.timer = 0;
        }

        obj.__define({

          _evnts: {
            value: {
              data: {},
              timer: 0,
              evnts: {}
            }
          },

          on: {
            value: attach
          },

          attachEvent: {
            value: attach
          },

          off: {
            value: detach
          },

          detachEvent: {
            value: detach
          },

          detachAllEvents: {
            value: detach_all
          },

          checkEvent: {
            value: function(name) {
              name = String(name).toLowerCase();
              return (this._evnts.data[name] != null);
            }
          },

          callEvent: {
            value: call
          },

          emit: {
            value: call
          },

          emit_async: {
            value: function callEvent(name, params){

              if(!this._evnts.evnts[name])
                this._evnts.evnts[name] = [];

              this._evnts.evnts[name].push(params);

              if(this._evnts.timer)
                clearTimeout(this._evnts.timer);

              this._evnts.timer = setTimeout(ontimer.bind(this), 4);
            }
          }

        });
      }
    }
  });

  // если мы внутри браузера и загружен dhtmlx, переносим в AppEvents свойства dhx4
  if(typeof window !== "undefined" && window.dhx4){
    for(var p in dhx4){
      this[p] = dhx4[p];
      delete dhx4[p];
    }
    window.dhx4 = this;

  }else if(typeof WorkerGlobalScope === "undefined"){

    // мы внутри Nodejs

    this.do_eventable(this);

  }

}

/**
 * ### Параметры работы программы
 * - Хранит глобальные настройки варианта компиляции (_Заказ дилера_, _Безбумажка_, _Демо_ и т.д.)
 * - Настройки извлекаются из файла "settings" при запуске приложения и дополняются параметрами url,
 * которые могут быть переданы как через search (?), так и через hash (#)
 * - см. так же, {{#crossLink "WSQL/get_user_param:method"}}{{/crossLink}} и {{#crossLink "WSQL/set_user_param:method"}}{{/crossLink}} - параметры, изменяемые пользователем
 *
 * @class JobPrm
 * @static
 * @menuorder 04
 * @tooltip Параметры приложения
 */
function JobPrm(){

  function base_url(){
    return $p.wsql.get_user_param("rest_path") || $p.job_prm.rest_path || "/a/zd/%1/odata/standard.odata/";
  }

  function parse_url(){

    function parse(url_prm){
      var prm = {}, tmp = [], pairs;

      if(url_prm.substr(0, 1) === "#" || url_prm.substr(0, 1) === "?")
        url_prm = url_prm.substr(1);

      if(url_prm.length > 2){

        pairs = decodeURI(url_prm).split('&');

        // берём параметры из url
        for (var i in pairs){   //разбиваем пару на ключ и значение, добавляем в их объект
          tmp = pairs[i].split('=');
          if(tmp[0] == "m"){
            try{
              prm[tmp[0]] = JSON.parse(tmp[1]);
            }catch(e){
              prm[tmp[0]] = {};
            }
          }else
            prm[tmp[0]] = tmp[1] || "";
        }
      }

      return prm;
    }

    return parse(location.search)._mixin(parse(location.hash));
  }

  this.__define({

    /**
     * Осуществляет синтаксический разбор параметров url
     * @method parse_url
     * @return {Object}
     */
    parse_url: {
      value: parse_url
    },

    offline: {
      value: false,
      writable: true
    },

    local_storage_prefix: {
      value: "",
      writable: true
    },

    create_tables: {
      value: true,
      writable: true
    },

    /**
     * Содержит объект с расшифровкой параметров url, указанных при запуске программы
     * @property url_prm
     * @type {Object}
     * @static
     */
    url_prm: {
      value: typeof window != "undefined" ? parse_url() : {}
    },

    /**
     * Адрес стандартного интерфейса 1С OData
     * @method rest_url
     * @return {string}
     */
    rest_url: {
      value: function () {
        var url = base_url(),
          zone = $p.wsql.get_user_param("zone", $p.job_prm.zone_is_string ? "string" : "number");
        if(zone)
          return url.replace("%1", zone);
        else
          return url.replace("%1/", "");
      }
    },

    /**
     * Адрес http интерфейса библиотеки интеграции
     * @method irest_url
     * @return {string}
     */
    irest_url: {
      value: function () {
        var url = base_url(),
          zone = $p.wsql.get_user_param("zone", $p.job_prm.zone_is_string ? "string" : "number");
        url = url.replace("odata/standard.odata", "hs/rest");
        if(zone)
          return url.replace("%1", zone);
        else
          return url.replace("%1/", "");
      }
    }
  });

  // подмешиваем параметры, заданные в файле настроек сборки
  $p.eve.callEvent("settings", [this]);

  // подмешиваем параметры url
  // Они обладают приоритетом над настройками по умолчанию и настройками из settings.js
  for(var prm_name in this){
    if(prm_name !== "url_prm" && typeof this[prm_name] !== "function" && this.url_prm.hasOwnProperty[prm_name])
      this[prm_name] = this.url_prm[prm_name];
  }

}

/**
 * ### Модификатор отложенного запуска
 * Служебный объект, реализующий отложенную загрузку модулей,<br />
 * в которых доопределяется (переопределяется) поведение объектов и менеджеров конкретных типов
 *
 * @class Modifiers
 * @constructor
 * @menuorder 62
 * @tooltip Внешние модули
 */
function Modifiers(){

  var methods = [];

  /**
   * Добавляет метод в коллекцию методов для отложенного вызова
   * @method push
   * @param method {Function} - функция, которая будет вызвана после инициализации менеджеров объектов данных
   */
  this.push = function (method) {
    methods.push(method);
  };

  /**
   * Отменяет подписку на событие
   * @method detache
   * @param method {Function}
   */
  this.detache = function (method) {
    var index = methods.indexOf(method);
    if(index != -1)
      methods.splice(index, 1);
  };

  /**
   * Отменяет все подписки
   * @method clear
   */
  this.clear = function () {
    methods.length = 0;
  };

  /**
   * Загружает и выполняет методы модификаторов
   * @method execute
   */
  this.execute = function (context) {

    // выполняем вшитые в сборку модификаторы
    var res, tres;
    methods.forEach(function (method) {
      if(typeof method === "function")
        tres = method(context);
      else
        tres = $p.injected_data[method](context);
      if(res !== false)
        res = tres;
    });
    return res;
  };

  /**
   * выполняет подключаемые модификаторы
   * @method execute_external
   * @param data
   */
  this.execute_external = function (data) {

    var paths = $p.wsql.get_user_param("modifiers");

    if(paths){
      paths = paths.split('\n').map(function (path) {
        if(path)
          return new Promise(function(resolve, reject){
            $p.load_script(path, "script", resolve);
          });
        else
          return Promise.resolve();
      });
    }else
      paths = [];

    return Promise.all(paths)
      .then(function () {
        this.execute(data);
      }.bind(this));
  };

}