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

Показывать:
  1. /**
  2. * Содержит методы обработки событий __при запуске__ программы, __перед закрытием__,<br />
  3. * при обновлении файлов __ApplicationCache__, а так же, при переходе в __offline__ и __online__
  4. *
  5. * События развиваются в такой последовательности:
  6. *
  7. * 1) выясняем, совместим ли браузер. В зависимости от параметров url и параметров по умолчанию,
  8. * может произойти переход в ChromeStore или другие действия
  9. *
  10. * 2) анализируем AppCache, при необходимости обновляем скрипты и перезагружаем страницу
  11. *
  12. * 3) инициализируем $p.wsql и комбинируем параметры работы программы с параметрами url
  13. *
  14. * 4) если режим работы предполагает использование построителя, подключаем слушатель его событий.
  15. * по событию построителя "ready", выполняем метод initMainLayout() объекта $p.iface.
  16. * Метод initMainLayout() переопределяется во внешним, по отношению к ядру, модуле
  17. *
  18. * &copy; Evgeniy Malyarov http://www.oknosoft.ru 2014-2016
  19. *
  20. * @module common
  21. * @submodule events
  22. */
  23.  
  24. /**
  25. * ### Обработчики событий приложения
  26. *
  27. * Cм. так же, модули {{#crossLinkModule "events"}}{{/crossLinkModule}} и {{#crossLinkModule "events_browser"}}{{/crossLinkModule}}
  28. *
  29. * @class AppEvents
  30. * @static
  31. * @menuorder 30
  32. * @tooltip Движок событий
  33. */
  34. function AppEvents() {
  35.  
  36. this.__define({
  37.  
  38. /**
  39. * ### Запускает процесс инициализаци параметров и метаданных
  40. *
  41. * @method run
  42. * @for AppEvents
  43. *
  44. */
  45. init: {
  46. value: function () {
  47. $p.__define("job_prm", {
  48. value: new JobPrm(),
  49. writable: false
  50. });
  51. $p.wsql.init_params();
  52. }
  53. },
  54.  
  55. /**
  56. * ### Добавляет объекту методы генерации и обработки событий
  57. *
  58. * @method do_eventable
  59. * @for AppEvents
  60. * @param obj {Object} - объект, которому будут добавлены eventable свойства
  61. */
  62. do_eventable: {
  63. value: function (obj) {
  64.  
  65. function attach(name, func) {
  66. name = String(name).toLowerCase();
  67. if (!this._evnts.data[name])
  68. this._evnts.data[name] = {};
  69. var eventId = $p.utils.generate_guid();
  70. this._evnts.data[name][eventId] = func;
  71. return eventId;
  72. }
  73.  
  74. function detach(eventId) {
  75.  
  76. if(!eventId){
  77. return detach_all.call(this);
  78. }
  79.  
  80. for (var a in this._evnts.data) {
  81. var k = 0;
  82. for (var b in this._evnts.data[a]) {
  83. if (b == eventId) {
  84. this._evnts.data[a][b] = null;
  85. delete this._evnts.data[a][b];
  86. } else {
  87. k++;
  88. }
  89. }
  90. if (k == 0) {
  91. this._evnts.data[a] = null;
  92. delete this._evnts.data[a];
  93. }
  94. }
  95. }
  96.  
  97. function detach_all() {
  98. for (var a in this._evnts.data) {
  99. for (var b in this._evnts.data[a]) {
  100. this._evnts.data[a][b] = null;
  101. delete this._evnts.data[a][b];
  102. }
  103. this._evnts.data[a] = null;
  104. delete this._evnts.data[a];
  105. }
  106. }
  107.  
  108. function call(name, params) {
  109. name = String(name).toLowerCase();
  110. if (this._evnts.data[name] == null)
  111. return true;
  112. var r = true;
  113. for (var a in this._evnts.data[name]) {
  114. r = this._evnts.data[name][a].apply(this, params) && r;
  115. }
  116. return r;
  117. }
  118.  
  119. function ontimer() {
  120.  
  121. for(var name in this._evnts.evnts){
  122. var l = this._evnts.evnts[name].length;
  123. if(l){
  124. for(var i=0; i<l; i++){
  125. this.emit(name, this._evnts.evnts[name][i]);
  126. }
  127. this._evnts.evnts[name].length = 0;
  128. }
  129. }
  130.  
  131. this._evnts.timer = 0;
  132. }
  133.  
  134. obj.__define({
  135.  
  136. _evnts: {
  137. value: {
  138. data: {},
  139. timer: 0,
  140. evnts: {}
  141. }
  142. },
  143.  
  144. on: {
  145. value: attach
  146. },
  147.  
  148. attachEvent: {
  149. value: attach
  150. },
  151.  
  152. off: {
  153. value: detach
  154. },
  155.  
  156. detachEvent: {
  157. value: detach
  158. },
  159.  
  160. detachAllEvents: {
  161. value: detach_all
  162. },
  163.  
  164. checkEvent: {
  165. value: function(name) {
  166. name = String(name).toLowerCase();
  167. return (this._evnts.data[name] != null);
  168. }
  169. },
  170.  
  171. callEvent: {
  172. value: call
  173. },
  174.  
  175. emit: {
  176. value: call
  177. },
  178.  
  179. emit_async: {
  180. value: function callEvent(name, params){
  181.  
  182. if(!this._evnts.evnts[name])
  183. this._evnts.evnts[name] = [];
  184.  
  185. this._evnts.evnts[name].push(params);
  186.  
  187. if(this._evnts.timer)
  188. clearTimeout(this._evnts.timer);
  189.  
  190. this._evnts.timer = setTimeout(ontimer.bind(this), 4);
  191. }
  192. }
  193.  
  194. });
  195. }
  196. }
  197. });
  198.  
  199. // если мы внутри браузера и загружен dhtmlx, переносим в AppEvents свойства dhx4
  200. if(typeof window !== "undefined" && window.dhx4){
  201. for(var p in dhx4){
  202. this[p] = dhx4[p];
  203. delete dhx4[p];
  204. }
  205. window.dhx4 = this;
  206.  
  207. }else if(typeof WorkerGlobalScope === "undefined"){
  208.  
  209. // мы внутри Nodejs
  210.  
  211. this.do_eventable(this);
  212.  
  213. }
  214.  
  215. }
  216.  
  217. /**
  218. * ### Параметры работы программы
  219. * - Хранит глобальные настройки варианта компиляции (_Заказ дилера_, _Безбумажка_, _Демо_ и т.д.)
  220. * - Настройки извлекаются из файла "settings" при запуске приложения и дополняются параметрами url,
  221. * которые могут быть переданы как через search (?), так и через hash (#)
  222. * - см. так же, {{#crossLink "WSQL/get_user_param:method"}}{{/crossLink}} и {{#crossLink "WSQL/set_user_param:method"}}{{/crossLink}} - параметры, изменяемые пользователем
  223. *
  224. * @class JobPrm
  225. * @static
  226. * @menuorder 04
  227. * @tooltip Параметры приложения
  228. */
  229. function JobPrm(){
  230.  
  231. function base_url(){
  232. return $p.wsql.get_user_param("rest_path") || $p.job_prm.rest_path || "/a/zd/%1/odata/standard.odata/";
  233. }
  234.  
  235. function parse_url(){
  236.  
  237. function parse(url_prm){
  238. var prm = {}, tmp = [], pairs;
  239.  
  240. if(url_prm.substr(0, 1) === "#" || url_prm.substr(0, 1) === "?")
  241. url_prm = url_prm.substr(1);
  242.  
  243. if(url_prm.length > 2){
  244.  
  245. pairs = decodeURI(url_prm).split('&');
  246.  
  247. // берём параметры из url
  248. for (var i in pairs){ //разбиваем пару на ключ и значение, добавляем в их объект
  249. tmp = pairs[i].split('=');
  250. if(tmp[0] == "m"){
  251. try{
  252. prm[tmp[0]] = JSON.parse(tmp[1]);
  253. }catch(e){
  254. prm[tmp[0]] = {};
  255. }
  256. }else
  257. prm[tmp[0]] = tmp[1] || "";
  258. }
  259. }
  260.  
  261. return prm;
  262. }
  263.  
  264. return parse(location.search)._mixin(parse(location.hash));
  265. }
  266.  
  267. this.__define({
  268.  
  269. /**
  270. * Осуществляет синтаксический разбор параметров url
  271. * @method parse_url
  272. * @return {Object}
  273. */
  274. parse_url: {
  275. value: parse_url
  276. },
  277.  
  278. offline: {
  279. value: false,
  280. writable: true
  281. },
  282.  
  283. local_storage_prefix: {
  284. value: "",
  285. writable: true
  286. },
  287.  
  288. create_tables: {
  289. value: true,
  290. writable: true
  291. },
  292.  
  293. /**
  294. * Содержит объект с расшифровкой параметров url, указанных при запуске программы
  295. * @property url_prm
  296. * @type {Object}
  297. * @static
  298. */
  299. url_prm: {
  300. value: typeof window != "undefined" ? parse_url() : {}
  301. },
  302.  
  303. /**
  304. * Адрес стандартного интерфейса 1С OData
  305. * @method rest_url
  306. * @return {string}
  307. */
  308. rest_url: {
  309. value: function () {
  310. var url = base_url(),
  311. zone = $p.wsql.get_user_param("zone", $p.job_prm.zone_is_string ? "string" : "number");
  312. if(zone)
  313. return url.replace("%1", zone);
  314. else
  315. return url.replace("%1/", "");
  316. }
  317. },
  318.  
  319. /**
  320. * Адрес http интерфейса библиотеки интеграции
  321. * @method irest_url
  322. * @return {string}
  323. */
  324. irest_url: {
  325. value: function () {
  326. var url = base_url(),
  327. zone = $p.wsql.get_user_param("zone", $p.job_prm.zone_is_string ? "string" : "number");
  328. url = url.replace("odata/standard.odata", "hs/rest");
  329. if(zone)
  330. return url.replace("%1", zone);
  331. else
  332. return url.replace("%1/", "");
  333. }
  334. }
  335. });
  336.  
  337. // подмешиваем параметры, заданные в файле настроек сборки
  338. $p.eve.callEvent("settings", [this]);
  339.  
  340. // подмешиваем параметры url
  341. // Они обладают приоритетом над настройками по умолчанию и настройками из settings.js
  342. for(var prm_name in this){
  343. if(prm_name !== "url_prm" && typeof this[prm_name] !== "function" && this.url_prm.hasOwnProperty[prm_name])
  344. this[prm_name] = this.url_prm[prm_name];
  345. }
  346.  
  347. }
  348.  
  349. /**
  350. * ### Модификатор отложенного запуска
  351. * Служебный объект, реализующий отложенную загрузку модулей,<br />
  352. * в которых доопределяется (переопределяется) поведение объектов и менеджеров конкретных типов
  353. *
  354. * @class Modifiers
  355. * @constructor
  356. * @menuorder 62
  357. * @tooltip Внешние модули
  358. */
  359. function Modifiers(){
  360.  
  361. var methods = [];
  362.  
  363. /**
  364. * Добавляет метод в коллекцию методов для отложенного вызова
  365. * @method push
  366. * @param method {Function} - функция, которая будет вызвана после инициализации менеджеров объектов данных
  367. */
  368. this.push = function (method) {
  369. methods.push(method);
  370. };
  371.  
  372. /**
  373. * Отменяет подписку на событие
  374. * @method detache
  375. * @param method {Function}
  376. */
  377. this.detache = function (method) {
  378. var index = methods.indexOf(method);
  379. if(index != -1)
  380. methods.splice(index, 1);
  381. };
  382.  
  383. /**
  384. * Отменяет все подписки
  385. * @method clear
  386. */
  387. this.clear = function () {
  388. methods.length = 0;
  389. };
  390.  
  391. /**
  392. * Загружает и выполняет методы модификаторов
  393. * @method execute
  394. */
  395. this.execute = function (context) {
  396.  
  397. // выполняем вшитые в сборку модификаторы
  398. var res, tres;
  399. methods.forEach(function (method) {
  400. if(typeof method === "function")
  401. tres = method(context);
  402. else
  403. tres = $p.injected_data[method](context);
  404. if(res !== false)
  405. res = tres;
  406. });
  407. return res;
  408. };
  409.  
  410. /**
  411. * выполняет подключаемые модификаторы
  412. * @method execute_external
  413. * @param data
  414. */
  415. this.execute_external = function (data) {
  416.  
  417. var paths = $p.wsql.get_user_param("modifiers");
  418.  
  419. if(paths){
  420. paths = paths.split('\n').map(function (path) {
  421. if(path)
  422. return new Promise(function(resolve, reject){
  423. $p.load_script(path, "script", resolve);
  424. });
  425. else
  426. return Promise.resolve();
  427. });
  428. }else
  429. paths = [];
  430.  
  431. return Promise.all(paths)
  432. .then(function () {
  433. this.execute(data);
  434. }.bind(this));
  435. };
  436.  
  437. }
  438.  
  439.