Current Path : /var/www/u0635749/data/www/hobbyclick.ru/public/hobbyclick/bitrix/js/sale/ |
Current File : /var/www/u0635749/data/www/hobbyclick.ru/public/hobbyclick/bitrix/js/sale/core_ui_widget.js |
(function(){ if(typeof BX.ui != 'object') BX.ui = {}; ////////////////////////////// // base widget ////////////////////////////// BX.ui.widget = function(opts){ BX.merge(this, { opts: { scope: false, // it should be either native dom object, or string that represents node id useSpawn: false, // if set to true, you can do .spawn() on this object messages: {}, // language-dependent messages to display controls: {}, // known links to controls bindEvents: {}, // event pre-binding (when use this, keep in mind that the resulting instance could not be fully formed yet) removeTemplates: true, // remove script nodes after search initializeByGlobalEvent: false, // if equals to a not-empty string, initialization will be performed only by event with that name, being fired on document globalEventScope: 'document' // initializeByGlobalEvent scope (could be 'document' or 'window') }, vars: {}, // significant variables ctrls: {}, // links to controls tmpls: {}, // templates sys: { stack: {init:[]}, code: 'widget', // only [a-z0-9_-] allowed initialized: false } }); this.pushFuncStack('init', BX.ui.widget); this.isUIWidget = true; // prevent BX.merge() from going deeper on a widget instance } // the following functions can be overrided with inheritance BX.merge(BX.ui.widget.prototype, { //////////////////////////// /// about initialization // only basic things here preInit: function(){ var ctx = this, so = this.opts, sc = this.ctrls, code = this.sys.code; sc.scope = null; if(!('querySelector' in document)) throw new Error('Your browser does not support querySelector'); if(!code.match(/^[a-zA-Z0-9-_]+$/)) throw new Error('Only letters, digitis, "-" and "_" allowed in code'); }, // member of stack of initializers, must be defined even if do nothing init: function(){ var ctx = this, sc = this.ctrls, so = this.opts, code = this.sys.code; if(so.scope !== false){ // some widgets may have no scope sc.scope = BX.type.isNotEmptyString(so.scope) ? BX(so.scope) : so.scope; if(!BX.type.isElementNode(sc.scope)) throw new Error('Invalid node passed'); if(so.useSpawn && sc.scope) ctx.tmpls['scope'] = sc.scope.outerHTML; // templates var templates = sc.scope.querySelectorAll('script[type="text/html"]'); for(var k = 0; k < templates.length; k++){ var id = BX.data(templates[k], 'template-id'); if(typeof id == 'string' && id.length > 0 && id.search('bx-ui-'+code) == 0){ id = id.replace('bx-ui-'+code+'-', ''); ctx.tmpls[id] = templates[k].innerHTML; if(this.opts.removeTemplates) BX.remove(templates[k]); } } } // events if(typeof so.bindEvents == 'object'){ for(var k in so.bindEvents) { if(so.bindEvents.hasOwnProperty(k)) if(BX.type.isFunction(so.bindEvents[k])) this.bindEvent(k, so.bindEvents[k]); } } so.bindEvents = null; }, remove: function(){ // drop scope if(BX.type.isDomNode(this.ctrls.scope)) this.ctrls.scope.innerHTML = ''; // ubind custom events BX.unbindAll(this); // here should be a mechanism of "remove stack", just equal to "init stack", but works in reversed manner /* this.opts = null; this.vars = null; this.ctrls = null; this.tmpls = null; this.sys = null; */ // later unregister in global dispatcher, if ID is set }, //////////////////////////// /// about system getControlClassName: function(id){ return 'bx-ui-'+this.sys.code+'-'+id; }, getControl: function(id, notRequired, scope, getAll){ if(!BX.type.isNotEmptyString(id)) return null; if(BX.type.isElementNode(this.opts.controls[id])) return this.opts.controls[id]; if(!this.ctrls.scope && !searchWholeDoc) return null; var sScope = this.ctrls.scope; if(BX.type.isElementNode(scope)) sScope = scope; var checkFound = function(result){ return (!getAll && result !== null) || (getAll && result.length > 0); }; try{ // it might be in a special data attribute var node = sScope[getAll ? 'querySelectorAll' : 'querySelector']('[data-bx-ui-id="'+this.sys.code+'-'+id+'"]'); if(checkFound(node)) return node; }catch(e){} try{ // it might be control class var node = sScope[getAll ? 'querySelectorAll' : 'querySelector']('.'+this.getControlClassName(id)); if(checkFound(node)) return node; }catch(e){} try{ // it might be some other class var node = sScope[getAll ? 'querySelectorAll' : 'querySelector']('.'+id); if(checkFound(node)) return node; }catch(e){} try{ // last chance - it might be a specified selector var node = sScope[getAll ? 'querySelectorAll' : 'querySelector'](id); if(checkFound(node)) return node; }catch(e){} if(node === null && !notRequired) throw new Error('Requested control node can not be found ('+id+')'); return node; }, setOption: function(name, value){ this.opts[name] = value; }, getOption: function(name){ return this.opts[name]; }, getSysCode: function(){ return this.sys.code; }, //////////////////////////// /// about templating template: function(id, html) { if(typeof html == 'undefined') { return this.tmpls[id]; } else { if(!BX.type.isString(html)) throw new TypeError('Bad template html'); this.tmpls[id] = html; } }, getHTMLByTemplate: function(templateId, replacements){ var html = this.tmpls[templateId]; if(!BX.type.isNotEmptyString(html)) { BX.debug("template not found: "+templateId); return ''; } for(var k in replacements) { if(!replacements.hasOwnProperty(k)) continue; if(typeof replacements[k] != 'undefined' && replacements.hasOwnProperty(k)){ var replaceWith = ''; if(k.toString().indexOf('=') == 0){ // leading '=' stands for an unsafe replace - no escaping replaceWith = replacements[k].toString(); k = k.toString().substr(1); }else replaceWith = BX.util.htmlspecialchars(replacements[k]).toString(); var placeHolder = '{{'+k.toString().toLowerCase()+'}}'; if(replaceWith.search(placeHolder) >= 0) // you must be joking replaceWith = ''; while(html.search(placeHolder) >= 0) // new RegExp('', 'g') on user-controlled data seems not so harmless html = html.replace(placeHolder, replaceWith); } } return html; }, createNodesByTemplate: function(templateId, replacements, onlyTags){ var html = this.getHTMLByTemplate(templateId, replacements); var template = this.tmpls[templateId]; if(!BX.type.isNotEmptyString(template)) { return null; } template = template.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); // trim // table makeup behaves not so well when being parsed by a browser, so a little hack is on route: var isTableRow = false; var isTableCell = false; if(template.search(/^<\s*(tr|th)[^<]*>/) >= 0) isTableRow = true; else if(template.search(/^<\s*td[^<]*>/) >= 0) isTableCell = true; var keeper = document.createElement('div'); if(isTableRow || isTableCell){ if(isTableRow){ keeper.innerHTML = '<table><tbody>'+html+'</tbody></table>'; keeper = keeper.childNodes[0].childNodes[0]; }else{ keeper.innerHTML = '<table><tbody><tr>'+html+'</tr></tbody></table>'; keeper = keeper.childNodes[0].childNodes[0].childNodes[0]; } }else keeper.innerHTML = html; if(onlyTags){ var children = keeper.childNodes; var result = []; // we need only non-text nodes for(var k = 0; k < children.length; k++) if(BX.type.isElementNode(children[k])) result.push(children[k]); return result; }else return Array.prototype.slice.call(keeper.childNodes); }, replaceTemplate: function(templateId, html){ this.tmpls[templateId] = html; }, //////////////////////////// /// about inheritance parentConstruct: function(owner, opts){ var c = owner.superclass; if(typeof c == 'object') c.constructor.apply(this, [opts, true]); }, handleInitStack: function(nf, owner, opts){ this.pushFuncStack('init', owner); if(!nf){ BX.merge(this.opts, opts); BX.ui.widget.prototype.preInit.call(this); var init = function(){ if(this.sys.initialized) // already initialized once return; this.resolveFuncStack('init'); // resove init stacks for(var i in this.sys.stack) { if(this.sys.stack.hasOwnProperty(i)) if(i != 'init') this.resolveFuncStack(i, true); // resolve all other stacks } this.sys.initialized = true; this.fireEvent('init', [this]); }; if(BX.type.isString(this.opts.initializeByGlobalEvent) && this.opts.initializeByGlobalEvent.length > 0){ var scope = this.opts.globalEventScope == 'window' ? window : document; BX.addCustomEvent(scope, this.opts.initializeByGlobalEvent, BX.proxy(init, this)); }else init.call(this); } }, // when you add fName to the stack, function with the corresponding name must exist, at least equal to BX.DoNothing() pushFuncStack: function(fName, owner){ if(BX.type.isFunction(owner.prototype[fName])){ if(typeof this.sys.stack[fName] == 'undefined') this.sys.stack[fName] = []; this.sys.stack[fName].push({owner: owner, f: owner.prototype[fName]}); } }, disableInFuncStack: function(fName, owner){ var stack = this.sys.stack[fName]; if(typeof stack == 'undefined') return; for(var k = 0; k < stack.length; k++){ if(stack[k].owner == owner) stack[k].f = BX.DoNothing; } }, resolveFuncStack: function(fName, fire){ var stack = this.sys.stack[fName]; if(typeof stack == 'undefined') return; for(var k = 0; k < stack.length; k++){ stack[k].f.call(this); } if(fire) this.fireEvent(fName, [this], document); this.sys.stack[fName] = null; }, //////////////////////////// /// about events fireEvent: function(eventName, args, scope){ scope = scope || this; args = args || []; BX.onCustomEvent(scope, 'bx-ui-'+this.sys.code+'-'+eventName, args); }, bindEvent: function(eventName, callback){ BX.addCustomEvent(this, 'bx-ui-'+this.sys.code+'-'+eventName, callback); }, //////////////////////////// /// about css states setCSSState: function(statName, scope) { this.changeCSSState(statName, scope, true); }, dropCSSState: function(statName, scope) { this.changeCSSState(statName, scope, false); }, changeCSSState: function(statName, scope, way) { scope = scope || this.ctrls.scope; if(typeof statName != 'string' || statName.length == 0) return; BX[way ? 'addClass' : 'removeClass'](scope, 'bx-ui-state-'+statName); }, //////////////////////////// /// about miscellaneous spawn: function(node, onSpawn){ // if spawning was enabled, you can spawn widget on auto-duplicated scope // otherwise, you should prepare scope by yourself if(this.opts.useSpawn) BX.html(node, this.tmpls.scope); var opts = BX.clone(this.opts); opts.scope = node; if(BX.type.isFunction(onSpawn)) onSpawn.apply(this, [opts, node]); return new this.constructor(opts); }, getRandom: function(){ // only letters, digits, - and _ allowed to return return 'bx'+this.sys.code+ Math.floor((Math.random() * 1000) + 1)+ Math.floor((Math.random() * 1000) + 1); } }); })();