Javascript-форум (https://javascript.ru/forum/)
-   Ваши сайты и скрипты (https://javascript.ru/forum/project/)
-   -   CComponent - кроссбраузерные компоненты (https://javascript.ru/forum/project/8818-ccomponent-krossbrauzernye-komponenty.html)

tenshi 14.04.2010 17:38

CComponent - кроссбраузерные компоненты
 
терминология:
виджет - это некоторый объект, который привызывается к некоторому элементу и реализует некоторую законченную функциональность связанную с ним.
компонента - это некий менеджер виджетов, который создаёт их при появлении в доме обределённых тэгов и разрушает виджеты при исчезновении этих тэгов.

близкие технологии - htc (ie) и xbl (ff)

виджеты создаются после загрузки элемента, не дожидаясь загрузки всего дома. допустимо произвольное динамическое изменение дома. при переносе элемента перерождения виджета не происходит.

пример - добаляем счётчик кликов ко всем ссылкам:
CComponent({ tag: 'a', factory: function( link ){
	var href= link.href
	link.href= '//example.org/click-counter?' + encodeURIComponent( href )
	this.destroy= function(){
		link.href= href
	}
})

собственно либа:
var CComponent= new function(){

Version: 2
Description: 'attach widget constructor to DOM as live component'
License: 'public domain'
 
Implementation:

var latencyDefault= 50

var CComponent= function( arg ){
	if(!( this instanceof CComponent )) return new CComponent( arg )

    var elements= [], widgets= []
    var timerTracking, timerCleaning

    var latencyTracking= arg.latencyTracking || latencyDefault
    var latencyCleaning= arg.latencyCleaning || latencyDefault
    var field= 'CComponent:' + new Date + Math.random()

    var tag= arg.tag
    var factory= arg.factory

	var attach= function( el ){
	    var widget= new factory( el ) || null
	    el[ field ]= widget
	    if( !widget ) return
	    elements.push( el )
	    widgets.push( widget )
	}

	var attachIfLoaded= function( el ){
	    var cur= el
	    do {
	        if( !cur.nextSibling ) continue
	        attach( el )
	        break
	    } while( cur= cur.parentNode )
	}

	var tracking= function( ){
		var nodes= CComponent.tags( tag )
	    for( var i= 0, len= nodes.length; i < len; ++i ){
	    	var el= nodes[ i ]
		    var widget= el[ field ]
		    if( typeof widget === 'object' ) continue
		    attachIfLoaded( el )
	    }
	    timerTracking= setTimeout( tracking, latencyTracking )
	}

	var cleaning= function( ){
	    var doc= document.documentElement
	    checking: for( var i= elements.length; i--; ){
	    	var el= elements[ i ]
		    var cur= el
		    do {
		        if( cur === doc ) continue checking
		    } while( cur= cur.parentNode )
		    var widget= el[ field ]
		    if( widget.destroy ) widget.destroy()
		    el[ field ]= void 0
		    elements.splice( i, 1 )
		    widgets.splice( i, 1 )
	    }
	    timerCleaning= setTimeout( cleaning, latencyCleaning )
	}

	var revive= function( ){
		tracking()
		cleaning()
	}
	var freeze= function( ){
	    timerTracking= clearTimeout( timerTracking )
	    timerCleaning= clearTimeout( timerCleaning )
	}

	var destroy= function( ){
	    freeze()
	    for( var i= elements.length; i--; ){
	    	var widget= widgets[ i ]
		    if( widget.destroy ) widget.destroy()
	    }
	    elements= []
	    widgets= []
	}
	
	var onload= function( ){ attachIfLoaded= attach }
	// replace with DOMContentLoaded from your framework
	document.addEventListener
	? document.addEventListener( 'load', onload, false )
	: document.attachEvent( 'onload', onload )
	
	this.tag= function( ){ return tag }
	this.field= function( ){ return field }
	this.factory= function( ){ return factory }
	this.latencyTracking= function( ){ return latencyTracking }
	this.latencyCleaning= function( ){ return latencyCleaning }
	this.elements= function( ){ return elements.slice( 0 ) }
	this.widgets= function( ){ return widgets.slice( 0 ) }
	this.revive= revive
	this.freeze= freeze
	this.destroy= destroy

    revive()
}

var cacheTags= {}

CComponent.tags= function( name ){
	var nodes= cacheTags[ name ]
	if( !nodes ){
	    var isIE= /*@cc_on!@*/ false
	    var chunks= /(?:(\w+):)?([-\w]+)/.exec( name )
	    var scope= isIE && chunks[1] || ''
	    var tag= isIE && chunks[2]|| name
	    nodes= cacheTags[ name ]= document.getElementsByTagName( tag )
	    nodes.scopeName= scope
	}
	var res= [], scope= nodes.scopeName
	if( scope ){
		for( var i= 0, len= nodes.length; i < len; ++i ){
			var node= nodes[ i ]
			if( node.scopeName !== scope ) continue
			res.push( node )
		}
	} else 	{
		for( var i= 0, len= nodes.length; i < len; ++i )
			res.push( nodes[ i ] )
	};
	return res
}

Export: return CComponent

Usage:

CComponent(
{   tag: 'c:example'
,   factory: function( el ){
		this.el= el
        alert( 'FOUND ' + this.el.innerHTML )
        this.destroy= function(){
	        alert( 'LOST ' + this.el.innerHTML )
	        delete this.el
        }
    }
} )

}

B~Vladi 16.04.2010 11:00

Цитата:

Сообщение от tenshi
виджеты создаются после загрузки элемента, не дожидаясь загрузки всего дома.

Я так понимаю, это делается с помощья setInterval?

tenshi 16.04.2010 11:01

setTimeout

B~Vladi 20.04.2010 22:31

Кстати, что лучше для этого setInterval или setTimeout? И почему?:)

tenshi 20.04.2010 22:48

таймаут обеспечит вызов не чаще заданного времени, а таймаут будет стараться выполнять именно с заданной частотой. слежение за компонентами не критично по времени, поэтому лучше использовать таймаут и в случае загруженности процессора соответственно снижать частоту

B~Vladi 20.04.2010 23:22

Цитата:

Сообщение от tenshi
в случае загруженности процессора соответственно снижать частоту

Но мы же это не сможем определить, или я не прав?


Часовой пояс GMT +3, время: 20:01.