1(function(){ 2 3 if(typeof BX.ui != 'object') 4 BX.ui = {}; 5 6 ////////////////////////////// 7 // base widget 8 ////////////////////////////// 9 10 BX.ui.widget = function(opts){ 11 12 BX.merge(this, { 13 opts: { 14 scope: false, // it should be either native dom object, or string that represents node id 15 useSpawn: false, // if set to true, you can do .spawn() on this object 16 17 messages: {}, // language-dependent messages to display 18 controls: {}, // known links to controls 19 bindEvents: {}, // event pre-binding (when use this, keep in mind that the resulting instance could not be fully formed yet) 20 21 removeTemplates: true, // remove script nodes after search 22 23 initializeByGlobalEvent: false, // if equals to a not-empty string, initialization will be performed only by event with that name, being fired on document 24 globalEventScope: 'document' // initializeByGlobalEvent scope (could be 'document' or 'window') 25 }, 26 vars: {}, // significant variables 27 ctrls: {}, // links to controls 28 tmpls: {}, // templates 29 sys: { 30 stack: {init:[]}, 31 code: 'widget', // only [a-z0-9_-] allowed 32 initialized: false 33 } 34 }); 35 36 this.pushFuncStack('init', BX.ui.widget); 37 38 this.isUIWidget = true; // prevent BX.merge() from going deeper on a widget instance 39 } 40 // the following functions can be overrided with inheritance 41 BX.merge(BX.ui.widget.prototype, { 42 43 //////////////////////////// 44 /// about initialization 45 46 // only basic things here 47 preInit: function(){ 48 var ctx = this, 49 so = this.opts, 50 sc = this.ctrls, 51 code = this.sys.code; 52 53 sc.scope = null; 54 55 if(!('querySelector' in document)) 56 throw new Error('Your browser does not support querySelector'); 57 58 if(!code.match(/^[a-zA-Z0-9-_]+$/)) 59 throw new Error('Only letters, digitis, "-" and "_" allowed in code'); 60 }, 61 62 // member of stack of initializers, must be defined even if do nothing 63 init: function(){ 64 65 var ctx = this, 66 sc = this.ctrls, 67 so = this.opts, 68 code = this.sys.code; 69 70 if(so.scope !== false){ // some widgets may have no scope 71 72 sc.scope = BX.type.isNotEmptyString(so.scope) ? BX(so.scope) : so.scope; 73 if(!BX.type.isElementNode(sc.scope)) 74 throw new Error('Invalid node passed'); 75 76 if(so.useSpawn && sc.scope) 77 ctx.tmpls['scope'] = sc.scope.outerHTML; 78 79 // templates 80 var templates = sc.scope.querySelectorAll('script[type="text/html"]'); 81 for(var k = 0; k < templates.length; k++){ 82 var id = BX.data(templates[k], 'template-id'); 83 84 if(typeof id == 'string' && id.length > 0 && id.search('bx-ui-'+code) == 0){ 85 86 id = id.replace('bx-ui-'+code+'-', ''); 87 ctx.tmpls[id] = templates[k].innerHTML; 88 89 if(this.opts.removeTemplates) 90 BX.remove(templates[k]); 91 } 92 } 93 } 94 95 // events 96 if(typeof so.bindEvents == 'object'){ 97 for(var k in so.bindEvents) 98 { 99 if(so.bindEvents.hasOwnProperty(k)) 100 if(BX.type.isFunction(so.bindEvents[k])) 101 this.bindEvent(k, so.bindEvents[k]); 102 } 103 } 104 so.bindEvents = null; 105 }, 106 107 remove: function(){ 108 // drop scope 109 if(BX.type.isDomNode(this.ctrls.scope)) 110 this.ctrls.scope.innerHTML = ''; 111 112 // ubind custom events 113 BX.unbindAll(this); 114 115 // here should be a mechanism of "remove stack", just equal to "init stack", but works in reversed manner 116 117 /* 118 this.opts = null; 119 this.vars = null; 120 this.ctrls = null; 121 this.tmpls = null; 122 this.sys = null; 123 */ 124 125 // later unregister in global dispatcher, if ID is set 126 }, 127 128 //////////////////////////// 129 /// about system 130 131 getControlClassName: function(id){ 132 return 'bx-ui-'+this.sys.code+'-'+id; 133 }, 134 135 getControl: function(id, notRequired, scope, getAll){ 136 137 if(!BX.type.isNotEmptyString(id)) 138 return null; 139 140 if(BX.type.isElementNode(this.opts.controls[id])) 141 return this.opts.controls[id]; 142 143 if(!this.ctrls.scope && !searchWholeDoc) 144 return null; 145 146 var sScope = this.ctrls.scope; 147 if(BX.type.isElementNode(scope)) 148 sScope = scope; 149 150 var checkFound = function(result){ 151 return (!getAll && result !== null) || (getAll && result.length > 0); 152 }; 153 154 try{ 155 156 // it might be in a special data attribute 157 var node = sScope[getAll ? 'querySelectorAll' : 'querySelector']('[data-bx-ui-id="'+this.sys.code+'-'+id+'"]'); 158 if(checkFound(node)) 159 return node; 160 161 }catch(e){} 162 163 try{ 164 165 // it might be control class 166 var node = sScope[getAll ? 'querySelectorAll' : 'querySelector']('.'+this.getControlClassName(id)); 167 if(checkFound(node)) 168 return node; 169 170 }catch(e){} 171 172 try{ 173 174 // it might be some other class 175 var node = sScope[getAll ? 'querySelectorAll' : 'querySelector']('.'+id); 176 if(checkFound(node)) 177 return node; 178 179 }catch(e){} 180 181 try{ 182 183 // last chance - it might be a specified selector 184 var node = sScope[getAll ? 'querySelectorAll' : 'querySelector'](id); 185 if(checkFound(node)) 186 return node; 187 188 }catch(e){} 189 190 if(node === null && !notRequired) 191 throw new Error('Requested control node can not be found ('+id+')'); 192 193 return node; 194 }, 195 196 setOption: function(name, value){ 197 this.opts[name] = value; 198 }, 199 200 getOption: function(name){ 201 return this.opts[name]; 202 }, 203 204 getSysCode: function(){ 205 return this.sys.code; 206 }, 207 208 //////////////////////////// 209 /// about templating 210 211 template: function(id, html) 212 { 213 if(typeof html == 'undefined') 214 { 215 return this.tmpls[id]; 216 } 217 else 218 {
219 if(!BX.type.isString(html)) 220 throw new TypeError('Bad template html'); 221 222 this.tmpls[id] = html; 223 } 224 }, 225 226 getHTMLByTemplate: function(templateId, replacements){ 227 228 var html = this.tmpls[templateId]; 229 230 if(!BX.type.isNotEmptyString(html)) 231 { 232 BX.debug("template not found: "+templateId); 233 return ''; 234 } 235 236 for(var k in replacements) 237 { 238 if(!replacements.hasOwnProperty(k)) 239 continue; 240 241 if(typeof replacements[k] != 'undefined' && replacements.hasOwnProperty(k)){ 242 243 var replaceWith = ''; 244 if(k.toString().indexOf('=') == 0){ // leading '=' stands for an unsafe replace - no escaping 245 replaceWith = replacements[k].toString(); 246 k = k.toString().substr(1); 247 }else 248 replaceWith = BX.util.htmlspecialchars(replacements[k]).toString(); 249 250 var placeHolder = '{{'+k.toString().toLowerCase()+'}}'; 251 252 if(replaceWith.search(placeHolder) >= 0) // you must be joking 253 replaceWith = ''; 254 255 while(html.search(placeHolder) >= 0) // new RegExp('', 'g') on user-controlled data seems not so harmless 256 html = html.replace(placeHolder, replaceWith); 257 } 258 } 259 260 return html; 261 }, 262 263 createNodesByTemplate: function(templateId, replacements, onlyTags){ 264 265 var html = this.getHTMLByTemplate(templateId, replacements); 266 267 var template = this.tmpls[templateId]; 268 if(!BX.type.isNotEmptyString(template)) 269 { 270 return null; 271 } 272 273 template = template.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); // trim 274 275 // table makeup behaves not so well when being parsed by a browser, so a little hack is on route: 276 277 var isTableRow = false; 278 var isTableCell = false; 279 280 if(template.search(/^<\s*(tr|th)[^<]*>/) >= 0) 281 isTableRow = true; 282 else if(template.search(/^<\s*td[^<]*>/) >= 0) 283 isTableCell = true; 284 285 var keeper = document.createElement('div'); 286 287 if(isTableRow || isTableCell){ 288 289 if(isTableRow){ 290 keeper.innerHTML = '<table><tbody>'+html+'</tbody></table>'; 291 keeper = keeper.childNodes[0].childNodes[0]; 292 }else{ 293 keeper.innerHTML = '<table><tbody><tr>'+html+'</tr></tbody></table>'; 294 keeper = keeper.childNodes[0].childNodes[0].childNodes[0]; 295 } 296 }else 297 keeper.innerHTML = html; 298 299 if(onlyTags){ 300 301 var children = keeper.childNodes; 302 var result = []; 303 304 // we need only non-text nodes 305 for(var k = 0; k < children.length; k++) 306 if(BX.type.isElementNode(children[k])) 307 result.push(children[k]); 308 309 return result; 310 }else 311 return Array.prototype.slice.call(keeper.childNodes); 312 }, 313 314 replaceTemplate: function(templateId, html){ 315 this.tmpls[templateId] = html; 316 }, 317 318 //////////////////////////// 319 /// about inheritance 320 321 parentConstruct: function(owner, opts){ 322 var c = owner.superclass; 323 if(typeof c == 'object') 324 c.constructor.apply(this, [opts, true]); 325 }, 326 327 handleInitStack: function(nf, owner, opts){ 328 329 this.pushFuncStack('init', owner); 330 331 if(!nf){ 332 BX.merge(this.opts, opts); 333 334 BX.ui.widget.prototype.preInit.call(this); 335 336 var init = function(){ 337 338 if(this.sys.initialized) // already initialized once 339 return; 340 341 this.resolveFuncStack('init'); // resove init stacks 342 343 for(var i in this.sys.stack) 344 { 345 if(this.sys.stack.hasOwnProperty(i)) 346 if(i != 'init') 347 this.resolveFuncStack(i, true); // resolve all other stacks 348 } 349 350 this.sys.initialized = true; 351 this.fireEvent('init', [this]); 352 }; 353 354 if(BX.type.isString(this.opts.initializeByGlobalEvent) && this.opts.initializeByGlobalEvent.length > 0){ 355 var scope = this.opts.globalEventScope == 'window' ? window : document; 356 BX.addCustomEvent(scope, this.opts.initializeByGlobalEvent, BX.proxy(init, this)); 357 }else 358 init.call(this); 359 } 360 }, 361 362 // when you add fName to the stack, function with the corresponding name must exist, at least equal to BX.DoNothing() 363 pushFuncStack: function(fName, owner){ 364 if(BX.type.isFunction(owner.prototype[fName])){ 365 366 if(typeof this.sys.stack[fName] == 'undefined') 367 this.sys.stack[fName] = []; 368 369 this.sys.stack[fName].push({owner: owner, f: owner.prototype[fName]}); 370 } 371 }, 372 373 disableInFuncStack: function(fName, owner){ 374 375 var stack = this.sys.stack[fName]; 376 377 if(typeof stack == 'undefined') 378 return; 379 380 for(var k = 0; k < stack.length; k++){ 381 if(stack[k].owner == owner)
382 stack[k].f = BX.DoNothing; 383 } 384 }, 385 386 resolveFuncStack: function(fName, fire){ 387 388 var stack = this.sys.stack[fName]; 389 390 if(typeof stack == 'undefined') 391 return; 392 393 for(var k = 0; k < stack.length; k++){ 394 stack[k].f.call(this); 395 } 396 397 if(fire) 398 this.fireEvent(fName, [this], document); 399 400 this.sys.stack[fName] = null; 401 }, 402 403 //////////////////////////// 404 /// about events 405 406 fireEvent: function(eventName, args, scope){ 407 scope = scope || this; 408 args = args || []; 409 BX.onCustomEvent(scope, 'bx-ui-'+this.sys.code+'-'+eventName, args); 410 }, 411 412 bindEvent: function(eventName, callback){ 413 BX.addCustomEvent(this, 'bx-ui-'+this.sys.code+'-'+eventName, callback); 414 }, 415 416 //////////////////////////// 417 /// about css states 418 419 setCSSState: function(statName, scope) 420 { 421 this.changeCSSState(statName, scope, true); 422 }, 423 424 dropCSSState: function(statName, scope) 425 { 426 this.changeCSSState(statName, scope, false); 427 }, 428 429 changeCSSState: function(statName, scope, way) 430 { 431 scope = scope || this.ctrls.scope; 432 if(typeof statName != 'string' || statName.length == 0) 433 return; 434 435 BX[way ? 'addClass' : 'removeClass'](scope, 'bx-ui-state-'+statName); 436 }, 437 438 //////////////////////////// 439 /// about miscellaneous 440 441 spawn: function(node, onSpawn){ 442 443 // if spawning was enabled, you can spawn widget on auto-duplicated scope 444 // otherwise, you should prepare scope by yourself 445
446 if(this.opts.useSpawn) 447 BX.html(node, this.tmpls.scope); 448 449 var opts = BX.clone(this.opts); 450 opts.scope = node; 451 452 if(BX.type.isFunction(onSpawn)) 453 onSpawn.apply(this, [opts, node]); 454 455 return new this.constructor(opts); 456 }, 457 458 getRandom: function(){ 459 // only letters, digits, - and _ allowed to return 460 return 'bx'+this.sys.code+ 461 Math.floor((Math.random() * 1000) + 1)+ 462 Math.floor((Math.random() * 1000) + 1); 463 } 464 465 }); 466 467})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.