How to use clearEmulate method in chromy

Best JavaScript code snippet using chromy

app.js

Source:app.js Github

copy

Full Screen

1/* jshint camelcase:false, unused:false, laxbreak:true, expr:true, boss:true */2/* globals modules, BEMHTML, app, project, window, document, setCookie, getCookie */3/**4 *5 * @module app6 * @overview Управление инфраструктурой одностраничного приложения (SPA).7 * @author lilliputten <lilliputten@yandex.ru>8 * @since 2016.07.269 * @version 2016.07.27, 16:4410 *11 * $Date: 2017-07-17 14:16:38 +0300 (Mon, 17 Jul 2017) $12 * $Id: app.js 8762 2017-07-17 11:16:38Z miheev $13 *14 * @see WEB_TINTS/source/blocks/shared/app/app.deps.js15 * @see WEB_TINTS/source/blocks/shared/app/_NavMenu/app_NavMenu.js16 *17 */18modules.define('app', [19 // 'app_controllers',20 'i-bem-dom',21 // 'vlayout',22 // 'appholder',23 // 'progressbar',24 // 'screenholder',25 'uri__querystring',26 'next-tick',27 'events__channels',28 'popup_controller',29 'waiter',30 'request_controller',31 'requestor',32 'project',33 'socket',34 'session',35 'vow',36 'store',37 'md5',38 'jquery'39],40function(provide,41 // app_controllers,42 BEMDOM,43 // vlayout,44 // appholder,45 // progressbar,46 // screenholder,47 querystring,48 nextTick,49 channels,50 popup_controller,51 waiter,52 request_controller,53 requestor,54 project,55 socket,56 session,57 vow,58 store,59 md5,60 $61) {62/**63 *64 * @class app65 * @classdesc Управление инфраструктурой одностраничного приложения (SPA).66 *67 *68 * TODO69 * ====70 *71 * 2017.03.22, 12:13 -- Выделение подмодулей:72 * - загрузка/предоставление данных,73 * - обработка ошибок,74 * - сессия/авторизация,75 * - открытие/закрытие страниц,76 * - работа с событиями/контентом заголовка/меню,77 * - параметры/конфигурация/состояние приложения,78 *79 * 2016.07.27, 16:34 -- Регистрация методов на закрытие страницы с подтверждением (можно закрыть/нельзя).80 *81 * 2016.08.03, 16:57 -- Повторная загрузка модулей (store) при загрузке browser.js пакетов.82 *83 * ОПИСАНИЕ84 * ========85 *86 * Параметры app.params87 * ====================88 *89 * - params.pageId : Идентификатор текущей страницы90 * - params.pageData : Описание текущей страницы91 *92 */93// Ссылка на описание модуля94var __module = this;95/** defaultParams ** {{{ Значения параметров по умолчанию.96 * @type {Object}97 */98var defaultParams = {99 /** [defaultParams] Время ожидания загрузки ресурсов. */100 asset_load_timeout : 600000, // XXX101 /** loaded_dicts ** Информация о загруженных данных/словарях "по требованию".102 * Сами данные хранятся в `app.params.dicts`.103 *104 * @type {Object}105 *106 */107 loaded_dicts : {108 },109 /** loaded_assets ** Информация о загруженных компонентах "по требованию".110 * @type {Object}111 *112 */113 loaded_assets : {114 },115 /** [defaultParams] Загруженные данные/словари **/116 dicts : {},117 /** [defaultParams] Загруженные данные/словари **/118 assets_data : {},119};/*}}}*/120provide(BEMDOM.declBlock(this.name, /** @lends app.prototype */ {121 /*{{{ Данные... */122 /** _clearLocationHash ** {{{123 */124 _clearLocationHash : function () {125 var prevLoadingPageFlag = app.params.loadingPageFlag;126 app.params.loadingPageFlag = true;127 window.location.hash = '';128 nextTick(function() {129 app.params.loadingPageFlag = prevLoadingPageFlag;130 });131 // history.replaceState({}, document.title, '.');132 },/*}}}*/133 /** _doLogon ** {{{ Запустить процесс авторизации134 */135 _doLogonAction : function (e) {136 e.preventDefault();137 this._clearLocationHash();138 this._initApp(true);139 return false;140 },/*}}}*/141 /** _specialPagesAcceptDom ** {{{ Действия при показе спецстраницы142 * @param specialId143 * @param {object} specialCtx144 * @param {DOM} domElem145 */146 _specialPagesAcceptDom : function (specialId, specialCtx, domElem) {147 var app = this,148 logonAction = $(domElem).find('#appLogon'),149 logonMethod = this._doLogonAction150 ;151 if ( logonAction && logonAction.length && logonMethod ) {152 this._domEvents(logonAction)153 .un('click', logonMethod)154 .on('click', logonMethod)155 ;156 }157 },/*}}}*/158 /** _specialPages{} ** {{{ Спецстраницы (напр., `SignedOut`) */159 _specialPages : {160 /*{{{*/AuthError : {161 title : 'Ошибка',162 icon : 'ti-lock',163 content : {164 block : 'page_message',165 escapeContent : false,166 content : [167 { elem : 'title', content : 'Возникла ошибка', },168 { elem : 'text', content : [169 {170 block : 'link',171 url : '{{rootUrl}}application/User/signin',172 id : 'appLogon',173 content : 'Заново войти в систему',174 },175 ] },176 ]177 },178 // acceptDom : function (specialId, specialCtx, domElem) {179 // },180 },/*}}}*/181 /*{{{*/SignedOut : {182 title : 'Сеанс работы с системой завершён',183 icon : 'ti-lock',184 content : {185 block : 'page_message',186 content : [187 { elem : 'title', content : 'Сеанс работы с системой завершён', },188 { elem : 'text', content : [189 {190 block : 'link',191 url : '{{rootUrl}}application/User/signin',192 id : 'appLogon',193 content : 'Заново войти в систему',194 },195 ] },196 ]197 },198 // acceptDom : function (specialId, specialCtx, domElem) {199 // },200 },/*}}}*/201 },/*}}}*/202 /** Обработчики закрытия страницы.203 * @type {Callback[]}204 */205 on_page_close_callbacks : [],206 /** Обработчики, которых опрашивают перед закрытием страницы: если вызывают переданный callback, то страницу можно закрывать. Реально закрывается, если все обработчики подтвердили закрытие.207 * @type {Callback[]}208 */209 before_page_close_pollers : [],210 /** Ошибки во время загрузки211 * @type {String[]}212 */213 load_errors : [],214 /** Счётчик ожидания загрузки ресурсов.215 * @type {Number}216 */217 load_waiting : 0,218 /** Флаг занятости. Состояние выполнения блокирующей процедуры. true : джём до завершения.219 * @type {Boolean}220 * @see См. {@link app#do}221 */222 operating : false,223 /** Очередь задач на выполнение после завершения текущей операции.224 * @type {Boolean}225 * @see См. {@link app#do}226 */227 operating_callbacks : [],228 registered_channel_events : [],229 collected_errors : '',230 /*}}}*/231 // Методы232 /** error2string ** {{{ Преобразование строки/объекта в текст ошибки233 * @param {*} o - Переменная для преобразования в строку234 * @returns {string}235 * TODO 2017.02.14, 23:11 -- Отработать многострочные разрывы (параграф, перенос строки...)236 */237 error2string : function (error) {238 var app = this,239 that = this,240 text = '',241 maxShowStringLength = project.config.maxShowStringLength || 300,242 match,243 undef244 ;245 try {246 // Если ошибка -- не объект, показываем, как строку247 if ( typeof error !== 'object' ) {248 text = String(error);249 }250 // Если ошибка уже обработана, то ничего не показываем251 else if ( !error || error.processed ) {252 return '';253 }254 else if ( Array.isArray(error) ) {255 text = error.map(this.error2string, this).join('\n\n');256 }257 // Объект с ошибкой258 else if ( error instanceof Error ) {259 text = /* error.stack || */error.message || String(error);260 if ( error.stack ) {261 text += '\n\n<pre>' + error.stack + '</pre>';262 }263 if ( Array.isArray(error.trace) ) {264 text += '\n\n<small><b>Ошибка перехвачена в:</b> ' + error.trace.join(', ') + '</small>';265 }266 }267 // ajax 404268 else if ( ( ( error.jqXHR && error.jqXHR.status === 404 ) || error.error === 'Not found' ) && error.settings && error.settings.url ) {269 console.warn( 'app error2string Not found', error );270 text = 'Ресурс не найден: <u>'+error.settings.url+'</u>';271 }272 else if ( error.message ) {273 console.warn( 'app error2string message', error );274 // text += ( error.description || 'Сообщение об ошибке:' ) + '\n\n';275 text += error.message;276 }277 else if ( /* error.error === 'errorMessages' && */ Array.isArray(error.errorMessages) ) {278 console.warn( 'app error2string errorMessages', error );279 // text = ( error.description || 'Список ошибок:' ) + '\n\n';280 text += error.errorMessages281 .map(function(error){ return that.error2string(error); })282 .join('\n\n')283 ;284 }285 else if ( error.textStatus === 'parsererror' ) {286 text = 'Ошибка обработки ответа сервера';287 var plusText = error.error || ( error.jqXHR && error.jqXHR.responseText && ( match = error.jqXHR.responseText.match(/<b>(Parse error|Fatal error).*/) ) !== null && match[0] ) ;288 if ( plusText ) {289 if ( plusText.length > maxShowStringLength ) {290 plusText = plusText.substr(0, maxShowStringLength-3)+'...';291 }292 text += '\n' + plusText;293 }294 }295 else if ( error.error === 'jqXHR' ) {296 console.warn( 'app error2string jqXHR', error );297 text = error.description || 'Ошибка ajax';298 var299 props = {300 'адрес' : error.url || error.location,301 },302 propsText = Object.keys(props)303 .filter(function(name){ return props[name] ? true : false; })304 .map(function(name){ return name+': '+props[name]; })305 .join(', ')306 ;307 if ( propsText ) {308 text += ' ('+propsText+')';309 }310 return text;311 }312 else if ( error.error && typeof error.error === 'object' /* && !Array.isArray(error.error) */ ) {313 text += this.error2string(error.error);314 }315 else {316 text = error.description || error.message || error.error || error.errorText || 'Неопределённая ошибка';// String(error);317 // ?????318 if ( error.jqXHR && error.jqXHR.responseText ) {319 text += '\n\n' + error.jqXHR.responseText;320 }321 if ( error.error && typeof error.error === 'object' ) {322 text += '\n\n' + this.error2string(error.error);323 }324 if ( Array.isArray(error.trace) ) {325 text += '\n\n<b>Ошибка перехвачена в:</b> ' + error.trace.join(', ');326 }327 }328 return text.trim();329 }330 catch (e) {331 console.error( 'error2string error:', e );332 /*DEBUG*//*jshint -W087*/debugger;333 ( e.trace || ( e.trace = [] ) ).push('app:error2string');334 return e;335 }336 },/*}}}*/337 /** error2html ** {{{ Преобразование строки/объекта в html-текст ошибки338 * @param {*} o - Переменная для преобразования в строку339 * @returns {string} HTML340 */341 error2html : function (e) {342 var errorText = this.error2string(e),343 errorHtml = '<p>' + errorText.trim().replace(/(\s*\n){2,}/g, '</p><p>').replace(/\s*\n/g, '<br>') + '</p>',344 undef345 ;346 return errorHtml;347 },/*}}}*/348 /** error() ** {{{ Прерываем операции (?), показываем сообщение об ошибке349 * @param {...} errors - Список сообщений об ошибке. Отдельные параметры объединяются через пробел.350 */351 error : function (error) {352 var app = this,353 that = app,354 params = this.params,355 args = Array.prototype.slice.call(arguments),356 firstArg = args[0]357 ;358 // Если одиничная ошибка, проверяем на спецошибки // XXX359 if ( args.length === 1 && typeof firstArg === 'object' && firstArg ) {360 // Ошибка обработана; ничего не делаем361 if ( firstArg.processed ) {362 return;363 }364 // Если отмена авторизации365 else if ( firstArg.error === 'AuthCancelled'366 || ( firstArg.textStatus === 'canceled' && firstArg.error === 'отмена авторизации' ) ) {367 return this.showSpecialPage('SignedOut');368 }369 }370 var371 textArgs = args.map(function(arg){372 return that.error2string(arg);373 }),374 errorText = textArgs.join(' ').replace(/\s*<br[^<>]*>[\n\r]*/g, '\n').trim(),375 // errorText = Array.prototype.slice.call(arguments).join(' ').replace(/\s*<br[^<>]*>[\n\r]*/g, '\n'),376 errorHtml = errorText.replace(/(\s*\n){2,}/g, '<p>').replace(/\s*\n/g, '<br>'),377 errorPlain = errorText.replace(/<[^<>]*>/g, '').replace(/(\s*\n){3,}/g, '\n\n'),378 currentScreenholder = ( this._appholder && this._appholder.getMod('show') ) ? this._appholder : this.screenholder,379 undef380 ;381 try {382 console.error( 'app.error called', errorPlain );383 /*DEBUG*//*jshint -W087*/debugger;384 // Если ошибка пустая, ничего не делаем385 if ( !errorText ) {386 return;387 }388 // this.screenholder && this.screenholder.error(this.collected_errors);389 // currentScreenholder && currentScreenholder.error(this.collected_errors);390 if ( currentScreenholder ) { currentScreenholder.error(errorHtml); }391 // popup_controller.error(errorHtml, 'Ошибка приложения');392 }393 catch (e) {394 console.error(e);395 /*DEBUG*//*jshint -W087*/debugger;396 }397 },/*}}}*/398 /** __error ** {{{ Обработка внутренней ошибки399 * @param {Object|*} error - Ошибка400 * @param {String} [methodName] - Имя метода, вызвавшего ошибку. Если не укзан, пробуем определить через `callee.caller`.401 * @returns {Promise} - reject-промис с ошибкой.402 *403 * Вывод сообщения о вызове (модуль, метод) в консоль, останавливает404 * выполнение (debugger), добавляет информацию о вызове в ошибку (если405 * объект), возвращает проваленный (rejected) промис с ошибкой.406 */407 __error : function (error, methodName) {408 methodName = methodName || ( arguments.callee && arguments.callee.caller && arguments.callee.caller.name ) || '(anonymous)'; // jshint ignore:line409 var errorId = __module.name + ':' + methodName;410 console.error( errorId, error );411 /*DEBUG*//*jshint -W087*/debugger;412 if ( !error ) { error = {}; }413 else if ( typeof error !== 'object' || Array.isArray(error) ) { error = { error : error }; }414 // if ( error && typeof error === 'object' && !Array.isArray(error) ) {415 ( error.trace || ( error.trace = [] ) ).push(errorId);416 // }417 return vow.Promise.reject(error);418 },/*}}}*/419 /** do(callback) ** {{{ Выполнение критичной процедуры: отлженное или сразу, с учётом флага занятости. (???)420 * @param {Callback} callback421 *422 * @see См. {@link app#operating}, {@link app#operating_callbacks}423 *424 */425 do : function (callback) {426 // var app = this;427 ( this.operating_callbacks || (this.operating_callbacks=[]) ).push(callback);428 if ( !this.operating ) {429 this.operating = true;430 var waited_callback;431 while ( waited_callback = this.operating_callbacks.shift() ) {432 waited_callback();433 }434 // callback();435 this.operating = false;436 }437 },/*}}}*/438 /** _renewSession() ** {{{ Обновляем сессию439 */440 _renewSession : function () {441 // Если не залогинены или обновление происходит сейчас, то ничего не делаем442 if ( /* !this.getMod('loggedOn') || */ this._renewingSessionNow ) {443 return;444 }445 // Иначе устанавливаем флаг446 else {447 this._renewingSessionNow = true;448 }449 var app = this,450 params = this.params,451 oldToken = getCookie(project.config.authCookieName),452 renewUrl = project.helpers.expand_path(project.config.renew_session_url),453 requestTimeout = app.params.asset_load_timeout, //30000,454 renew_waiter_settings = {455 id : 'renewSession',456 title : 'Обновление сессии',457 timeout : requestTimeout,458 method : 'GET',459 url : renewUrl,460 silent_errors : true,461 },462 renewWaiter = request_controller.do_waiter_request(renew_waiter_settings),463 newToken,464 newTokenData465 ;466 // Иначе (не проверка) обычный вызов.467 // Наверное, вообще можно ничего не обрабатывать по завершении?468 renewWaiter469 .then(function(data){470 newToken = getCookie(project.config.authCookieName);471 newTokenData = data.tokenValue;472 return data;473 })474 .fail(function(error) {475 if ( app._renewSessionTimer ) {476 clearTimeout(app._renewSessionTimer);477 delete app._renewSessionTimer;478 }479 if ( error.indexOf('<a href') === -1 ) {480 var auth_url = project.helpers.expand_path(project.config.auth_url);481 error = 'Ошибка авторизации\n'+error+'\n<a href="'+auth_url+'">Аторизоваться</a>';482 }483 app.error(error);484 /*DEBUG*//*jshint -W087*/debugger;485 return error;486 })487 .always(function(){488 app._renewingSessionNow = false;489 })490 ;491 return renewWaiter;492 },/*}}}*/493 /** _stopRenewSession() ** {{{ Останавливаем периодическое обновление сессии494 */495 _stopRenewSession : function () {496 var app = this,497 undef498 ;499 if ( this._renewSessionTimer ) {500 clearInterval(this._renewSessionTimer);501 delete this._renewSessionTimer;502 }503 },/*}}}*/504 /** _startRenewSession() ** {{{ Инициируем периодическое обновление сессии505 */506 _startRenewSession : function () {507 var app = this,508 params = this.params,509 token_refresh_time = params.config.token.refresh_time510 ;511 if ( !project.config.LOCAL_ENB ) {512 this._renewSessionTimer = setInterval(function(){513 app._renewSession();514 }, token_refresh_time);515 }516 },/*}}}*/517 /** _startSocket() ** {{{ Запускаем работу с сокетами518 * @returns {Promise}519 */520 _startSocket : function () {521 if ( !project.config.useSockets ) {522 return vow.Promise.resolve({ status : 'notUseSockets', description : 'Сокеты не используются (параметр конфигурации)' });523 }524 var app = this,525 params = this.params,526 // Метод информирования об ошибках подключения сокетов -- в зависимости от конфигурации, либо ошибка, либо игнорирование527 waiterFailMethod = project.config.catchSocketsError ? waiter.error : waiter.done,528 waiterFail = waiterFailMethod.bind(waiter),529 waiterId = 'startSocket' + Date.now(),530 socketWaiter = waiter.start(waiterId, {531 title: 'Подключение сокетов',532 timeout : 30000,533 on_cancel : function () {534 app.socket.disconnect();535 waiterFail(waiterId, { status : 'socketConnectionCanceled', description : 'Подключение сокетов отменено' });536 },537 })538 ;539 app.socket.connect()540 .then(function(data){541 waiter.done(waiterId, data);542 return data;543 // return vow.Promise.resolve({ status : 'socketsNotPresent', error : e });544 })545 .fail(function(error){546 // auth-fail:547 // description : "Отказ авторизации сокетов"548 // error : "socketError"549 // msg : "redisClient.get("ko:3047134149476200959184219bf69c4.19119989") error: empty reply"550 // socket : Object551 // socketToken : "3047134149476200959184219bf69c4.19119989"552 // socketUrl : "http://localhost:8083"553 // trace : Array(1)554 // type : "socketAuthError"555 console.error( '_startSocket fail', error );556 /*DEBUG*//*jshint -W087*/debugger;557 var result = { status : 'socketsAbsent', description : 'Отсутствует подключение к сокетам', error : error };558 waiterFail(waiterId, result);559 })560 ;561 return socketWaiter;562 },/*}}}*/563 /** _stopSocket() ** {{{ Останавливаем работу с сокетами564 * @returns {Promise}565 */566 _stopSocket : function () {567 var app = this,568 params = this.params569 ;570 if ( !app.socket.isConnected() ) {571 return vow.Promise.resolve({ status : 'nothingToDisconnect' });572 }573 return app.socket.disconnect()574 // .then(function(data){575 // return data;576 // })577 .fail(function(e){578 // return app.error(e);579 return app.__error(e, '_stopSocket:Promise');580 })581 ;582 },/*}}}*/583 /** register_channel_event() ** {{{ Устанавливаем и сохраняем для последующего автоматического сброса при закрытии страницы событие канала.584 * @param {string} channel_id585 * @param {string} event_id586 * @param {callback} callback587 * @param {boolean} is_once588 */589 register_channel_event : function (channel_id, event_id, callback, is_once) {590 typeof this.registered_channel_events[channel_id] === 'undefined'591 && ( this.registered_channel_events[channel_id] = [] );592 this.registered_channel_events[channel_id].indexOf(event_id) === -1593 && this.registered_channel_events[channel_id].push(event_id);594 if ( is_once ) {595 channels(channel_id).once(event_id, callback);596 }597 else {598 channels(channel_id).on(event_id, callback);599 }600 },/*}}}*/601 /** init_window_actions() ** {{{ (???) Инициализируем глобальные функции (для работы модульной системы).602 *603 */604 init_window_actions : function () {605 var app = this;606 /** TODO: Функции обратных вызовов пока не используются */607 },/*}}}*/608 /** load_asset(asset_params) ** {{{ Загружаем ресурс609 *610 * TODO 2016.11.02, 12:25 -- Переделать под Promise и все запросы под ajax. (!!!)611 * TODO 2016.08.03, 14:09 -- +dicts (2016.08.16, 19:18 -- ???)612 * TODO 2016.08.16, 19:18 -- +передача данных в запрос613 * TODO 2016.08.17, 14:04 -- Накопление колбеков (повторный вызов во время загрузки)614 *615 * @param {Object} asset_params - Объект описания ресурса.616 * @param {String} asset_params.type - Тип ресурса (package, script, style, json, html)617 * @param {String} [asset_params.kind] - (Для type=package) Тип пакета (browser.js, bemhtml.js, styles.css)618 * @param {String} [asset_params.id] - (Для type=package) Идентификатор пакета619 * @param {String} [asset_params.url] - Адрес загрузки ресурса (Не обязателен для type=package && kind && id).620 * @param {String} [asset_params.get] - Метод загрузки (get, post).621 * @param {Object} [asset_params.data] - Данные для передачи на сервер.622 * @param {Object} [asset_params.expires] - Время жизни данных (если тип 'data' или 'request') -- ???.623 *624 * @param {Callback} [on_success] - Обратный вызов в случае успеха.625 * @param {Callback} [on_error] - Обратный вызов в случае ошибки.626 *627 */628 load_asset : function (asset_params, on_success, on_error) {629 var app = this;630 var params = this.params;631 var id;632 // {{{ Если указан идентификатор (ресурс был загружен ранее -- в описании не указываем прямо)...633 if ( asset_params.id ) {634 id = asset_params.id;635 }// }}}636 // {{{ ...иначе созадаём идентификатор...637 else {638 id = asset_params.type || 'data';639 // Для пакета640 if ( asset_params.type === 'package' ) {641 // Если указан адрес642 if ( asset_params.url ) {643 var no_min_url = asset_params.url.replace(/\.min\b/, '');644 var res = no_min_url.match(/([\w\.-]+)\.(bemhtml\.(js)|browser\.(js)|styles\.(css))$/);645 if ( !res ) {646 console.error('Не указан идентификатор пакета для загрузки:', asset_params);647 typeof on_error === 'function' && on_error('Не указан идентификатор пакета для загрузки: '+asset_params.url);648 return false;649 }650 if ( !asset_params.name ) { asset_params.name = res[1]; }651 if ( !asset_params.kind ) { asset_params.kind = res[2]; }652 }653 // ... иначе, если нет адреса, но указаны тип и имя пакета, собираем адрес:654 else if ( asset_params.name && asset_params.kind ) {655 var root_prefix;656 // Если локальный enb-server, запрашиваем из папки бандлов: '/pages/<bundle>/<bundle>.<kind>'657 if ( project.config.LOCAL_ENB ) {658 root_prefix = '/pages/'659 + asset_params.name660 + '/'661 ;662 }663 // Иначе из стандартной серверной локации: '.../{js,css}/bem/<bundle>.<kind>664 else {665 root_prefix = project.config.coreUrl666 + asset_params.kind.replace(/(bemhtml|browser|styles)\./,'')667 + '/bem/'668 ;669 }670 asset_params.url = root_prefix671 + asset_params.name672 + '.'673 + asset_params.kind674 ;675 // ???676 // Для enb-server запрашиваем переупакованные пакеты (.bemhtmlx.js, .browserx.js, .stylesx.css)677 if ( project.config.LOCAL_ENB ) {678 // asset_params.url = asset_params.url.replace(/(bemhtml|browser|styles)(\.js|\.css)/,'$1x$2');679 asset_params.url = asset_params.url.replace(/(bemhtml|browser|styles)(\.(?:js|css))/,'$1x$2');680 }681 // ???682 // Для рабочего сервера запрашиваем минимизированную версию, если не режим отладки683 if ( !project.config.LOCAL_ENB && project.useMinifiedPackets && project.config.MINEXT ) {684 asset_params.url = asset_params.url.replace(/(\.(?:js|css))$/, project.config.MINEXT+'$1');685 }686 }687 // ...Иначе невозможно сформировать id688 else {689 console.error('Невозможно сформировать идентификатор пакета: ', asset_params);690 typeof on_error === 'function' && on_error('Невозможно сформировать идентификатор пакета: '+asset_params);691 return false;692 }693 id += ':'+asset_params.kind+':'+asset_params.name;694 }695 // ...для обычного ресурса (скрипт, стили и т.д.)696 else {697 id += ':'+asset_params.url;698 }699 // Сохраняем идентификатор в описании700 asset_params.id = id;701 }// }}}702 var current_time = Date.now(),703 loaded_asset = params.loaded_assets[id],704 on_success_cb,705 on_error_cb706 ;707 // {{{ Создаём объект отслеживания ресурса, если не создан ранее...708 if ( typeof loaded_asset === 'undefined' ) {709 loaded_asset = params.loaded_assets[id] = {710 params : asset_params,711 on_success_stack : [],712 on_error_stack : [],713 };714 }// }}}715 if ( !loaded_asset.on_success_stack ) { loaded_asset.on_success_stack = []; }716 if ( !loaded_asset.on_error_stack ) { loaded_asset.on_error_stack = []; }717 typeof on_success === 'function' && loaded_asset.on_success_stack.push(on_success);718 typeof on_error === 'function' && loaded_asset.on_error_stack.push(on_error);719 // TODO 2016.08.17, 13:59 -- +Проверка на таймаут?720 // {{{ Если установлен флаг загруженности и период актуальности не истёк, то загружен ранее -- завершаемся с успехом.721 if ( loaded_asset.loaded722 && loaded_asset.time723 && ( !asset_params.expires || current_time - loaded_asset.time < asset_params.expires ) ) {724 if ( loaded_asset.on_success_stack ) {725 while ( on_success_cb = loaded_asset.on_success_stack.shift() ) {726 on_success_cb();727 }728 }729 delete loaded_asset.on_success_stack;730 delete loaded_asset.on_error_stack;731 return true;732 }// }}}733 // Заменяем переменные пути734 asset_params.url = project.helpers.expand_path(asset_params.url);735 // {{{ Если урл не абсолютный (или ссылающийся на верхний уровень иерархии?)736 // -- не для пакетов, для них путь формируем отдельно (см.выше)...737 if ( !asset_params.url.startsWith('http://')738 && !asset_params.url.startsWith('../')739 && !asset_params.url.startsWith('/')740 && project.config.staticUrl ) {741 // ...считаем, что файл располагается в папке со статическими ресурсами742 asset_params.url = project.config.staticUrl + asset_params.url;743 }// }}}744 // Анти-кэш745 if ( app.config.bem.hashTag && asset_params.url.match(/\.(css|js|htm|html|txt|json)$/) ) {746 asset_params.url += '?--' + app.config.bem.hashTag;747 }748 // Для локального enb-сервера (и режима эмуляции) пакеты не загружаем749 // -- в демо-режиме указываем всё необходимое в зависисмостях750 // при генерации страницы из bem-xjst (чтобы сформировать заодно и bemjson страницы).751 if ( app.getMod('emulate') && asset_params.type === 'package' && asset_params.name === app.params.pageId ) {752 if ( loaded_asset.on_success_stack ) {753 while ( on_success_cb = loaded_asset.on_success_stack.shift() ) { // jshint ignore:line754 on_success_cb();755 }756 }757 delete loaded_asset.on_success_stack;758 delete loaded_asset.on_error_stack;759 return;760 }761 // Иначе загружаем ресурс:762 // {{{ Стили загружаем без отслеживания результата процесса.763 if ( asset_params.type === 'style' || asset_params.kind === 'styles.css' ) {764 loaded_asset.loaded = true;765 loaded_asset.time = current_time;766 var link = document.createElement('link');767 link.setAttribute('rel', 'stylesheet');768 link.setAttribute('type', 'text/css');769 link.setAttribute('href', asset_params.url);770 var head = document.getElementsByTagName('head')[0];771 head.insertBefore(link, head.firstChild);772 if ( loaded_asset.on_success_stack ) {773 while ( on_success_cb = loaded_asset.on_success_stack.shift() ) { // jshint ignore:line774 on_success_cb();775 }776 }777 delete loaded_asset.on_success_stack;778 delete loaded_asset.on_error_stack;779 }// }}}780 // {{{ ... Иначе загружаем скрипт...781 else /* if ( false ) */ {782 // Если уже загружается...783 if ( loaded_asset.waiting ) {784 // ...ничего не делаем.785 return true;786 }787 // {{{ Подготавливаем...788 // Устанавливаем флаг ожидания...789 loaded_asset.waiting = true;790 // ...и увеличиваем счётчик ожидания.791 app.load_waiting++;792 // Идентификатор ожидателя:793 var waiter_id = ('app_asset_load_'+id).replace(/[^A-z0-9]+/g,'_')+'_'+Date.now();794 // }}}795 // {{{ Код для окончания загрузки:796 var done_loading = function (error) {797 // Обработка ошибок798 if ( error ) {799 if ( loaded_asset.on_error_stack ) {800 while ( on_error_cb = loaded_asset.on_error_stack.shift() ) { // jshint ignore:line801 on_error_cb(error);802 }803 }804 delete loaded_asset.on_success_stack;805 delete loaded_asset.on_error_stack;806 }807 // Если очередь загружающихся ресурсов пуста и всё ещё ждём...808 if ( !--app.load_waiting ) { // XXX!!!809 // ...завершаем ожидание.810 waiter.finish('app_load_assets_and_dicts');811 }812 };// }}}813 // {{{ Стартуем ожидатель:814 waiter.start(waiter_id, {815 title : 'Загрузка ресурса '+id,816 timeout : app.params.asset_load_timeout,817 timeout_break : true,818 on_timeout : function () {819 // jsdoc error: Delete of an unqualified identifier in strict mode. -- ???820 // delete loaded_asset; // ERROR: Delete of an unqualified identifier in strict mode ???821 done_loading('Превышено время ожидания для загрузки ресурса '+id);822 },823 on_cancel : function () {824 // Завершаем ожидание825 waiter.finish(waiter_id);826 // jsdoc error: Delete of an unqualified identifier in strict mode. -- ???827 // delete loaded_asset; // ERROR: Delete of an unqualified identifier in strict mode ???828 done_loading('Отменена загрузка ресурса '+id);829 },830 on_finish : function () {831 done_loading();832 },833 });// }}}834 // {{{ В зависимости от типа: ... Если данные (html, json, запрос)...835 if ( typeof asset_params.type === 'undefined' || asset_params.type === 'request' || asset_params.type === 'data' ) {836 requestor.promiseRequest(asset_params)837 .then(function(data){838 waiter.finish(waiter_id);839 delete loaded_asset.waiting;840 loaded_asset.loaded = true;841 loaded_asset.time = current_time;842 params.assets_data[id] = data;843 if ( loaded_asset.on_success_stack ) {844 while ( on_success_cb = loaded_asset.on_success_stack.shift() ) {845 on_success_cb(data);846 }847 }848 delete loaded_asset.on_success_stack;849 delete loaded_asset.on_error_stack;850 })851 .fail(function(data){852 console.error( 'asset_load fail', id, data );853 /*DEBUG*//*jshint -W087*/debugger;854 waiter.finish(waiter_id, data);855 if ( loaded_asset.on_error_stack ) {856 while ( on_error_cb = loaded_asset.on_error_stack.shift() ) {857 on_error_cb(data);858 }859 }860 delete loaded_asset.on_success_stack;861 delete loaded_asset.on_error_stack;862 })863 ;864 }// }}}865 // {{{ Если скрипт (пакет или сам по себе)...866 else if ( asset_params.type === 'script' || asset_params.kind === 'browser.js' || asset_params.kind === 'bemhtml.js' ) {867 var script = document.createElement('script');868 script.type = 'text/javascript';869 script.charset = 'utf-8';870 script.src = loaded_asset.params.url;871 script.onload = function script_onload () {872 var script_loaded = function script_loaded (result) {873 waiter.finish(waiter_id);874 if ( loaded_asset ) {875 delete loaded_asset.waiting;876 loaded_asset.loaded = true;877 loaded_asset.time = current_time;878 if ( loaded_asset.on_success_stack ) {879 while ( on_success_cb = loaded_asset.on_success_stack.shift() ) {880 on_success_cb();881 }882 }883 delete loaded_asset.on_success_stack;884 delete loaded_asset.on_error_stack;885 }886 };887 if ( true || script.src.endsWith('browser.js') || script.src.endsWith('browser.min.js') ) {888 modules.require([889 'i-bem-dom__init',890 ], function(){891 script_loaded();892 });893 }894 else {895 script_loaded();896 }897 };898 var headTag = document.getElementsByTagName('head')[0];899 headTag.insertBefore(script, headTag.firstChild);900 }// скрипт }}}901 }// }}}902 return true;903 },/*}}}*/904 /** load_dicts_queue_request(...) ** {{{ Загружаем данные/словари по списку и типу кеша905 *906 * @param {String[]} idlist - Список идентификаторов данных/словарей для загрузки907 * @param {String} lifetime_id - Идентификатор типа кеширования.908 * См. `$_CONSTANTS['cache']['_DATA_TYPES']`909 * в `WEB_TINTS/core/scripts/php/app/config/config_constants.php`910 * @param {Callback} on_success - Выполняем в случае успеха.911 * @param {Callback} on_error - Выполняем в случае ошибки.912 * @param {String} load_waiting_waiter_id - Идентификатор ожидателя для завершения,913 * если счётчик загружаемых ресурсов ({@link app#load_waiting}) равен нулю.914 * Напр., 'app_load_assets_and_dicts'.915 */916 load_dicts_queue_request : function (idlist, lifetime_id, on_success, on_error, load_waiting_waiter_id) {917 var app = this,918 params = this.params;919 var op_id = 'app_load_dicts_queue',920 idlist_s = idlist.join(','),921 idlist_sx = idlist_s.replace(/,\s*/g, '_'),922 idlist_ss = idlist.join(', '),923 request_id = op_id + '_' + idlist_sx + Date.now()924 ;925 app.load_waiting++;926 var ajax_url = project.helpers.get_remote_url(op_id),927 ajax_method = project.helpers.get_remote_method(op_id),928 ajax_data = {929 lifetime : lifetime_id,930 idlist : idlist_s,931 },932 load_dicts_waiter = requestor.waiterRequest({933 request_id : request_id,934 title : 'Загрузка параметров приложения: '+idlist_ss,935 method : ajax_method,936 url : ajax_url,937 data : ajax_data,938 }),939 DONE940 ;941 load_dicts_waiter942 .then(function(data){943 // Если очередь загружающихся ресурсов пуста и всё ещё ждём... -- ???944 if ( !--app.load_waiting && load_waiting_waiter_id ) {945 // ...завершаем ожидание.946 waiter.finish(load_waiting_waiter_id);947 }948 if ( typeof on_success === 'function' ) {949 on_success(data);950 }951 })952 .fail(function (error) {953 console.error( 'app_load_dicts_queue error', error, idlist );954 /*DEBUG*//*jshint -W087*/debugger;955 // waiter.finish(waiter_id);956 // app.load_waiting--;957 if ( typeof on_error === 'function' ) {958 on_error(error);959 }960 })961 ;962 return load_dicts_waiter;963 },/*}}}*/964 /** load_dicts(...) {{{ Загружаем данные/словари по списку.965 *966 * @param {String[]} idlist - Список идентификаторов данных/словарей для загрузки967 * @param {Callback} on_success - Выполняем в случае успеха.968 * @param {Callback} on_error - Выполняем в случае ошибки.969 * @param {String} load_waiting_waiter_id - Идентификатор ожидателя для завершения,970 * если счётчик загружаемых ресурсов ({@link app#load_waiting}) равен нулю. Передаётся напрямую в971 * {@link app#load_dicts_queue_request}. Напр., 'app_load_assets_and_dicts'.972 *973 * @returns {Promise}974 *975 */976 load_dicts : function (idlist, on_success, on_error, load_waiting_waiter_id) {977 var app = this,978 params = this.params,979 idlist_s = idlist.join(','),980 idlist_sx = idlist_s.replace(/\W+/g, '_'),981 idlist_ss = idlist.join(', '),982 current_time = Date.now(), // Получаем текущее время (msec)983 // Подготовка данных для колбэка984 /** ** {{{*/get_all_data = function () {985 var all_data = {};986 idlist.forEach(function _idlist_map (dict_id) {987 if ( typeof params.dicts[dict_id] === 'undefined' ) {988 params.dicts[dict_id] = {}; // WTF???989 }990 all_data[dict_id] = params.dicts[dict_id];991 });992 return all_data;993 },/*}}}*/994 undef995 ;996 var load_dicts_promise = new vow.Promise(function _load_dicts_promise (resolve,reject) {997 // Если нечего загружать, возвращаем пустой набор словарей998 if ( !idlist || !Array.isArray(idlist) || !idlist.length ) {999 return resolve({});1000 }1001 idlist.sort();1002 var1003 waiter_id = 'app_load_dicts_' + idlist_s.replace(/,/g, '_') + '_' + current_time,1004 waiter_title = 'Загрузка данных ('+idlist_ss+')',1005 waiter_timeout = 2000,1006 load_queues = {}, // Сюда складываем формируемые очереди (группируем по времени жизни)1007 errors = [], // Накапливаем сообщения об ошибках1008 waiting_dicts_count = 0, // Общее количество словарей для загрузки1009 failed_dicts_count = 0, // Количество незагруженных словарей1010 result_dicts = {}, // Набор словарей для возврата в случае удачного выполнения1011 // Общая функция завершения, -- если все словари/очереди отработаны1012 on_done = function () {1013 // Успешное завершение1014 if ( !errors || !errors.length ) {1015 var all_data = get_all_data();1016 resolve(all_data);1017 typeof on_success === 'function' && on_success(all_data);1018 }1019 // Ошибки1020 else {1021 reject(errors);1022 typeof on_error === 'function' && on_error(errors);1023 }1024 },1025 // Функции обратного вызова для загрузки словарей.1026 // Служат для обслуживания множественных запросов на один и тот же словарь:1027 // если словарь уже загружается, то в его очередь колбеков1028 // (on_success_stack, on_error_stack) добавляется ещё один вызов.1029 //1030 on_dict_success = function on_dict_success (dict_id) {1031 --waiting_dicts_count || on_done();1032 },1033 on_dict_error = function on_dict_error (dict_id, error) {1034 ++failed_dicts_count;1035 var error_text = 'Невозможно загрузить словарь '+dict_id;1036 if ( error ) { error_text += ': '+error; }1037 errors.push(error_text);1038 --waiting_dicts_count || on_done();1039 },1040 // queues_waiting_count = 0,1041 done1042 ;1043 // Формируем очереди запросов для устаревших или отсутствующих данных1044 // в соотв. с типами кеширования1045 Array.isArray(idlist)1046 && idlist.map(function process_load_dicts (dict_id) {1047 var dict_info = params.config.cache._DATA_TYPES[dict_id],1048 on_success_cb,1049 on_error_cb1050 ;1051 if ( !dict_info ) {1052 var error_text = 'Некорректный идентификатор словаря: '+dict_id;1053 console.warn( 'app:load_dicts:map error', error_text );1054 /*DEBUG*//*jshint -W087*/debugger;1055 typeof on_error === 'function' && on_error(error_text);1056 // load_dicts_waiter.Error(error_text);1057 // return false;1058 return reject(error_text);1059 }1060 var1061 type = dict_info.type,1062 loaded_dict = params.loaded_dicts[dict_id],1063 lifetime = dict_info.lifetime,1064 // Таймаут в настройках на сервере хранится в секундах - ???1065 // lifetime_timeout = params.config.cache['lifetime_'+lifetime] * 1000,1066 lifetime_timeout = params.config.cache['lifetime_'+lifetime] || 0,1067 undef1068 ;1069 // Если объекта трассировки словаря нет, то создаём его...1070 if ( typeof loaded_dict === 'undefined' ) {1071 loaded_dict = params.loaded_dicts[dict_id] = {1072 // on_success_stack : [],1073 // on_error_stack : [],1074 };1075 }1076 // ...иначе, если уже загружен, вообще ничего не делаем.1077 else if ( loaded_dict.loaded1078 && ( loaded_dict.time && current_time - loaded_dict.time < lifetime_timeout )1079 ) {1080 return;1081 }1082 // Устанавливаем колбеки для каждого словаря1083 if ( !loaded_dict.on_success_stack ) { loaded_dict.on_success_stack = []; }1084 if ( !loaded_dict.on_error_stack ) { loaded_dict.on_error_stack = []; }1085 typeof on_dict_success === 'function' && loaded_dict.on_success_stack.push(on_dict_success);1086 typeof on_dict_error === 'function' && loaded_dict.on_error_stack.push(on_dict_error);1087 waiting_dicts_count++;1088 // Если уже загружается, то ещё раз не загружаем, ждём чужой загрузки на колбеке1089 if ( loaded_dict.loading ) {1090 return;1091 }1092 // Добавляем в очередь для загрузки1093 if ( typeof load_queues[lifetime] === 'undefined' ) { load_queues[lifetime] = []; }1094 if ( load_queues[lifetime].indexOf(dict_id) === -1 ) {1095 load_queues[lifetime].push(dict_id);1096 loaded_dict.loading = true;1097 }1098 });1099 // Если нечего загружать1100 if ( !waiting_dicts_count ) {1101 return on_done();1102 }1103 var1104 on_success_cb,1105 on_error_cb1106 ;1107 // Загружаем очереди1108 var promises = [];1109 Object.keys(load_queues).map(function(lifetime_id) {1110 var queue = load_queues[lifetime_id],1111 queue_s = queue.join(', ');1112 var queue_promise = app.load_dicts_queue_request(queue, lifetime_id, null, null, load_waiting_waiter_id);1113 promises.push(queue_promise);1114 queue_promise1115 .then(function _on_success (data) {1116 // Обнуляем состояния ожидания и сохраняем данные1117 for ( var dict_id in data ) {1118 if ( data.hasOwnProperty(dict_id) ) {1119 var loaded_dict = params.loaded_dicts[dict_id];1120 if ( typeof loaded_dict !== 'undefined' && loaded_dict ) {1121 params.dicts[dict_id] = data[dict_id] || {};1122 loaded_dict.time = current_time;1123 loaded_dict.loaded = true;1124 delete loaded_dict.loading;1125 if ( loaded_dict.on_success_stack ) {1126 while ( on_success_cb = loaded_dict.on_success_stack.shift() ) {1127 on_success_cb(dict_id);1128 }1129 }1130 delete loaded_dict.on_success_stack;1131 delete loaded_dict.on_error_stack;1132 }1133 }1134 }1135 })1136 .fail(function _on_error (error) {1137 // Обнуляем состояния ожидания1138 for ( var n=0; n<queue.length; n++ ) {1139 var dict_id = queue[n];1140 var loaded_dict = params.loaded_dicts[dict_id];1141 if ( typeof loaded_dict !== 'undefined' && loaded_dict ) {1142 delete loaded_dict.loading;1143 if ( loaded_dict.on_error_stack ) {1144 while ( on_error_cb = loaded_dict.on_error_stack.shift() ) {1145 on_error_cb(dict_id, error);1146 }1147 }1148 delete loaded_dict.on_success_stack;1149 delete loaded_dict.on_error_stack;1150 }1151 }1152 })1153 ;1154 });1155 });1156 return load_dicts_promise;1157 },/*}}}*/1158 /** get_loaded_assets_list() ** {{{ Список загруженных ранее данных1159 * @return {array}1160 */1161 get_loaded_assets_list : function () {1162 return Object.keys(this.params.loaded_assets);1163 },/*}}}*/1164 /** get_stored_assets_list() ** {{{ Список загруженных и сохранённых данных1165 * @return {array}1166 */1167 get_stored_assets_list : function () {1168 return Object.keys(this.params.assets_data);1169 },/*}}}*/1170 /** get_stored_dicts_list() ** {{{ Список загруженных ранее словарей1171 * @return {array}1172 */1173 get_stored_dicts_list : function () {1174 return Object.keys(this.params.dicts);1175 },/*}}}*/1176 /** get_stored_dict_data() ** {{{ Данные загруженного ранее словаря1177 * @param {string} dict_id1178 * @return {*}1179 */1180 get_stored_dict_data : function (dict_id) {1181 return this.params.dicts[dict_id];1182 },/*}}}*/1183 /** get_stored_asset_data() ** {{{ Данные загруженного ранее словаря1184 * @param {string} asset_id1185 * @return {*}1186 */1187 get_stored_asset_data : function (asset_id) {1188 return this.params.assets_data[asset_id];1189 },/*}}}*/1190 /** is_asset_loaded() ** {{{ Проверка состояния загруженности элемента ресурсов (данных).1191 * @param {String} asset_id Идентификатор ресурса1192 * @return {Boolean}1193 */1194 is_asset_loaded : function (asset_id) {1195 var app = this,1196 params = this.params;1197 return params.loaded_assets[asset_id] && params.loaded_assets[asset_id].loaded || false;1198 },/*}}}*/1199 /** is_dict_loaded() ** {{{ Проверка состояния згруженности словаря.1200 * @param {String} dict_id Идентификатор словаря.1201 * @returns {Boolean}1202 */1203 is_dict_loaded : function (dict_id) {1204 var app = this,1205 params = this.params;1206 return params.loaded_dicts[dict_id] && params.loaded_dicts[dict_id].loaded || false;1207 },/*}}}*/1208 /** callback_dicts() ** {{{ Колбэк для словарей.1209 * @param {String[]|String} idlist - Идентификаторы (идентификатор) необходимых словарей1210 * @param {Callback} [on_success] - Колбэк для случая, если словари присутствуют или загружены.1211 * @param {Callback} [on_error] - Колбэк для ошибки загрузки.1212 * @param {String} [waiter_id] - Идентификатор ожидателя для завершения.1213 * @return none1214 */1215 callback_dicts : function (idlist, on_success, on_error, waiter_id) {1216 var app = this;1217 var params = this.params;1218 // Функция-колбэк успешного завершения (успешная загрузка или данные уже готовы)1219 // -- подготавливаем требуемые данные (словари) и вызываем пользовательский колбек (`on_success`), если задан.1220 // Если передан идентификатор (`waiter_id`, завершаем ожидатель.1221 var apply_promise = function apply_promise () {1222 if ( typeof on_success === 'function' ) {1223 var all_data = {},1224 apply_data = idlist.map(function idlist_map (dict_id) {1225 if ( typeof params.dicts[dict_id] === 'undefined' ) {1226 params.dicts[dict_id] = {}; // WTF???1227 }1228 all_data[dict_id] = params.dicts[dict_id];1229 return params.dicts[dict_id];1230 });1231 apply_data.unshift(all_data);1232 on_success.apply(this, apply_data);1233 }1234 typeof waiter_id !== 'undefined' && waiter.finish(waiter_id);1235 };1236 if ( typeof idlist === 'string' ) { idlist = [ idlist ]; }1237 if ( !Array.isArray(idlist) ) {1238 typeof on_error === 'function' && on_error('app.callback_dicts(): Параметр `idlist` должен быть списком.');1239 typeof waiter_id !== 'undefined' && waiter.finish(waiter_id);1240 return false;1241 }1242 var not_loaded_count = 0;1243 idlist.forEach(function process_load_dicts (dict_id) {1244 if ( !app.is_dict_loaded(dict_id) ) {1245 not_loaded_count++;1246 }1247 });1248 if ( !not_loaded_count ) {1249 apply_promise();1250 }1251 else {1252 app.load_dicts(idlist, function _on_success () {1253 apply_promise();1254 }, function _on_error (status) {1255 typeof on_error === 'function' && on_error(status);1256 typeof waiter_id !== 'undefined' && waiter.finish(waiter_id);1257 });//, 'app_load_assets_and_dicts');1258 }1259 },/*}}}*/1260 /** resolve_dicts() ** {{{ Получаем данные/словари с проверкой загруженности через промис1261 * @param {string[]} dicts_list - Список словарей для загрузки1262 * @return {Promise} promise1263 */1264 resolve_dicts : function (dicts_list) {1265 var promise = this.load_dicts(dicts_list);1266 return promise;1267 },/*}}}*/1268 /** load_assets() {{{ Загружаем ресурсы1269 *1270 * @param {Object[]} assets - Список описаний ресурсов к загрузке.1271 * @param {Callback} [on_success] - Обратный вызов в случае успеха.1272 * @param {Callback} [on_error] - Обратный вызов в случае ошибки.1273 *1274 * @returns {waiter/Promise}1275 *1276 * TODO 2016.10.10, 14:23 -- Возвращать данные в callback/promise?1277 *1278 */1279 load_assets : function (assets, on_success, on_error) {1280 var app = this,1281 params = this.params;1282 if ( typeof assets === 'undefined' || !Array.isArray(assets) || !assets.length ) {1283 typeof on_success === 'function' && on_success();1284 return vow.Promise.resolve({ status : 'no assets to load' });1285 }1286 var expected_count = 0,1287 assets_to_load = assets,1288 errors_count = 0,1289 errors = [],1290 current_time = Date.now(),1291 waiter_id = 'app_load_assets_' + current_time,1292 waiter_title = 'Загрузка ресурсов',1293 waiter_timeout = 2000,1294 load_assets_waiter = waiter.start(waiter_id, { // Запускаем ожидатель1295 title : waiter_title,1296 timeout : waiter_timeout,1297 }),1298 on_done = function on_done (asset) {1299 // TODO 2016.10.10, 14:27 -- Создавать набор данных для передачи в callback/promise?1300 if ( !errors_count ) {1301 typeof on_success === 'function' && on_success();1302 return load_assets_waiter.Done();1303 }1304 else {1305 typeof on_error === 'function' && on_error(errors);1306 return load_assets_waiter.Error(errors);1307 }1308 // waiter.finish(waiter_id);1309 }1310 ;1311 // Фильтруем ресурсы (только для запуска в режиме разработки на enb-сервере1312 if ( app.getMod('emulate') ) {1313 assets_to_load = [];1314 assets.forEach(function filter_assets_to_load (asset) {1315 // Если эмуляция в bem/enb, то "свои" пакеты не загружаем1316 if ( asset.type === 'package' && asset.name === params.emulateId ) {1317 return vow.Promise.resolve({ status : 'skip base page assets in emulate mode' });1318 }1319 // Иначе добавляем в очередь1320 assets_to_load.push(asset);1321 });1322 }1323 // Заранее (!) вычисляем количество ресурсов для загрузки1324 expected_count = assets_to_load.length;1325 assets_to_load.forEach(function process_load_assets (asset) {1326 // Если эмуляция в bem/enb, то "свои" пакеты не загружаем1327 if ( app.getMod('emulate') && asset.type === 'package' && asset.name === params.emulateId ) {1328 return 'emulate: ignore';1329 }1330 // Инициируем загрузку ресурса1331 app.load_asset(asset,1332 function on_asset_success () {1333 --expected_count || on_done(asset);1334 },1335 function on_asset_error (error) {1336 errors_count++;1337 if ( error ) {1338 var new_errors = Array.isArray(error) ? error : [ error ];1339 new_errors.forEach(function(error) {1340 errors.push(error);1341 });1342 }1343 --expected_count || on_done(asset);1344 }1345 );1346 });1347 return load_assets_waiter;1348 },/*}}}*/1349 /** load_page_assets() {{{ Загружаем все ресурсы для текущей страницы.1350 *1351 * @param {String} id - Идентификатор страницы1352 *1353 */1354 load_page_assets : function (id) {1355 var app = this,1356 params = this.params,1357 pageData = params.config.appdata.pages[id];1358 if ( pageData && pageData.required && Array.isArray(pageData.required.assets) ) {1359 return app.load_assets(pageData.required.assets,1360 function _on_success() {1361 },1362 function _on_error (error) {1363 var new_errors = Array.isArray(error) ? error : [ error ];1364 new_errors.forEach(function(error) {1365 app.load_errors.push(error);1366 app.error(error);1367 });1368 }1369 );1370 }1371 return null;1372 },/*}}}*/1373 /** callback_assets() ** {{{ Загружаем ресурсов с обратными вызовами.1374 * @param {Object[]} assets_list - Список объектов описателей запросов для загрузки.1375 * @param {Callback} on_success - Колбэк для случая, если данные присутствуют или загружены.1376 * @param {Callback} on_error - Колбек для ошибки загрузки.1377 */1378 callback_assets : function (assets_list, on_success, on_error) {1379 var app = this;1380 var params = this.params;1381 if ( !Array.isArray(assets_list) ) {1382 if ( typeof assets_list === 'undefined' ) {1383 assets_list = [];1384 }1385 else {1386 assets_list = [ assets_list ];1387 }1388 // typeof on_error === 'function' && on_error('app.callback_assets(): Параметр `assets_list` должен быть определён.');1389 // return false;1390 }1391 // // TODO 2016.08.17, 13:36 -- ??? Проверять таймаут?1392 // var not_loaded_count = 0;1393 // assets_list.forEach(function process_load_data (asset) {1394 // var id = asset.id || 'data:' + asset.url;1395 // if ( !app.is_asset_loaded(id) ) {1396 // not_loaded_count++;1397 // }1398 // });1399 var apply_success_data = function apply_success_data () {1400 if ( typeof on_success === 'function' ) {1401 var all_data = {},1402 apply_data = assets_list.map(function assets_list_map (asset) {1403 var id = asset.id || 'data:' + asset.url;1404 if ( typeof params.assets_data[id] === 'undefined' ) {1405 params.assets_data[id] = {}; // WTF???1406 }1407 all_data[id] = params.assets_data[id];1408 return params.assets_data[id];1409 });1410 apply_data.unshift(all_data);1411 on_success.apply(this, apply_data);1412 }1413 };1414 // if ( !not_loaded_count ) {1415 // apply_success_data();1416 // }1417 // else {1418 app.load_assets(assets_list, function load_assets_apply_promise () {1419 apply_success_data();1420 }, on_error);1421 // }1422 },/*}}}*/1423 /** resolve_assets() ** {{{ Загружаем ресурсы с отработкой через Promise1424 *1425 * @param {Object[]} assets_list - Список объектов описателей запросов для загрузки.1426 *1427 * Пример описания одного ресурса:1428 *1429 * ```1430 * {1431 * id : 'data:test_data',1432 * url : '{{approot}}core/js/otchet/test_data.json',1433 * method : 'GET',1434 * expires : app.config.cache.lifetime_long,1435 * }1436 * ```1437 * @param {string} assets_list[].id - Идентификатор ресурса. По этому ключу элемент данных можно найти в общем хранилище, если ресурс сохраняемый (данные).1438 * @param {string} assets_list[].url - Адрес ресурса.1439 * @param {string} assets_list[].method - HTTP-метод запроса (GET, POST).1440 * @param {number} assets_list[].expires - Таймаут времени актуальности ресурса (времени жизни), мс. См. предопределённые значения в {@link project__config} (`app.config.cache.lifetime_*`).1441 *1442 * @return {Promise} promise1443 */1444 resolve_assets : function (assets_list) {1445 var app = this,1446 params = this.params,1447 promise = new vow.Promise(function (resolve, reject) {1448 app.callback_assets(assets_list,1449 function _on_success (data) {1450 resolve(data);1451 },1452 function _on_error (error) {1453 reject(error);1454 }1455 );1456 })1457 ;1458 return promise;1459 },/*}}}*/1460 /** load_page_dicts() {{{ Загружаем все ресурсы для текущей страницы.1461 *1462 * @param {String} id - Идентификатор страницы1463 *1464 * @returns {Promise|null}1465 *1466 */1467 load_page_dicts : function (id) {1468 var app = this,1469 params = this.params,1470 pageData = params.config.appdata.pages[id];1471 if ( pageData && pageData.required && Array.isArray(pageData.required.dicts) ) {1472 return app.load_dicts(pageData.required.dicts, function success () {1473 // done1474 }, function error (error) {1475 app.load_errors.push(error);1476 app.error(error);1477 },1478 'app_load_assets_and_dicts'1479 );1480 }1481 return null;1482 },/*}}}*/1483 /** release_resources() ** {{{ Освобождаем ресурсы1484 *1485 * @param {String[]} list - Список идентификаторов вида: `{data:<data_id>|dict:<dict_id>|<dict_id>}`.1486 */1487 release_resources : function (list) {1488 var app = this;1489 var params = this.params;1490 Array.isArray(list) && list.forEach(function release_resource_item (id) {1491 // Если `data:*`, удаляем ресурс...1492 if ( id.startsWith('data:') ) {1493 if ( params.loaded_assets[id] && params.loaded_assets[id].loaded ) {1494 delete params.assets_data[id];1495 // delete params.loaded_assets[id];1496 params.loaded_assets[id].loaded = false;1497 }1498 }1499 // Если явное указание `dict:*` ...1500 if ( id.startsWith('dict:') ) {1501 id = id.substr('dict:'.length);1502 }1503 // ...Или идентификатор, то удаляем словарь...1504 if ( id.match(/^\w+$/) ) {1505 if ( params.loaded_dicts[id] && params.loaded_dicts[id].loaded ) {1506 delete params.dicts[id];1507 // delete params.loaded_dicts[id];1508 params.loaded_dicts[id].loaded = false;1509 }1510 }1511 });1512 },/*}}}*/1513 /** _closePage() ** {{{ Сбрасываем текущую страницу.1514 * @param {boolean} [clearEmulate=false] - Очищать статус эмулирования bemhtml контента (для enb)1515 * @returns {Promise}1516 * Сбрасываем все (возможные?) данные (?), вызываем обработчики на закрытие.1517 */1518 _closePage : function (clearEmulate)1519 {1520 var app = this,1521 params = this.params,1522 pageId = params.pageId,1523 undef1524 ;1525 // Очищаем статус эмулирования bemhtml контента (для enb)1526 if ( clearEmulate && app.getMod('emulate') ) {1527 app.delMod('emulate');1528 }1529 // Если нет открытой страницы1530 if ( pageId ) {1531 if ( typeof app.on_page_close_callbacks !== 'undefined'1532 && Array.isArray(app.on_page_close_callbacks)1533 && app.on_page_close_callbacks.length ) {1534 // TODO 2017.03.16, 21:40 -- Накапливать Promises?1535 var callback;1536 while ( callback = app.on_page_close_callbacks.shift() ) {1537 callback();1538 }1539 }1540 // Удаляем зарегистрированные поллеры на закрытие страницы1541 app.before_page_close_pollers = [];1542 // Удаляем зарегистрированные каналы событий1543 for ( var channel_id in this.registered_channel_events ) {1544 if ( this.registered_channel_events.hasOwnProperty(channel_id) ) {1545 var event_id;1546 while ( event_id = this.registered_channel_events[channel_id].shift() ) {1547 channels(channel_id).un(event_id);//, callback);1548 }1549 delete this.registered_channel_events[channel_id];1550 }1551 }1552 delete params.pageId;1553 delete params.pageData;1554 app.delMod('pageId');1555 return vow.Promise.resolve({ status : 'pageClosed', pageId : pageId, description : 'Страница закрыта' });1556 }1557 // TODO: Создавать событие?1558 return vow.Promise.resolve({ status : 'noPageToClose', description : 'Нет открытой страницы' });1559 },/*}}}*/1560 /** _clearPageContainer() ** {{{ Очищаем контент старой страницы. Технические синхронные процедуры.1561 * Вызывается после {@link #_closePage} (?) pageId не проверяем.1562 *1563 */1564 _clearPageContainer : function () {1565 var app = this;1566 // Если не эмулируем создание bemhtml контента в enb1567 if ( !app.getMod('emulate') ) {1568 // Удаляем "висячие" объекты в корне (выбор дат)1569 // См.1570 // WEB_TINTS/source/blocks/libs/datetimepicker/datetimepicker.js1571 $('.xdsoft_datetimepicker_control').map(function(n,item){1572 $(item).datetimepicker('destroy');1573 });1574 // $('body > .xdsoft_datetimepicker').remove();1575 var container = $('.app > .app__container');1576 if ( container && container.length ) {1577 BEMDOM.destruct(container, true);1578 }1579 }1580 return vow.Promise.resolve({ status : 'containerCleared', description : 'Контейнер очищен' });1581 },/*}}}*/1582 /** registerPageReadyAction() ** {{{ Зарегистрировать действие на открытие страницы.1583 * @param {Callback} callback - Обратный вызов функции на открытие страницы.1584 * TODO: Failback на случай, если метод вызывается после того, как страница уже была готова?1585 */1586 registerPageReadyAction : function (callback) {1587 var app = this;1588 app.register_channel_event('app', 'pageReady', callback, true); // once-event1589 },/*}}}*/1590 /** register_close_action() ** {{{ Зарегистрировать действие на закрытие страницы.1591 *1592 * @param {Callback} callback - Обратный вызов функции на закрытие.1593 *1594 */1595 register_close_action : function (callback) {1596 var app = this,1597 params = this.params;1598 app.on_page_close_callbacks.push(callback);1599 },/*}}}*/1600 /** poll_before_close() ** {{{ Зарегистрировать хэндлер на опрос перед закрытием страницы.1601 *1602 * @param {Callback} close_promise - Промис закрытия.1603 *1604 */1605 poll_before_close : function (close_promise) {1606 var app = this,1607 params = this.params;1608 if ( !this.before_page_close_pollers || !this.before_page_close_pollers.length ) {1609 return close_promise.resolve('no close pollers');1610 }1611 var defers_list = [];1612 this.before_page_close_pollers.forEach(function(poll_callback, n) {1613 if ( typeof poll_callback !== 'function' ) { return; }1614 var defer = vow.defer();1615 defers_list.push(defer.promise());1616 poll_callback(defer);1617 });1618 vow.all(defers_list)1619 .then(function () {1620 close_promise.resolve('all pollers allow close');1621 })1622 .fail(function (reason) {1623 close_promise.reject(reason);1624 })1625 ;1626 /*1627 for ( var i in app.before_page_close_pollers ) {1628 var poll_callback = app.before_page_close_pollers[i];1629 // Не функция? WTF?1630 if ( typeof poll_callback !== 'function' ) {1631 poll_callback_done();1632 continue;1633 }1634 poll_callback(poll_callback_done, poll_callback_cancel);1635 }1636 */1637 },/*}}}*/1638 /** register_close_poller() ** {{{ Зарегистрировать хэндлер на опрос перед закрытием страницы.1639 *1640 * @param {Callback} callback - Колбек поллера (параметром передаётся два колбека: (1) разрешение на закрытие, (2) запрет).1641 *1642 */1643 register_close_poller : function (poll_callback) {1644 var app = this,1645 params = this.params;1646 app.before_page_close_pollers.push(poll_callback);1647 },/*}}}*/1648 /** screenholderAnimationDone() ** {{{ Промис на завершение анимации заставки экрана1649 * @returns {Promise}1650 */1651 screenholderAnimationDone : function () {1652 var app = this,1653 that = this,1654 params = this.params,1655 undef1656 ;1657 if ( app.screenholder.status === 'show:animation' ) {1658 var promise = new vow.Promise(function(resolve,reject){1659 app.screenholder.registerShowCallback(function(){1660 return resolve({ status : 'animation done' });1661 });1662 });1663 return promise;1664 }1665 else {1666 return vow.Promise.resolve({ status : 'no animation' });1667 }1668 },/*}}}*/1669 /** _openPageStart() ** {{{ Подготовка открытия страницы (Шаг 0, sync)1670 *1671 * @param {string} id - Идентификатор страницы1672 * @returns {boolean}1673 *1674 * Проверка на возможность открытия страницы.1675 * (Только в sync режиме! Все async действия -- в `openPage()`).1676 *1677 * Проверяет и изменяет свойства `params`:1678 * - params.openingPage1679 * - params.openingPageId1680 * - params.queuedPageId1681 *1682 * Устанавливает заставку в режим ожидания.1683 *1684 * Если открытие страницы невозможно, возвращает false1685 *1686 * TODO 2017.04.07, 15:59 -- Возвращать Promise?1687 *1688 */1689 _openPageStart : function (id) {1690 var app = this,1691 that = this,1692 params = this.params,1693 undef1694 ;1695 // Проверка: если страница открывается в данный момент...1696 if ( params.openingPage ) {1697 // Если открывается не та же страница, то запоминаем id1698 if ( params.openingPageId !== id ) {1699 params.queuedPageId = id;1700 }1701 return false;1702 }1703 // Иначе устанавливаем флаг "страница загружается".1704 params.openingPage = true;1705 // И сбрасываем "следующую" страницу.1706 delete params.queuedPageId;1707 // window.location.hash = '';1708 // app._clearLocationHash();1709 // Проверка на непустой идентификатор страницы1710 if ( !id ) {1711 app.openPageError('Ошибка подготовки открытия экрана - не задан идентификатор страницы');1712 return false;1713 }1714 // Проверка на наличие страницы1715 if ( ( params.config && params.config.appdatat && !params.config.appdata.pages[id] ) && !this._isSpecialPageId(id) ) {1716 app.openPageError('Ошибка подготовки открытия экрана (<u>'+id+'</u>) - описание страницы не найдено');1717 return false;1718 }1719 // Сохраняем ID открываемой страницы (позже станет `pageId`).1720 params.openingPageId = id;1721 // Устанавливаем режим ожидания.1722 app.screenholder.waiting(); // XXX 2017.04.07, 15:50 -- Ср. appholder?1723 // Подтверждаем возможность открытия страницы.1724 return true;1725 },/*}}}*/1726 /** _openPageLoadResources(id) ** {{{ Загружаем ресурсы из зависимостей страницы (Шаг 1, async)1727 * @param {string} id - Идентификатор страницы1728 * @returns {Promise}1729 */1730 _openPageLoadResources : function (id) {1731 var app = this,1732 that = this,1733 params = this.params,1734 promises_list = [1735 app.load_page_assets(id),1736 app.load_page_dicts(id),1737 ],1738 promise = vow.all(promises_list),1739 undef1740 ;1741 promise1742 .fail(function(error){1743 console.error(error);1744 /*DEBUG*//*jshint -W087*/debugger;1745 })1746 ;1747 return promise;1748 },/*}}}*/1749 /** _openPagePrepare(id) ** {{{ Подготавливаем окружение для загрузки страницы. (Шаг 2, async)1750 *1751 * Вызывается из `_openPageRender`1752 *1753 * @param {String} id Идентификатор страницы1754 *1755 */1756 _openPagePrepare : function (id)1757 {1758 var app = this,1759 that = this,1760 params = this.params,1761 undef1762 ;1763 var promise = new vow.Promise(function(resolve,reject){1764 var errorText;1765 if ( !params.config || !params.config.appdata || !params.config.appdata.pages ) {1766 errorText = 'Не задана конфигурация страниц (config.appdata.pages)';1767 }1768 else if ( !params.config.appdata.pages[id] ) {1769 errorText = 'Не найдено описание страницы <u>'+id+'</u>';1770 }1771 if ( errorText ) {1772 console.error( '_openPagePrepare error', errorText );1773 /*DEBUG*//*jshint -W087*/debugger;1774 return reject({ error : errorText });1775 }1776 var pageData = params.config.appdata.pages[id];1777 // // Очищаем контейнер страницы1778 // app._clearPageContainer();1779 // Устанавливаем параметры страницы1780 params.pageId = id;1781 params.pageData = pageData;1782 store.set('appPageId', id);1783 // app.setMod('pageId', app.params.pageId);1784 app.domElem.attr('id', app.params.pageId);1785 // Если указано, устанавливаем дополнительный waiter для отработки в самой странице1786 if ( params.pageData.setPageReadyWaiter ) {1787 waiter.start('pageReady', {1788 title : 'Готовность страницы',1789 timeout : app.params.asset_load_timeout,1790 timeout_break : true,1791 on_timeout : function () {1792 // app.screenholder.error('Отсутствие кода завершения инициализации экрана.');1793 // waiter.finish('pageReady');1794 app.error('Отсутствие подтверждения готовности экрана ('+id+').');1795 },1796 on_finish : function () {1797 app.screenholder.ready();1798 },1799 });1800 }1801 resolve( { status : 'pagePrepared', pageId : id, description : 'Подготовлено открытие страницы' });1802 });1803 return promise;1804 },/*}}}*/1805 /** _openPageRender(id) ** {{{ Отображаем страницу не экране (Шаг 3, async)1806 * @param {string} id - Идентификатор страницы1807 * @returns {Promise}1808 */1809 _openPageRender : function (id) {1810 var app = this,1811 that = this,1812 params = this.params,1813 pageData = params.config.appdata.pages[id],1814 undef1815 ;1816 var promise = new vow.Promise(function(resolve,reject){1817 // Дёргаем инициализатор -- для свежеподгруженных модулей с классами блоков1818 modules.require([1819 'i-bem-dom__init',1820 ], function _render_require_ () {1821 var asset_id, html, error_text, container, dom;1822 // {{{ Вариант 0: Эмуляция (enb-сервер) -- ничего не делаем (контент уже готов)...1823 if ( app.getMod('emulate') ) {1824 // app.init_page();1825 return resolve({ status : 'pageRenderEmulate', pageId : id, description : 'Страница не отрисовывалась - режим эмуляции' });1826 }// }}}1827 // {{{ Вариант 1: Выводим статический контент...1828 else if ( typeof pageData.open.html !== 'undefined' ) {1829 // ??? === 'html' || pageData.open.method === 'html_asset' )1830 html = pageData.open.html;1831 if ( typeof html === 'string' && html.startsWith('data:') ) {1832 asset_id = html;1833 html = params.assets_data[asset_id];1834 if ( !html ) {1835 error_text = 'Ошибка открытия экрана <u>'+id+'</u>: Не найден элемент данных "<u>'+asset_id+'</u>" (статика)';1836 console.error( error_text, app );1837 app.load_errors.push(error_text);1838 return reject({ error : error_text });1839 }1840 }1841 else if ( typeof html === 'object' && Array.isArray(html) ) {1842 html = html.join('\n');1843 }1844 if ( !html ) {1845 error_text = 'Ошибка открытия экрана <u>'+id+'</u>: не верно задан шаблон содержимого "<u>'+id+'</u>" (статика)';1846 console.error( error_text, app );1847 app.load_errors.push(error_text);1848 return reject({ error : error_text });1849 }1850 else {1851 container = $('.app > .app__container');1852 dom = BEMDOM.update(container, html);1853 // Инициализируем параметры для созданной страницы (???)1854 return resolve({ status : 'pageRenderedFromStatic', pageId : id, description : 'Страница отрисована из статики' });1855 }1856 }// }}}1857 // {{{ Вариант 2: Разворачиваем из шаблона...1858 else if ( typeof pageData.open.bemhtml !== 'undefined' ) {1859 // ??? ... pageData.open.method === 'bemhtml' || pageData.open.method === 'bemhtml_asset' )1860 var bemhtml = pageData.open.bemhtml;1861 if ( typeof bemhtml === 'string' && bemhtml.startsWith('data:') ) {1862 asset_id = bemhtml;1863 bemhtml = params.assets_data[bemhtml];1864 if ( !bemhtml ) {1865 error_text = 'Ошибка открытия экрана <u>'+id+'</u>: Не найден элемент данных "<u>'+asset_id+'</u>" (шаблон)';1866 app.load_errors.push(error_text);1867 return reject({ error : error_text });1868 }1869 }1870 if ( !bemhtml ) {1871 error_text = 'Ошибка открытия экрана <u>'+id+'</u>: Не верно задан шаблон содержимого "<u>'+asset_id+'</u>" (шаблон)';1872 app.load_errors.push(error_text);1873 return reject({ error : error_text });1874 }1875 else {1876 // Если эмулируем (в enb), то шаблон не загружаем1877 if ( !app.getMod('emulate') ) {1878 // Разворачиваем шаблон1879 html = BEMHTML.apply(bemhtml);1880 container = $('.app > .app__container');1881 dom = BEMDOM.update(container, html);1882 }1883 // Инициализируем параметры для созданной страницы (???)1884 return resolve({ status : 'pageRenderedFromTemplate', pageId : id, description : 'Страница отрисована из шаблона' });1885 }1886 }// }}}1887 // Вариант X: Ошибка...1888 return reject({ error : 'Ошибка открытия экрана <u>'+id+'</u> - не задан шаблон содержимого' });1889 });1890 });1891 return promise;1892 },/*}}}*/1893 /** _openPageInit(id) ** {{{ Инициализируем новую страницу (Шаг 4, async)1894 * @param {string} id - Идентификатор страницы1895 * return {Promise}1896 */1897 _openPageInit : function (id) {1898 var app = this,1899 that = this,1900 params = this.params,1901 pageData = params.config.appdata.pages[id],1902 undef1903 ;1904 var promise = new vow.Promise(function(resolve,reject){1905 try {1906 // Если указано, освобождаем ресурсы после загрузки...1907 if ( pageData.release_on_init ) {1908 app.release_resources(pageData.release_on_init);1909 }1910 document.title = pageData.title || id;1911 if ( ( project.config.LOCAL_DEV || project.config.LOCAL_ENB ) && pageData.title) {1912 document.title = '#'+id+' - '+pageData.title;1913 }1914 channels('app').emit('pageReady', { pageId : id });1915 return resolve({ status : '_openPageInit ok', pageId : id });1916 }1917 catch (error) {1918 console.error('_openPageInit error', error);1919 /*DEBUG*//*jshint -W087*/debugger;1920 return reject('Ошибка инициализации страницы ('+id+') - Ошибка кода выполнения: '+error);1921 }1922 });1923 return promise;1924 },/*}}}*/1925 /** _openPageDone() ** {{{ Успешное завершение открытия страницы (sync)1926 *1927 */1928 _openPageDone : function () {1929 var app = this,1930 that = this,1931 params = this.params,1932 undef1933 ;1934 try {1935 if ( !params.pageData ) {1936 return vow.Promise.reject({ error : 'Не определен набор данных описания страницы (pageData) для '+params.openingPageId });1937 }1938 // Если ещё ждём, то снимаем ожидатель открытия страницы1939 if ( waiter.is_waiting('open_page_'+params.openingPageId) ) {1940 waiter.finish('open_page_'+params.openingPageId);1941 }1942 // Снимаем заставку1943 if ( !params.pageData.setPageReadyWaiter ) {1944 app.screenholder.ready();1945 }1946 // Снимаем флаг и идентификатор открытия страницы1947 delete params.openingPageId;1948 delete params.openingPage;1949 // this.setMod('pageId', app.params.pageId);1950 // params.queuedPageId // ??? Циклический (повторный?) запуск открытия страницы, если установлена следующая страниа на открытие?1951 }1952 catch (error) {1953 console.error(error);1954 /*DEBUG*//*jshint -W087*/debugger;1955 }1956 return { status : '_openPageDone ok' };1957 },/*}}}*/1958 /** openPageError() ** {{{ Аварийное Завершение открытия страницы (sync)1959 * @param {string|array} error - Сообщение об ошибке (массив сообщений)1960 */1961 openPageError : function (error) {1962 var app = this,1963 that = this,1964 params = this.params,1965 undef1966 ;1967 // Если ещё ждём, то снимаем ожидатель открытия страницы (с ошибкой)1968 if ( waiter.is_waiting('open_page_'+params.openingPageId) ) {1969 waiter.finish('open_page_'+params.openingPageId, error);1970 }1971 // // Устанавливаем ошибку (делается на финальном отлове reject)1972 // app.error(error);1973 // Снимаем флаг и идентификатор открытия страницы1974 delete params.openingPageId;1975 delete params.openingPage;1976 // params.queuedPageId // ???1977 },/*}}}*/1978 /** _doOpenPage() ** {{{ Открываем страницу (версия II, Promises)1979 * @param {string} id - Идентификатор страницы1980 * @returns {Promise}1981 *1982 * Вызывается из `openPage`1983 *1984 */1985 _doOpenPage : function (id) {1986 // ПРОВЕРКА: Если страницу невозможно сейчас открыть...1987 if ( !this._openPageStart(id) ) {1988 // Возвращаем null (на случай Promise.cast etc)1989 return null; // ??? Promise?1990 }1991 if ( this._isSpecialPageId(id) ) {1992 this.showSpecialPage(id);1993 return { status : 'showSpecialPage', description : 'Показана спецстраница '+id };1994 }1995 var app = this,1996 that = this,1997 params = this.params,1998 waiter_timeout = 30000,1999 open_page_waiter = waiter.start('open_page_'+id, {2000 title : 'Открытие страницы '+id,2001 waiter_timeout : waiter_timeout,2002 }),2003 undef2004 ;2005 var promise = app._closePage()2006 .then(function(data){2007 // Очищаем контейнер2008 return app._clearPageContainer();2009 })2010 // Загружаем ресурсы и ждём завершения анимации заставки...2011 .then(function(data){2012 return vow.all([2013 app._openPageLoadResources(id),2014 app.screenholderAnimationDone(), // ??? Возможно, надо предусматривать ожидание appholder?2015 ]);2016 })2017 // Подготавливаем страницу...2018 .spread(function(loadResult,animationResult){2019 return app._openPagePrepare(id);2020 })2021 // Отрисовываем...2022 .then(function(data){2023 return app._openPageRender(id);2024 })2025 // Инициализируем...2026 .then(function(data){2027 return app._openPageInit(id);2028 })2029 // Завершаем...2030 .then(function(data){2031 return app._openPageDone(id);2032 })2033 .fail(function(data){2034 console.error( '_doOpenPage fail', promise, data );2035 /*DEBUG*//*jshint -W087*/debugger;2036 app.openPageError(data);2037 return vow.Promise.reject(data);2038 })2039 ;2040 return promise;2041 // return open_page_waiter;2042 },/*}}}*/2043 /** ask_allow_cancel_waiter() ** {{{ Запрос на прерывание текущей операции2044 * @param {Object} promise2045 */2046 ask_allow_cancel_waiter : function (promise) {2047 if ( !waiter.is_waiting() ) {2048 return promise.resolve('no waiters to cancel');2049 }2050 popup_controller.msgbox({2051 options : {2052 title : 'Прервать ожидание?',2053 buttons: [ 'yes', 'no' ], // По умолчанию всегда показывается кнопка "Ok"2054 },2055 content : '<p>Система находится в состоянии ожидания выполнения.'2056 +'<p>Хотите прервать ожидание?',2057 callback : function (id, self) {2058 if ( id === 'yes' ) {2059 waiter.reset(); // TODO 2016.09.16, 16:47 -- Check to clean progressbar items in waiter2060 promise.resolve('waiters cancelled');2061 }2062 else {2063 promise.reject('waiters canceling declined');2064 }2065 },2066 });2067 },/*}}}*/2068 /** openPage(...) ** {{{ Пробуем открыть другую страницу.2069 *2070 * TODO: Отслеживать операции со страницей (открытие и т.д.)?2071 *2072 * @param {String} pageId Идентификатор страницы2073 *2074 */2075 openPage : function (pageId) {2076 var app = this,2077 params = this.params2078 ;2079 // Если какая-то страница загружается. XXX Нужно ли?2080 if ( app.params.loadingPageFlag ) { return false; }2081 // Обрабатываем идентификатор2082 pageId = this._prepareTargetPageId(pageId);2083 // Текст ошибки2084 var errorText = '';2085 // Если не задан идентификатор...2086 if ( !pageId ) {2087 errorText = 'Не задан идентификатор страницы';2088 }2089 // Или если некорректный идентификатор...2090 else if ( !pageId.match(/^\w+$/) ) {2091 errorText = 'Некорректный идентификатор страницы: '+pageId;2092 }2093 // Проверяем и возвращаем ошибку2094 if ( errorText ) {2095 console.error( '_afterInit error', errorText );2096 /*DEBUG*//*jshint -W087*/debugger;2097 return vow.Promise.reject({ error : errorText });2098 }2099 // Снимаем эмуляцию для enb (см. System.app), если указана страница, отличная от эмулированной2100 if ( app.getMod('emulate') && app.params.emulateId !== pageId ) {2101 app.delMod('emulate');2102 delete app.params.emulateId;2103 }2104 // Если есть активная страница, то спрашиваем, можно ли её закрывать,2105 // если да, то сбрасываем её и пробуем открыть новую.2106 var promise;2107 if ( app.params.pageId ) {2108 // try promises here -- 2016.09.16, 15:522109 var cancel_waiting = vow.defer();2110 var can_close = vow.defer();2111 app.ask_allow_cancel_waiter(cancel_waiting);2112 app.poll_before_close(can_close);2113 promise = vow.all([2114 cancel_waiting.promise(),2115 can_close.promise(),2116 ])2117 .then(function (reason) {2118 return app._doOpenPage(pageId);2119 })2120 // .fail(function (reason) {2121 // })2122 ;2123 }2124 // Если страниц нет,2125 else {2126 // ...просто пробуем открыть новую страницу.2127 promise = app._doOpenPage(pageId);2128 }2129 return promise;2130 },/*}}}*/2131 /** changeAuthType ** {{{ Сменить тип авторизации2132 */2133 changeAuthType : function () {2134 try {2135 var app = this,2136 appholder = this._appholder,2137 ajaxUrl = project.config.app_params_url,2138 title = 'Смена типа авторизации...',2139 authTypeChangeRequestParams = {2140 id : 'authTypeChange',2141 title : title,2142 method : 'GET',2143 },2144 authTypeChangeWaiter = null,2145 undef2146 ;2147 appholder.loaderStatusSet(title);2148 var promise =2149 // Показываем заставку приложения2150 appholder.showScreenLoader()2151 // Сбрасываем состояние приложения2152 .then(function(data){2153 authTypeChangeWaiter = waiter.start('authTypeChangeWaiter', {2154 title : title,2155 timeout: 30000,2156 });2157 return app._resetApp();2158 })2159 // Запрашиваем смену авторизации через secureAjax2160 .then(function (data) {2161 return requestor.promiseRequest(authTypeChangeRequestParams);2162 })2163 .then(function (data) {2164 if ( authTypeChangeWaiter && !authTypeChangeWaiter.isFinished() ) {2165 authTypeChangeWaiter.Done(data);2166 }2167 return app._initApp();2168 })2169 // Окончание действий2170 .then(function (data) {2171 appholder.loaderStatusRemove(title);2172 authTypeChangeWaiter && authTypeChangeWaiter.Done(data);2173 })2174 // Ошибка2175 .fail(function (data) {2176 console.error( 'changeAuthType fail', data );2177 /*DEBUG*//*jshint -W087*/debugger;2178 appholder.loaderStatusRemove(title);2179 authTypeChangeWaiter && authTypeChangeWaiter.Error(data);2180 app.error(data);2181 })2182 ;2183 return authTypeChangeWaiter;2184 }2185 catch (data) {2186 console.error(data);2187 /*DEBUG*//*jshint -W087*/debugger;2188 }2189 },/*}}}*/2190 /** _initDom() ** {{{ Разворачиваем внутреннюю структуру контейнера и окружение.2191 *2192 * Инициализируем вложенные и внешние сущности, необходимые для работы SPA/APP.2193 *2194 */2195 _initDom : function () {2196 var app = this,2197 params = this.params,2198 // Внешний DOM-элемент2199 outerVlayout = this._vlayout = this.findParentBlock(BEMDOM.entity('vlayout')),2200 undef2201 ;2202 // Создаём свой контейнер, если отсутствует2203 if ( !this._elem('container') ) {2204 var elemsBemhtml = [2205 { block : 'app', elem : 'container' },2206 { block : 'screenholder' },2207 ];2208 var elemsHtml = BEMHTML.apply(elemsBemhtml);2209 var elemsDom = BEMDOM.append(this.domElem, elemsHtml);2210 }2211 // Если progressbar отсутствует на странице, добавляем его перед нашим блоком2212 // Внимание на первый progressbar в appholder!2213 var progressbarMain = outerVlayout.findChildBlock({ block : BEMDOM.entity('progressbar'), modName : 'main', modVal : true });2214 if ( !progressbarMain ) {2215 var progressbarBemhtml = [2216 { block : 'progressbar', mods : { main : true }, cls : 'vlayout__item_fixed'/* , mods : { hidden : true } */ },2217 ];2218 var progressbarHtml = BEMHTML.apply(progressbarBemhtml);2219 progressbarMain = $(this.domElem).before(progressbarHtml);2220 }2221 this._progressbarMain = progressbarMain;2222 // Находим заставку экрана...2223 //2224 // app.screenholder = app.findChildBlock(screenholder);2225 // workaround: findChildBlock находит первый screenholder,2226 // который может быть внутри контайнера,2227 // если эмулируем и контент уже загружен2228 //2229 var screenholder_dom = $(app.domElem).children('.screenholder');2230 app.screenholder = $(screenholder_dom).bem(BEMDOM.entity('screenholder'));2231 },/*}}}*/2232 /** _initDomEvents ** {{{ События DOM.2233 *2234 */2235 _initDomEvents : function () {2236 var app = this,2237 params = this.params2238 ;2239 // Перехватываем изменение хеша в адресе страницы2240 $(window).on('hashchange', function(e) {2241 if ( app.params.loadingPageFlag ) { return false; } // TODO 2017.03.22, 16:20 -- Сохранять, открвать позже?2242 var pageId = app._getPageIdToOpen();2243 app.delMod('emulate');2244 app.openPage(pageId);2245 });2246 },/*}}}*/2247 /** _acceptAppParams() ** {{{ Получаем и обрабатываем данные конфигурации2248 * @param {object} data - Данные конфигурации2249 */2250 _acceptAppParams : function (data, textStatus, jqXHR) {2251 var app = this,2252 params = this.params2253 ;2254 return new vow.Promise(function (resolve, reject) {2255 // Проверка валидности данных2256 if ( !data || typeof data !== 'object' || typeof data.config !== 'object' ) {2257 var error = 'Получены некорректные данные (app._acceptAppParams)';2258 console.error(error, data);2259 error = [ error, data ];2260 /*DEBUG*//*jshint -W087*/debugger;2261 return reject(error);2262 }2263 var id;2264 // Преобразуем значения задержек из php-представления (секунды) в js (милисекунды)2265 if ( data && data.config && data.config.cache ) {2266 for ( id in data.config.cache ) {2267 if ( data.config.cache.hasOwnProperty(id) ) {2268 id.startsWith('lifetime_') && ( data.config.cache[id] *= 1000 );2269 }2270 }2271 }2272 if ( data && data.config && data.config.token ) {2273 for ( id in data.config.token ) {2274 if ( data.config.token.hasOwnProperty(id) ) {2275 id.endsWith('_time') && ( data.config.token[id] *= 1000 );2276 }2277 }2278 }2279 // Перестраховка на случай реинициализации (удаляем старую конфигурацию)2280 params.config = {};2281 params.user = {};2282 // Сохраняем полученные данные2283 $.extend(true, params, data);2284 app.config = params.config;2285 return resolve(data);2286 });2287 },/*}}}*/2288 /** _loadAppParams() ** {{{ Загружаем конфигурационные данные2289 */2290 _loadAppParams : function () {2291 try {2292 var app = this,2293 that = this,2294 params = this.params,2295 opId = 'app_params',2296 ajaxUrl = project.config.app_params_url,2297 title = 'Загрузка данных приложения...',2298 loaderStatus = this._appholder && this._appholder.loaderStatusSet(title),2299 undef2300 ;2301 var requestParams = {2302 id : opId,2303 title : title,2304 method : 'GET',2305 url : ajaxUrl,2306 };2307 var appParamsPromise;2308 // DEBUG! (Отладочное) Выбор загрузчика -- старый или новый в зависимости от параметра конфигурации.2309 if ( project.config.useAppholder && this._appholder ) {2310 appParamsPromise = requestor.promiseRequest(requestParams);2311 }2312 else {2313 appParamsPromise = request_controller.do_waiter_request(requestParams);2314 }2315 return appParamsPromise2316 .then(function (data) {2317 that._appholder && that._appholder.loaderStatusRemove(title);2318 return that._acceptAppParams(data);2319 })2320 ;2321 }2322 catch (data) {2323 console.error(data);2324 /*DEBUG*//*jshint -W087*/debugger;2325 }2326 },/*}}}*/2327 /** _initDefaultParams() ** {{{ Инициализируем параметры по умолчанию2328 * ??? См. _getDefaultParams2329 */2330 _initDefaultParams : function ()2331 {2332 var app = this,2333 params = this.params2334 ;2335 Object.assign(params, defaultParams);2336 // // Параметры по умолчанию, если не передано в dom-элементе2337 // for ( var param_id in defaultParams ) {2338 // if ( typeof params[param_id] === 'undefined' ) {2339 // params[param_id] = defaultParams[param_id];2340 // }2341 // }2342 },/*}}}*/2343 /** _afterInit() ** {{{ Действия после инициализации (открытие первой страницы)2344 * @param {boolean} [dontForceHash=false] - Не использовать принудительно идентификатор страницы, переданный в url (#hash)2345 * @returns {Promise} - (из openPage)2346 */2347 _afterInit : function (dontForceHash) {2348 var app = this,2349 params = this.params,2350 appdata = app.config.appdata,2351 undef2352 ;2353 return vow.all([2354 // Запускаем обновление сессии2355 app._startRenewSession(),2356 // Запускаем сокеты. TODO: Проверять статус выполнения (Promise)?2357 app._startSocket(),2358 ])2359 .spread(function(sessionResult, socketResult){2360 // ??? TODO: Отлавливать некоторые типы ошибок (авторизация?)2361 if ( socketResult && socketResult.error ) {2362 console.warn( '_afterInit:_startSocket error (socketResult):', socketResult );2363 /*DEBUG*//*jshint -W087*/debugger;2364 }2365 // Запомненная страница или страница по умолчанию2366 var pageId = params.emulateId || store.get('appPageId') || appdata.defaultPage;2367 // Если не указан флаг запрета приоритета2368 // или не найдена страница по умолчанию или сохранённая...2369 if ( ( !pageId || !dontForceHash ) && window.location ) {2370 pageId = app._getPageIdToOpen(pageId);2371 }2372 // Открываем страницу, возвращаем Promise2373 return app.openPage(pageId);2374 })2375 ;2376 },/*}}}*/2377 /** getAppholder ** {{{ Получить объект `appholder` (???)2378 */2379 getAppholder : function () {2380 return project.config.useAppholder ? this._appholder : null;2381 },/*}}}*/2382 /** _clearAppData ** {{{ Очищаем все загруженные данные приложения2383 * TODO: Очищать в той же переменной, без замены (все сохранённые ссылки будут тоже "очищены"?)2384 */2385 _clearAppData : function () {2386 this.params.loaded_dicts = {};2387 this.params.loaded_assets = {};2388 this.params.dicts = {};2389 this.params.assets_data = {};2390 // config?2391 },/*}}}*/2392 /** _resetApp ** {{{ Сбрасываем состояние приложения (полная очистка)2393 * @returns {Promise}2394 */2395 _resetApp : function () {2396 var app = this,2397 undef2398 ;2399 // Закрываем страницу (и снимаем флаг эмуляции)2400 return app._closePage(true)2401 .then(function(){2402 // Снимаем флаг эмуляции bemhtml контента2403 app.delMod('emulate');2404 delete app.params.emulateId;2405 // Очищаем контейнер2406 app._clearPageContainer();2407 // Останавливаем повторяющийся вызов обновления сессии2408 app._stopRenewSession();2409 // Останавливаем сокеты2410 app._stopSocket();2411 // Очищаем все загруженные данные2412 app._clearAppData();2413 return { status : 'app state reseted' };2414 })2415 ;2416 },/*}}}*/2417 /** _isSpecialPageId ** {{{ Является ли страница спецстраницей?2418 * @param pageId2419 * @returns {boolean}2420 */2421 _isSpecialPageId : function (pageId) {2422 return !!( pageId && this._specialPages[pageId] );2423 },/*}}}*/2424 /** _getSpecialPageId ** {{{ Показывать ли спецстраницу (SignedOut) без загрузки параметров и инициализации?2425 * @return {string|boolean}2426 */2427 _getSpecialPageId : function () {2428 var pageId = this._getPageIdToOpen();2429 return this._isSpecialPageId(pageId) ? pageId : false;2430 },/*}}}*/2431 /** setSpecialAuthErrorTitle ** {{{ Установить заголовок (тело) для ошибки авторизации */2432 setSpecialAuthErrorTitle : function (title) {2433 // TODO: Использовать поиск внутри объекта!2434 app._specialPages.AuthError.content.content[0].content = title;2435 },/*}}}*/2436 /** showSpecialPage ** {{{ Показываем спецстраницу2437 * @param specialId - Идентификатор спецстраницы2438 */2439 showSpecialPage : function (specialId) {2440 var2441 app = this,2442 specialCtx = app._specialPages[specialId],2443 bemjson = specialCtx ? specialCtx.content: 'Неизвестная страница: '+specialId,2444 html = BEMHTML.apply(bemjson),2445 icon = specialCtx ? specialCtx.icon : 'ti-close',2446 undef2447 ;2448 app._appholder.hideScreenLoader(); // ???2449 app._appholder.error(html, icon)2450 // Спецстраница создана и анимация завершена2451 .then(function(data){2452 var acceptDom = specialCtx.acceptDom || app._specialPagesAcceptDom2453 ;2454 // Вызов колбека действий с DOM спецстраницы2455 if ( typeof acceptDom === 'function' ) {2456 acceptDom.call(app, specialId, specialCtx, data.domElem);2457 }2458 })2459 ;2460 },/*}}}*/2461 /** _initConfig ** {{{ Дополняем конфигурацию приложения переданными параметрами */2462 _initConfig : function () {2463 try {2464 var queryParams = querystring.parse(window.location.search);2465 Object.keys(queryParams).map(function(id){2466 var val = queryParams[id];2467 // Вариант 1: парсим переданные данные (опасно)2468 // val = eval(val, { // jshint ignore:line2469 // __builtins__ : {},2470 // });2471 // Вариант 2: разбираем булевы и числовые значения2472 if ( !isNaN(val) ) {2473 val = Number(val);2474 }2475 else if ( val === 'true' ) {2476 val = true;2477 }2478 else if ( val === 'false' ) {2479 val = false;2480 }2481 queryParams[id] = val;2482 });2483 Object.assign(project.config, queryParams);2484 console.info( 'project.config extended with', queryParams, '=>', project.config );2485 return { status : 'configInited' };2486 }2487 catch (error) {2488 console.error( '_initConfig error:', error );2489 /*DEBUG*//*jshint -W087*/debugger;2490 return app.__error([ 'Некорректно задан параметр: ', error ], '_initConfig');2491 }2492 },/*}}}*/2493 /** _initApp ** {{{ Инициализируем приложение2494 * @param {boolean} skipSpecials - Принудительно используем станадртную процедуру инициализации (не показываем спецстраницы)2495 * @returns {Promise}2496 */2497 _initApp : function (skipSpecials) {2498 var app = this,2499 that = this,2500 // Внешний DOM-элемент2501 outerVlayout = this._vlayout,2502 // Заставка приложения2503 appholder = this._appholder = project.config.useAppholder ? ( outerVlayout && outerVlayout.findChildBlock(BEMDOM.entity('appholder')) ) : null,2504 // Название первой фазы загрузки2505 title = 'Инициализация приложения...',2506 // Выводим информацию в заставку приложения2507 loaderStatus = appholder && appholder.loaderStatusSet(title),2508 // Стартуем основной waiter2509 appInitWaiter = waiter.start('app_init_'+Date.now(), {2510 title : title,2511 }),2512 undef2513 ;2514 // Не требуется загрузка параметров, просто показываем контент (SignedOut)2515 var specialPageId = this._getSpecialPageId();2516 if ( !skipSpecials && specialPageId ) {2517 // Показываем контент спецстраницы2518 appInitWaiter.Done();2519 this.showSpecialPage(specialPageId);2520 return null;2521 }2522 // Иначе идём на стандартную процедуру загрузки параметров и инициализации2523 return vow.cast(null)2524 // Дополняем конфигурацию2525 .then(app._initConfig, app)2526 // Загружаем параметры2527 .then(app._loadAppParams, app)2528 // Завершаем подготовку: открываем страницу2529 .then(app._afterInit, app)2530 .then(function(data) {2531 appInitWaiter.Done();2532 if ( data.status !== 'showSpecialPage' ) {2533 if ( appholder ) {2534 appholder.loaderStatusRemove(title);2535 appholder.closeLoaderAndDisappear(true);2536 }2537 }2538 })2539 .fail(function(error) {2540 console.error( 'app._loadAppParams error', error );2541 /*DEBUG*//*jshint -W087*/debugger;2542 app.error(error);2543 appInitWaiter.Error(error);2544 })2545 ;2546 },/*}}}*/2547 /** initialize() ** {{{ Инициализируем объект.2548 */2549 initialize : function initialize () {2550 var app = this,2551 that = this,2552 // __callee = arguments.callee.name,2553 // __calleeCaller = arguments.callee.caller.name,2554 params = this.params2555 ;2556 // Устанавливаем подмодули2557 app.modules = {2558 socket : socket,2559 session : session,2560 };2561 Object.assign(app, app.modules);2562 // Сохраняем ссылку на `app` в глобальном DOM // ???2563 window && ( window.app = this );2564 // Параметры2565 app._initDefaultParams();2566 // DOM2567 app._initDom();2568 app._initDomEvents();2569 // Инициализируем приложение2570 app._initApp();2571 },/*}}}*/2572 /** onSetMod... ** {{{ События на установку модификаторов...2573 *2574 * @method2575 *2576 */2577 onSetMod : {2578 /** js:inited ** {{{ Модификатор `js:inited` -- при инициализации блока системой.2579 */2580 'js' : {2581 'inited' : function () {2582 this.initialize();2583 },2584 },/*}}}*/2585 },/*}}}*/2586} ));...

Full Screen

Full Screen

custom_emulate.js

Source:custom_emulate.js Github

copy

Full Screen

...34 return 'customApp width:' + window.innerWidth35 })36 .result(console.log)37 .sleep(500)38 .clearEmulate()39 .evaluate(() => {40 return 'default width:' + window.innerWidth41 })42 .result(console.log)43 .sleep(500)44 .end()45 .then(_ => chromy.close())46 .catch(e => {47 console.log(e)48 chromy.close()...

Full Screen

Full Screen

emulate.js

Source:emulate.js Github

copy

Full Screen

...14 return 'iPhone6 width:' + window.innerWidth15 })16 .result(console.log)17 .sleep(500)18 .clearEmulate()19 .evaluate(() => {20 return 'default width:' + window.innerWidth21 })22 .result(console.log)23 .sleep(500)24 .end()25 .then(_ => chromy.close())26 .catch(e => {27 console.log(e)28 chromy.close()...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1const chromy = require('chromy');2 .clearEmulate()3 .evaluate(() => {4 return {5 };6 })7 .end()8 .result(r => {9 console.log(r);10 });11### `new Chromy(options)`12Default: `{waitUntil: 'load'}`13Default: `{behavior: 'instant', block: 'start'}`14Default: `{}`15Default: `{returnByValue: true}`

Full Screen

Using AI Code Generation

copy

Full Screen

1const chromy = new Chromy();2 .chain()3 .clearEmulate()4 .end()5 .then(() => {6 console.log('done');7 })8 .catch(e => {9 console.error(e);10 });11### new Chromy(options)12const chromy = new Chromy();13Default: `{}`14The options to launch Chrome. See [Launch Options](

Full Screen

Using AI Code Generation

copy

Full Screen

1chromy.clearEmulate();2chromy.clearNetworkInterception();3chromy.clearStorage();4chromy.close();5chromy.evaluate(function() {6 return 1 + 1;7});8chromy.evaluateAsync(function() {9 return new Promise(function(resolve) {10 setTimeout(function() {11 resolve(1 + 1);12 }, 1000);13 });14});15chromy.evaluateOnNewDocument(function() {16 window.__TEST__ = true;17});18chromy.evaluateTo('result', function() {19 return 1 + 1;20});21chromy.evaluateToAsync('result', function() {22 return new Promise(function(resolve) {23 setTimeout(function() {24 resolve(1 + 1);25 }, 1000);26 });27});28chromy.evaluateToProperty('

Full Screen

Using AI Code Generation

copy

Full Screen

1const chromy = require('chromy')2const fs = require('fs')3const path = require('path')4 .chain()5 .clearEmulate()6 .end()7 .then(() => {8 console.log('done')9 })10Contributions are welcome! Please see [CONTRIBUTING.md](

Full Screen

Using AI Code Generation

copy

Full Screen

1const chromy = new Chromy()2chromy.chain()3 .emulate(Device.iPhone6)4 .clearEmulate()5 .end()6 .then(() => {7 console.log('success')8 })9 .catch((e) => {10 console.log(e)11 })12## `chromy.chain().emulateViewport(width, height, deviceScaleFactor, mobile, fitWindow)`13const chromy = new Chromy()14chromy.chain()15 .emulateViewport(375, 667, 2, true, true)16 .end()17 .then(() => {18 console.log('success')19 })20 .catch((e) => {21 console.log(e)22 })23## `chromy.chain().clearEmulate()`24const chromy = new Chromy()25chromy.chain()26 .emulate(Device.iPhone6)27 .clearEmulate()28 .end()29 .then(() => {30 console.log('success')31 })32 .catch((e) => {33 console.log(e)34 })35## `chromy.chain().clearDeviceMetricsOverride()`36const chromy = new Chromy()37chromy.chain()38 .emulateViewport(375, 667, 2, true, true)39 .clearDeviceMetricsOverride()40 .end()41 .then(() => {42 console.log('success')43 })44 .catch((e) => {

Full Screen

Using AI Code Generation

copy

Full Screen

1const Chromy = require('chromy');2(async function() {3 const chromy = new Chromy();4 await chromy.chain()5 .emulate('Nexus 5X')6 .clearEmulate()7 .end()8 .result();9 await chromy.close();10 process.exit(0);11})();12### `new Chromy([options])`13- `options` {Object} - Optional options for Chromy14### `chromy.chain()`15### `chromy.end()`

Full Screen

Using AI Code Generation

copy

Full Screen

1chromy.clearEmulate();2### chromy.setEmulate(options)3### chromy.setUserAgent(userAgent)4### chromy.setViewport(options)5### chromy.setExtraHTTPHeaders(headers)6### chromy.setOfflineMode(enabled)7### chromy.setCacheEnabled(enabled)8### chromy.setCookie(name, value, url)9### chromy.deleteCookie(name, url)10### chromy.clearCookies()11### chromy.setGeolocation(options)12### chromy.setDeviceMetrics(options)13### chromy.setDeviceScaleFactor(factor)14### chromy.setTouchEmulationEnabled(enabled)15### chromy.setScriptExecutionDisabled(disabled)16### chromy.setScrollbarsHidden(hidden)17### chromy.setIgnoreCertificateErrors(enabled)18### chromy.setAcceptLanguage(language)

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run chromy automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful