/**
* Этот фрагмент кода выполняем только в браузере
* Created 28.12.2015<br />
* © Evgeniy Malyarov http://www.oknosoft.ru 2014-2016
* @module common
* @submodule events.ui
*/
$p.eve.__define({
/**
* Устанавливает состояние online/offline в параметрах работы программы
* @method set_offline
* @for AppEvents
* @param offline {Boolean}
*/
set_offline: {
value: function(offline){
var current_offline = $p.job_prm['offline'];
$p.job_prm['offline'] = !!(offline || $p.wsql.get_user_param('offline', 'boolean'));
if(current_offline != $p.job_prm['offline']){
// предпринять действия
current_offline = $p.job_prm['offline'];
}
}
},
/**
* Тип устройства и ориентация экрана
* @method on_rotate
* @for AppEvents
* @param e {Event}
*/
on_rotate: {
value: function (e) {
$p.job_prm.device_orient = (window.orientation == 0 || window.orientation == 180 ? "portrait":"landscape");
if (typeof(e) != "undefined")
$p.eve.callEvent("onOrientationChange", [$p.job_prm.device_orient]);
}
},
/**
* Шаги синхронизации (перечисление состояний)
* @property steps
* @for AppEvents
* @type SyncSteps
*/
steps: {
value: {
load_meta: 0, // загрузка метаданных из файла
authorization: 1, // авторизация на сервере 1С или Node (в автономном режиме шаг не выполняется)
create_managers: 2, // создание менеджеров объектов
process_access: 3, // загрузка данных пользователя, обрезанных по RLS (контрагенты, договоры, организации)
load_data_files: 4, // загрузка данных из файла зоны
load_data_db: 5, // догрузка данных с сервера 1С или Node
load_data_wsql: 6, // загрузка данных из локальной датабазы (имеет смысл, если локальная база не в ОЗУ)
save_data_wsql: 7 // кеширование данных из озу в локальную датабазу
}
},
/**
* Авторизация на сервере 1С
* @method log_in
* @for AppEvents
* @param onstep {Function} - callback обработки состояния. Функция вызывается в начале шага
* @return {Promise.<T>} - промис, ошибки которого должен обработать вызывающий код
* @async
*/
log_in: {
value: function(onstep){
var irest_attr = {},
mdd;
// информируем о начале операций
onstep($p.eve.steps.load_meta);
// выясняем, доступен ли irest (наш сервис) или мы ограничены стандартным rest-ом
// параллельно, проверяем авторизацию
$p.ajax.default_attr(irest_attr, $p.job_prm.irest_url());
return ($p.job_prm.offline ? Promise.resolve({responseURL: "", response: ""}) : $p.ajax.get_ex(irest_attr.url, irest_attr))
.then(function (req) {
if(!$p.job_prm.offline)
$p.job_prm.irest_enabled = true;
if(req.response[0] == "{")
return JSON.parse(req.response);
})
.catch(function () {
// если здесь ошибка, значит доступен только стандартный rest
})
.then(function (res) {
onstep($p.eve.steps.authorization);
// TODO: реализовать метод для получения списка ролей пользователя
mdd = res;
mdd.root = true;
// в автономном режиме сразу переходим к чтению первого файла данных
// если irest_enabled, значит уже авторизованы
if($p.job_prm.offline || $p.job_prm.irest_enabled)
return mdd;
else
return $p.ajax.get_ex($p.job_prm.rest_url()+"?$format=json", true)
.then(function () {
return mdd;
});
})
// обработчик ошибок авторизации
.catch(function (err) {
if($p.iface.auth.onerror)
$p.iface.auth.onerror(err);
throw err;
})
// интерпретируем ответ сервера
.then(function (res) {
onstep($p.eve.steps.load_data_files);
if($p.job_prm.offline)
return res;
// широковещательное оповещение об авторизованности на сервере
$p.eve.callEvent("log_in", [$p.ajax.authorized = true]);
if(typeof res == "string")
res = JSON.parse(res);
if($p.msg.check_soap_result(res))
return;
if($p.wsql.get_user_param("enable_save_pwd"))
$p.wsql.set_user_param("user_pwd", $p.ajax.password);
else if($p.wsql.get_user_param("user_pwd"))
$p.wsql.set_user_param("user_pwd", "");
// сохраняем разницу времени с сервером
if(res.now_1c && res.now_js)
$p.wsql.set_user_param("time_diff", res.now_1c - res.now_js);
})
// читаем справочники с ограниченным доступом, которые могли прибежать вместе с метаданными
.then(function () {
// здесь же, уточняем список печатных форм
_md.printing_plates(mdd.printing_plates);
});
}
}
});
/**
* Этот фрагмент кода выполняем только в браузере
* События окна внутри воркера и Node нас не интересуют
*/
(function(w, eve, msg){
var timer_setted = false,
cache;
/**
* Отслеживаем онлайн
*/
w.addEventListener('online', eve.set_offline);
w.addEventListener('offline', function(){eve.set_offline(true);});
/**
* ждём готовности документа
*/
w.addEventListener('load', function(){
/**
* Инициализацию выполняем с небольшой задержкой,
* чтобы позволить сторонним скриптам подписаться на событие onload и сделать свои черные дела
*/
setTimeout(function () {
/**
* Метод может быть вызван сторонним сайтом через post_message
* @param url
*/
function navigate(url){
if(url && (location.origin + location.pathname).indexOf(url)==-1)
location.replace(url);
}
/**
* Инициализируем параметры пользователя,
* проверяем offline и версию файлов
*/
function init_params(){
function load_css(){
var surl = dhtmlx.codebase, load_dhtmlx = true, load_meta = true;
if(surl.indexOf("cdn.jsdelivr.net")!=-1)
surl = "//cdn.jsdelivr.net/metadata/latest/";
// стили загружаем только при необходимости
for(var i=0; i < document.styleSheets.length; i++){
if(document.styleSheets[i].href){
if(document.styleSheets[i].href.indexOf("dhx_web")!=-1 || document.styleSheets[i].href.indexOf("dhx_terrace")!=-1)
load_dhtmlx = false;
if(document.styleSheets[i].href.indexOf("metadata.css")!=-1)
load_meta = false;
}
}
// задаём основной скин
dhtmlx.skin = $p.wsql.get_user_param("skin") || $p.job_prm.skin || "dhx_web";
//str.replace(new RegExp(list[i] + '$'), 'finish')
if(load_dhtmlx)
$p.load_script(surl + (dhtmlx.skin == "dhx_web" ? "dhx_web.css" : "dhx_terrace.css"), "link");
if(load_meta)
$p.load_script(surl + "metadata.css", "link");
// дополнительные стили
if($p.job_prm.additional_css)
$p.job_prm.additional_css.forEach(function (name) {
if(dhx4.isIE || name.indexOf("ie_only") == -1)
$p.load_script(name, "link");
});
// задаём путь к картинкам
dhtmlx.image_path = "//oknosoft.github.io/metadata.js/lib/imgs/";
// суффикс скина
dhtmlx.skin_suffix = function () {
return dhtmlx.skin.replace("dhx", "") + "/"
};
// запрещаем добавлять dhxr+date() к запросам get внутри dhtmlx
dhx4.ajax.cache = true;
/**
* ### Каркас оконного интерфейса
* См. описание на сайте dhtmlx [dhtmlXWindows](http://docs.dhtmlx.com/windows__index.html)
* @property w
* @for InterfaceObjs
* @type dhtmlXWindows
*/
$p.iface.__define("w", {
value: new dhtmlXWindows(),
enumerable: false
});
$p.iface.w.setSkin(dhtmlx.skin);
/**
* ### Всплывающие подсказки
* См. описание на сайте dhtmlx [dhtmlXPopup](http://docs.dhtmlx.com/popup__index.html)
* @property popup
* @for InterfaceObjs
* @type dhtmlXPopup
*/
$p.iface.__define("popup", {
value: new dhtmlXPopup(),
enumerable: false
});
}
// создавать dhtmlXWindows можно только после готовности документа
if("dhtmlx" in w)
load_css();
eve.stepper = {
step: 0,
count_all: 0,
step_size: 57,
files: 0
};
eve.set_offline(!navigator.onLine);
// инициализируем метаданные и обработчик при начале работы интерфейса
setTimeout(function () {
// устанавливаем параметры localStorage
$p.wsql.init_params();
// читаем локальные данные в ОЗУ
$p.wsql.pouch.load_data()
.catch($p.record_log);
// если есть сплэш, удаляем его
var splash;
if(splash = document.querySelector("#splash"))
splash.parentNode.removeChild(splash);
eve.callEvent("iface_init", [$p]);
}, 10);
msg.russian_names();
// TODO: переписать управление appcache на сервисворкерах
if($p.wsql.get_user_param("use_service_worker", "boolean") && typeof navigator != "undefined"
&& 'serviceWorker' in navigator && location.protocol.indexOf("https") != -1){
// Override the default scope of '/' with './', so that the registration applies
// to the current directory and everything underneath it.
navigator.serviceWorker.register('metadata_service_worker.js', {scope: '/'})
.then(function(registration) {
// At this point, registration has taken place.
// The service worker will not handle requests until this page and any
// other instances of this page (in other tabs, etc.) have been closed/reloaded.
$p.record_log('serviceWorker register succeeded');
})
.catch($p.record_log);
}else if (cache = w.applicationCache){
// обновление не требуется
cache.addEventListener('noupdate', function(e){
}, false);
// Ресурсы уже кэшированнны. Индикатор прогресса скрыт.
cache.addEventListener('cached', function(e){
timer_setted = true;
if($p.iface.appcache)
$p.iface.appcache.close();
}, false);
// Начало скачивания ресурсов. progress_max - количество ресурсов. Показываем индикатор прогресса
//cache.addEventListener('downloading', do_cache_update_msg, false);
// Процесс скачивания ресурсов. Индикатор прогресса изменяется
//cache.addEventListener('progress', do_cache_update_msg, false);
// Скачивание завершено. Скрываем индикатор прогресса. Обновляем кэш. Перезагружаем страницу.
cache.addEventListener('updateready', function(e) {
try{
cache.swapCache();
if($p.iface.appcache){
$p.iface.appcache.close();
}
}catch(e){}
do_reload();
}, false);
// Ошибка кеша
cache.addEventListener('error', $p.record_log, false);
}
}
// проверяем совместимость браузера
if(!w.JSON || !w.indexedDB){
eve.redirect = true;
msg.show_msg({type: "alert-error", text: msg.unsupported_browser, title: msg.unsupported_browser_title});
throw msg.unsupported_browser;
}
/**
* Нулевым делом, создаём объект параметров работы программы, в процессе создания которого,
* выполняется клиентский скрипт, переопределяющий триггеры и переменные окружения
* Параметры имеют значения по умолчанию, могут переопределяться подключаемыми модулями
* и параметрами url, синтаксический разбор url производим сразу
* @property job_prm
* @for MetaEngine
* @type JobPrm
* @static
*/
$p.__define("job_prm", {
value: new JobPrm(),
writable: false
});
/**
* если в job_prm указано использование геолокации, геокодер инициализируем с небольшой задержкой
*/
if($p.job_prm.use_ip_geo || $p.job_prm.use_google_geo){
/**
* Данные геолокации
* @property ipinfo
* @for MetaEngine
* @type IPInfo
* @static
*/
$p.ipinfo = new IPInfo();
}
if ($p.job_prm.use_google_geo) {
// подгружаем скрипты google
if(!window.google || !window.google.maps){
$p.on("iface_init", function () {
setTimeout(function(){
$p.load_script("//maps.google.com/maps/api/js?callback=$p.ipinfo.location_callback", "script", function(){});
}, 100);
});
}else
location_callback();
}
/**
* Если указано, навешиваем слушателя на postMessage
*/
if($p.job_prm.allow_post_message){
/**
* Обработчик события postMessage сторонних окон или родительского окна (если iframe)
* @event message
* @for AppEvents
*/
w.addEventListener("message", function(event) {
if($p.job_prm.allow_post_message == "*" || $p.job_prm.allow_post_message == event.origin){
if(typeof event.data == "string"){
try{
var res = eval(event.data);
if(res && event.source){
if(typeof res == "object")
res = JSON.stringify(res);
else if(typeof res == "function")
return;
event.source.postMessage(res, "*");
}
}catch(e){
$p.record_log(e);
}
}
}
});
}
if(typeof(w.orientation)=="undefined")
$p.job_prm.device_orient = w.innerWidth>w.innerHeight ? "landscape" : "portrait";
else
eve.on_rotate();
w.addEventListener("orientationchange", eve.on_rotate, false);
$p.job_prm.__define("device_type", {
get: function () {
var device_type = $p.wsql.get_user_param("device_type");
if(!device_type){
device_type = (function(i){return (i<800?"phone":(i<1024?"tablet":"desktop"));})(Math.max(screen.width, screen.height));
$p.wsql.set_user_param("device_type", device_type);
}
return device_type;
},
set: function (v) {
$p.wsql.set_user_param("device_type", v);
}
});
/**
* слушаем события клавиатуры
*/
document.body.addEventListener("keydown", function (ev) {
eve.callEvent("keydown", [ev]);
}, false);
setTimeout(init_params, 10);
}, 10);
function do_reload(){
if(!$p.ajax.authorized){
eve.redirect = true;
location.reload(true);
}
}
}, false);
/**
* Обработчик события "перед закрытием окна"
* @event onbeforeunload
* @for AppEvents
* @returns {string} - если не путсто, браузер показывает диалог с вопросом, можно ли закрывать
*/
w.onbeforeunload = function(){
if(!eve.redirect)
return msg.onbeforeunload;
};
/**
* Обработчик back/forward событий браузера
* @event popstat
* @for AppEvents
*/
w.addEventListener("popstat", $p.iface.hash_route);
/**
* Обработчик события изменения hash в url
* @event hashchange
* @for AppEvents
*/
w.addEventListener("hashchange", $p.iface.hash_route);
})(window, $p.eve, $p.msg);