Current Path : /var/www/www-root/data/www/info.monolith-realty.ru/bitrix/js/ui/vue/vue2/dev/src/ |
Current File : /var/www/www-root/data/www/info.monolith-realty.ru/bitrix/js/ui/vue/vue2/dev/src/bitrixvue.js |
/** * Bitrix Vue wrapper * * @package bitrix * @subpackage ui * @copyright 2001-2022 Bitrix */ import {EventEmitter} from 'main.core.events'; import { Extension, Loc, Type } from 'main.core'; import {RestClient, rest} from "rest.client"; import {PullClient, PULL as pull} from "pull.client"; export class BitrixVue { static developerMode = false; constructor(VueVendor) { this._appCounter = 0; this._components = {}; this._mutations = {}; this._clones = {}; this._instance = VueVendor; this._instance.use(this); this.event = new VueVendor; this.events = { restClientChange: 'RestClient::change', pullClientChange: 'PullClient::change', } const settings = Extension.getSettings('ui.vue'); this.localizationMode = settings.get('localizationDebug', false)? 'development': 'production'; } /** * Create new Vue instance * * @param {Object} params - definition * * @see https://vuejs.org/v2/guide/ */ create(params) { BitrixVue.showNotice( 'Method Vue.create is deprecated, use BitrixVue.createApp instead.\n' + 'If you are using "el" property or .$mount(...) to bind your application, use .mount(...) instead.' ); return this.createApp(params); } /** * Create new Vue instance * * @param {Object} params - definition * * @see https://v2.vuejs.org/v2/guide/ */ createApp(params) { const bitrixVue = this; // 1. Init Bitrix public api const $Bitrix = {}; // 1.1 Localization $Bitrix.Loc = { messages: {}, getMessage: function(messageId: string, replacements:? {[key: string]: string} = null): string { if (bitrixVue.localizationMode === 'development') { let debugMessageId = [messageId]; if (Type.isPlainObject(replacements)) { const replaceKeys = Object.keys(replacements); if (replaceKeys.length > 0) { debugMessageId = [messageId, ' (replacements: ', replaceKeys.join(', '), ')'] } } return debugMessageId.join(''); } let message = ''; if (!Type.isUndefined(this.messages[messageId])) { message = this.messages[messageId]; } else { message = Loc.getMessage(messageId); this.messages[messageId] = message; } if (Type.isString(message) && Type.isPlainObject(replacements)) { Object.keys(replacements).forEach((replacement: string) => { const globalRegexp = new RegExp(replacement, 'gi'); message = message.replace( globalRegexp, () => { return Type.isNil(replacements[replacement]) ? '' : String(replacements[replacement]); } ); }); } return message; }, hasMessage: function (messageId: string): boolean { return Type.isString(messageId) && !Type.isNil(this.getMessages()[messageId]); }, getMessages: function (): object { if (typeof BX.message !== 'undefined') { return {...BX.message, ...this.messages}; } return {...this.messages}; }, setMessage: function(id: string | {[key: string]: string}, value?: string): void { if (Type.isString(id)) { this.messages[id] = value; } if (Type.isObject(id)) { for (const code in id) { if (id.hasOwnProperty(code)) { this.messages[code] = id[code]; } } } } }; // 1.2 Application Data $Bitrix.Application = { instance: null, get: function(): Object { return this.instance; }, set: function(instance: Object): void { this.instance = instance; }, }; // 1.3 Application Data $Bitrix.Data = { data: {}, get: function(name: string, defaultValue?:any): any { return this.data[name] ?? defaultValue; }, set: function(name: string, value: any): void { this.data[name] = value; } }; // 1.4 Application EventEmitter $Bitrix.eventEmitter = new EventEmitter(); if (typeof $Bitrix.eventEmitter.setEventNamespace === 'function') { this._appCounter++; $Bitrix.eventEmitter.setEventNamespace('vue:app:'+this._appCounter); } else // hack for old version of Bitrix SM { window.BX.Event.EventEmitter.prototype.setEventNamespace = function () {} $Bitrix.eventEmitter.setEventNamespace = function () {} } // 1.5 Application RestClient $Bitrix.RestClient = { instance: null, get: function(): RestClient { return this.instance ?? rest; }, set: function(instance: RestClient): void { this.instance = instance; $Bitrix.eventEmitter.emit(bitrixVue.events.restClientChange); }, isCustom() { return this.instance !== null; } }; // 1.6 Application PullClient $Bitrix.PullClient = { instance: null, get: function(): PullClient { return this.instance ?? pull; }, set: function(instance: PullClient): void { this.instance = instance; $Bitrix.eventEmitter.emit(bitrixVue.events.pullClientChange); }, isCustom() { return this.instance !== null; } }; if (typeof (params.mixins) === 'undefined') { params.mixins = []; } params.mixins.unshift({ beforeCreate: function() { this.$bitrix = $Bitrix; }, }) let instance = new this._instance(params); instance.mount = function(rootContainer: string|Element): object { return this.$mount(rootContainer); } return instance; } /** * Register Vue component * * @param {String} id * @param {Object} params * @param {Object} [options] * * @see https://v2.vuejs.org/v2/guide/components.html */ component(id, params, options = {}) { if (!params.name) { params.name = id; } this._components[id] = Object.assign({}, params); this._components[id].bitrixOptions = { immutable: options.immutable === true, local: options.local === true, }; if (typeof this._clones[id] !== 'undefined') { this._registerCloneComponent(id); } const componentParams = this._getFinalComponentParams(id); if (this.isLocal(id)) { return componentParams; } return this._instance.component(id, componentParams); } /** * Register Vue component (local) * @see https://v2.vuejs.org/v2/guide/components.html * * @param {string} name * @param {Object} definition * @param {Object} [options] * * @returns {Object} */ localComponent(name, definition, options = {}) { return this.component(name, definition, {...options, local: true}); } /** * Get local Vue component * @see https://v2.vuejs.org/v2/guide/components.html * * @param {string} name * * @returns {Object} */ getLocalComponent(name) { if (!this.isComponent(name)) { BitrixVue.showNotice('Component "'+name+'" is not registered yet.'); return null; } if (!this.isLocal(name)) { BitrixVue.showNotice('You cannot get the component "'+name+'" because it is marked as global.'); return null; } return this._getFinalComponentParams(name); } /** * Modify Vue component * * @param {String} id * @param {Object} mutations * * @returns {Function|boolean} - function for remove this modification */ mutateComponent(id, mutations) { const mutable = this.isMutable(id); if (mutable === false) { BitrixVue.showNotice('You cannot mutate the component "'+id+'" because it is marked as immutable, perhaps cloning the component is fine for you.'); return false; } if (typeof this._mutations[id] === 'undefined') { this._mutations[id] = []; } this._mutations[id].push(mutations); if ( typeof this._components[id] !== 'undefined' && !this.isLocal(id) ) { this.component(id, this._components[id], this._components[id].bitrixOptions); } return () => { this._mutations[id] = this._mutations[id].filter((element) => element !== mutations); }; } /** * Clone Vue component * * @param {string} id * @param {string} sourceId * @param {object} mutations * @returns {boolean} */ cloneComponent(id, sourceId, mutations) { if (this.isLocal(sourceId)) { const definition = this.getLocalComponent(sourceId); definition.name = id; this.component(id, definition, {immutable: false, local: true}); this.mutateComponent(id, mutations); return true; } if (typeof this._clones[sourceId] === 'undefined') { this._clones[sourceId] = {}; } this._clones[sourceId][id] = {id, sourceId, mutations}; if (typeof this._components[sourceId] !== 'undefined') { this._registerCloneComponent(sourceId, id); } return true; } /** * Clone Vue component (object) * * @param {object} source * @param {object} mutations * @returns {object} */ cloneLocalComponent(source, mutations) { if (typeof source !== 'object') { source = this.getLocalComponent(source); if (!source) { return null; } } return this._applyMutation( this._cloneObjectWithoutDuplicateFunction(source, mutations), mutations ); } /** * Check exists Vue component * * @param {string} id * @returns {boolean} */ isComponent(id) { return typeof this._components[id] !== 'undefined' } /** * Check able to mutate Vue component * * @param id * @returns {boolean|undefined} - undefined when component not registered yet. */ isMutable(id) { if (typeof this._components[id] === 'undefined') { return undefined; } return !this._components[id].bitrixOptions.immutable; } /** * Check component is a local * * @param id * @returns {boolean|undefined} - undefined when component not registered yet. */ isLocal(id) { if (typeof this._components[id] === 'undefined') { return undefined; } return this._components[id].bitrixOptions.local === true; } /** * Create a "subclass" of the base Vue constructor. * * @param options * @returns {*} * * @see https://v2.vuejs.org/v2/api/#Vue-extend */ extend(options) { return this._instance.extend(options) } /** * Defer the callback to be executed after the next DOM update cycle. Use it immediately after you have changed some data to wait for the DOM update. * * @param {Function} callback * @param {Object} context * @returns {Promise|void} * * @see https://v2.vuejs.org/v2/api/#Vue-nextTick */ nextTick(callback, context) { return this._instance.nextTick(callback, context); } /** * Adds a property to a reactive object, ensuring the new property is also reactive, so triggers view updates. * * @param {Object|Array} target * @param {String|Number} key * @param {*} value * @returns {*} * * @see https://v2.vuejs.org/v2/api/#Vue-set */ set(target, key, value) { return this._instance.set(target, key, value); } /** * Delete a property on an object. If the object is reactive, ensure the deletion triggers view updates. * * @param {Object|Array} target * @param {String|Number} key * @returns {*} */ delete(target, key) { return this._instance.delete(target, key); } /** * Register or retrieve a global directive. * * @param {String} id * @param {Object|Function} definition * @returns {*} * * @see https://v2.vuejs.org/v2/api/#Vue-directive */ directive(id, definition) { return this._instance.directive(id, definition); } /** * Register or retrieve a global filter. * * @param id * @param definition * @returns {*} * * @see https://v2.vuejs.org/v2/api/#Vue-filter */ filter(id, definition) { return this._instance.filter(id, definition); } /** * Install a Vue.js plugin. * * @param {Object|Function} plugin * @returns {*} * * @see https://v2.vuejs.org/v2/api/#Vue-use */ use(plugin) { return this._instance.use(plugin); } /** * Apply a mixin globally, which affects every Vue instance created afterwards. * * @param {Object} mixin * @returns {*|Function|Object} * * @see https://v2.vuejs.org/v2/api/#Vue-mixin */ mixin(mixin) { return this._instance.mixin(mixin); } /** * Make an object reactive. Internally, Vue uses this on the object returned by the data function. * * @param object * @returns {*} * * @see https://v2.vuejs.org/v2/api/#Vue-observable */ observable(object) { return this._instance.observable(object); } /** * Compiles a template string into a render function. * * @param template * @returns {*} * * @see https://v2.vuejs.org/v2/api/#Vue-compile */ compile(template) { return this._instance.compile(template); } /** * Provides the installed version of Vue as a string. * * @returns {String} * * @see https://v2.vuejs.org/v2/api/#Vue-version */ version() { return this._instance.version; } /** * Test node for compliance with parameters * * @param obj * @param params * @returns {boolean} */ testNode(obj, params) { if (!params || typeof params !== 'object') { return true; } let i,j,len; for (i in params) { if(!params.hasOwnProperty(i)) { continue; } switch(i) { case 'tag': case 'tagName': if (typeof params[i] === "string") { if (obj.tagName.toUpperCase() !== params[i].toUpperCase()) { return false; } } else if (params[i] instanceof RegExp) { if (!params[i].test(obj.tagName)) { return false; } } break; case 'class': case 'className': if (typeof params[i] === "string") { if (!obj.classList.contains(params[i].trim())) { return false; } } else if (params[i] instanceof RegExp) { if ( typeof obj.className !== "string" || !params[i].test(obj.className) ) { return false; } } break; case 'attr': case 'attrs': case 'attribute': if (typeof params[i] === "string") { if (!obj.getAttribute(params[i])) { return false; } } else if (params[i] && Object.prototype.toString.call(params[i]) === "[object Array]") { for (j = 0, len = params[i].length; j < len; j++) { if (params[i][j] && !obj.getAttribute(params[i][j])) { return false; } } } else { for (j in params[i]) { if(!params[i].hasOwnProperty(j)) { continue } let value = obj.getAttribute(j); if (typeof value !== "string") { return false; } if (params[i][j] instanceof RegExp) { if (!params[i][j].test(value)) { return false; } } else if (value !== '' + params[i][j]) { return false; } } } break; case 'property': case 'props': if (typeof params[i] === "string") { if (!obj[params[i]]) { return false; } } else if (params[i] && Object.prototype.toString.call(params[i]) == "[object Array]") { for (j = 0, len = params[i].length; j < len; j++) { if (params[i][j] && !obj[params[i][j]]) { return false; } } } else { for (j in params[i]) { if(!params[i].hasOwnProperty(j)) { continue } if (typeof params[i][j] === "string") { if (obj[j] != params[i][j]) { return false; } } else if (params[i][j] instanceof RegExp) { if ( typeof obj[j] !== "string" || !params[i][j].test(obj[j]) ) { return false; } } } } break; } } return true; } /** * Getting a part of localization object for insertion into computed property. * * @param {String} phrasePrefix * @param {Object|null} phrases * @returns {ReadonlyArray<any>} */ getFilteredPhrases(phrasePrefix, phrases = null) { let result = {}; if (!phrases && typeof BX.message !== 'undefined') { phrases = BX.message; } else if (Type.isObject(phrases) && Type.isObject(phrases.$Bitrix)) { phrases = phrases.$Bitrix.Loc.getMessages(); } if (Array.isArray(phrasePrefix)) { for (let message in phrases) { if (!phrases.hasOwnProperty(message)) { continue } if (!phrasePrefix.find((element) => message.toString().startsWith(element))) { continue; } if (this.localizationMode === 'development') { result[message] = message; } else { result[message] = phrases[message]; } } } else { for (let message in phrases) { if (!phrases.hasOwnProperty(message)) { continue } if (!message.startsWith(phrasePrefix)) { continue; } if (this.localizationMode === 'development') { result[message] = message; } else { result[message] = phrases[message]; } } } return Object.freeze(result); } /** * Return component params with mutation * * @param {String} componentId * @param {Object} mutations * @returns {null|Object} * * @private */ _getComponentParamsWithMutation(componentId, mutations) { if (typeof this._components[componentId] === 'undefined') { return null; } let componentParams = Object.assign({}, this._components[componentId]); if (typeof mutations === 'undefined') { return componentParams; } mutations.forEach(mutation => { componentParams = this._applyMutation( this._cloneObjectWithoutDuplicateFunction(componentParams, mutation), mutation); }); return componentParams; } _getFinalComponentParams(id) { const mutations = this.isMutable(id)? this._mutations[id]: undefined; return this._getComponentParamsWithMutation(id, mutations); } /** * Register clone of components * * @param {String} sourceId * @param {String|null} [id] * * @private */ _registerCloneComponent(sourceId, id = null) { let components = []; if (id) { if (typeof this._clones[sourceId][id] !== 'undefined') { components.push(this._clones[sourceId][id]); } } else { for (let cloneId in this._clones[sourceId]) { if (!this._clones[sourceId].hasOwnProperty(cloneId)) { continue; } components.push(this._clones[sourceId][cloneId]); } } components.forEach(element => { let mutations = []; if (typeof this._mutations[element.sourceId] !== 'undefined') { mutations = mutations.concat(this._mutations[element.sourceId]); } mutations.push(element.mutations); let componentParams = this._getComponentParamsWithMutation(element.sourceId, mutations); if (!componentParams) { return false; } this.component(element.id, componentParams); }); } /** * Clone object without duplicate function for apply mutation * * @param objectParams * @param mutation * @param level * @param previousParamName * @private */ _cloneObjectWithoutDuplicateFunction(objectParams = {}, mutation = {}, level = 1, previousParamName = '') { const object = {}; for (const param in objectParams) { if (!objectParams.hasOwnProperty(param)) { continue; } if (Type.isString(objectParams[param])) { object[param] = objectParams[param]; } else if (Type.isArray(objectParams[param])) { object[param] = [].concat(objectParams[param]); } else if (Type.isObjectLike(objectParams[param])) { if ( previousParamName === 'watch' || previousParamName === 'props' || previousParamName === 'directives' ) { object[param] = objectParams[param]; } else if (Type.isNull(objectParams[param])) { object[param] = null; } else if (Type.isObjectLike(mutation[param])) { object[param] = this._cloneObjectWithoutDuplicateFunction(objectParams[param], mutation[param], (level+1), param) } else { object[param] = Object.assign({}, objectParams[param]) } } else if (Type.isFunction(objectParams[param])) { if (!Type.isFunction(mutation[param])) { object[param] = objectParams[param]; } else if (level > 1) { if (previousParamName === 'watch') { object[param] = objectParams[param]; } else { object['parent'+param[0].toUpperCase()+param.substr(1)] = objectParams[param]; } } else { if (Type.isUndefined(object['methods'])) { object['methods'] = {}; } object['methods']['parent'+param[0].toUpperCase()+param.substr(1)] = objectParams[param]; if (Type.isUndefined(objectParams['methods'])) { objectParams['methods'] = {}; } objectParams['methods']['parent'+param[0].toUpperCase()+param.substr(1)] = objectParams[param]; } } else if (!Type.isUndefined(objectParams[param])) { object[param] = objectParams[param]; } } return object; } /** * Apply mutation * * @param clonedObject * @param mutation * @param level * @private */ _applyMutation(clonedObject = {}, mutation = {}, level = 1): object { const object = Object.assign({}, clonedObject); for (const param in mutation) { if (!mutation.hasOwnProperty(param)) { continue; } if ( level === 1 && (param === 'compilerOptions' || param === 'setup') ) { object[param] = mutation[param]; } else if (level === 1 && param === 'extends') { object[param] = mutation[param]; } else if (Type.isString(mutation[param])) { if (Type.isString(object[param])) { object[param] = mutation[param].replace(`#PARENT_${param.toUpperCase()}#`, object[param]); } else { object[param] = mutation[param].replace(`#PARENT_${param.toUpperCase()}#`, ''); } } else if (Type.isArray(mutation[param])) { if (level === 1 && param === 'replaceMixins') { object['mixins'] = [].concat(mutation[param]); } else if (level === 1 && param === 'replaceInject') { object['inject'] = [].concat(mutation[param]); } else if (level === 1 && param === 'replaceEmits') { object['emits'] = [].concat(mutation[param]); } else if (level === 1 && param === 'replaceExpose') { object['expose'] = [].concat(mutation[param]); } else if (Type.isPlainObject(object[param])) { mutation[param].forEach(element => object[param][element] = null); } else { object[param] = object[param].concat(mutation[param]); } } else if (Type.isObjectLike(mutation[param])) { if ( level === 1 && param === 'props' && Type.isArray(object[param]) || level === 1 && param === 'emits' && Type.isArray(object[param]) ) { const newObject = {}; object[param].forEach(element => { newObject[element] = null; }); object[param] = newObject; } if (level === 1 && param === 'watch') { for (const paramName in object[param]) { if (!object[param].hasOwnProperty(paramName)) { continue; } if (paramName.includes('.')) { continue; } if ( Type.isFunction(object[param][paramName]) || ( Type.isObject(object[param][paramName]) && Type.isFunction(object[param][paramName]['handler']) ) ) { if (Type.isUndefined(object['methods'])) { object['methods'] = {}; } const originNewFunctionName = 'parentWatch'+paramName[0].toUpperCase()+paramName.substr(1); if (Type.isFunction(object[param][paramName])) { object['methods'][originNewFunctionName] = object[param][paramName]; } else { object['methods'][originNewFunctionName] = object[param][paramName]['handler']; } } } } if (level === 1 && param === 'replaceEmits') { object['emits'] = Object.assign({}, mutation[param]); } else if ( level === 1 && ( param === 'components' || param === 'directives' ) ) { if (Type.isUndefined(object[param])) { object[param] = {}; } for (const objectName in mutation[param]) { if (!mutation[param].hasOwnProperty(objectName)) { continue; } let parentObjectName = objectName[0].toUpperCase()+objectName.substr(1); parentObjectName = param === 'components'? 'Parent'+parentObjectName: 'parent'+parentObjectName object[param][parentObjectName] = Object.assign({}, object[param][objectName]); if (param === 'components') { if (Type.isUndefined(mutation[param][objectName].components)) { mutation[param][objectName].components = {}; } mutation[param][objectName].components = Object.assign({[parentObjectName]: object[param][objectName]}, mutation[param][objectName].components); } object[param][objectName] = mutation[param][objectName]; } } else if (Type.isArray(object[param])) { for (const mutationName in mutation[param]) { if (!mutation[param].hasOwnProperty(mutationName)) { continue; } object[param].push(mutationName); } } else if (Type.isObjectLike(object[param])) { object[param] = this._applyMutation(object[param], mutation[param], (level+1)); } else { object[param] = mutation[param]; } } else { object[param] = mutation[param]; } } return object; } /** * @private * @param text */ static showNotice(text) { if (BitrixVue.developerMode) { console.warn('BitrixVue: '+text); } } /** * @deprecated Special method for plugin registration */ install(app, options) { app.mixin( { beforeCreate() { if (typeof this.$root !== 'undefined') { this.$bitrix = this.$root.$bitrix; } }, computed: { $Bitrix: function() { return this.$root.$bitrix; }, }, mounted: function () { if (!Type.isNil(this.$root.$bitrixApplication)) { BitrixVue.showNotice("Store reference in global variables (like: this.$bitrixApplication) is deprecated, use this.$Bitrix.Data.set(...) instead."); } if (!Type.isNil(this.$root.$bitrixController)) { BitrixVue.showNotice("Store reference in global variables (like: this.$bitrixController) is deprecated, use this.$Bitrix.Data.set(...) instead."); } if (!Type.isNil(this.$root.$bitrixMessages)) { BitrixVue.showNotice("Store localization in global variable this.$bitrixMessages is deprecated, use this.$Bitrix.Log.setMessage(...) instead."); } if (!Type.isNil(this.$root.$bitrixRestClient)) { BitrixVue.showNotice("Working with a Rest-client through an old variable this.$bitrixRestClient is deprecated, use this.$Bitrix.RestClient.get() instead."); } if (!Type.isNil(this.$root.$bitrixPullClient)) { BitrixVue.showNotice("Working with a Pull-client through an old variable this.$bitrixPullClient is deprecated, use this.$Bitrix.PullClient.get() instead."); } } }); } }