Return to Snippet

Revision: 9197
at October 31, 2008 08:57 by kouphax


Updated Code
/*---------------------------------------------------------------------------------
 *
 *  Annotations jQuery Plugin
 *
 *---------------------------------------------------------------------------------
 *
 *  The annotaion framework provides access to client side comment based DOM 
 *  annotations.  The framework does not imply any specific usage and is 
 *  essentially useless on it's own.  It merely provides another way to offer 
 *  metadata for elements.
 *
 *  An annotated DOM Element is any element in the DOM preceeded by one or many 
 *  comments of the form below.  NB. The payload ({...}) is optional.
 *
 *  <!--@Contraints({blank:true})-->
 *  <input type="text" value="test" id="textbox"/>
 *
 *  <!--@ReadOnly-->;
 *  <input type="text" value="test" id="textbox"/>
 *
 *  -------------------------------------------------------------------------------
 *  24/10/2008 - Initial Version
 *  -------------------------------------------------------------------------------
 * 
 *///------------------------------------------------------------------------------ 
 
/*
 *  -------------------------------------------------------------------------------
 *  W A R N I N G   W A R N I N G   W A R N I N G   W A R N I N G   W A R N I N G  
 *  -------------------------------------------------------------------------------
 *
 *  XML Standards dictate that XML parsers are permitted to silently remove HTML
 *  comments before rendering the output.  It cannot be guarenteed that comments
 *  will make it into the final version of the HTML document.  All major browsers
 *  do not exhibit this behaviour.  It is simply a caveat!
 *
 */
(function($) {

    /*-----------------------------------------------------------------------------
     *
     *  Annotate Function
     *
     *-----------------------------------------------------------------------------
     *
     *  Adds comment based annotations to the scoped elements.
     *
     *  @param Annotations variable number of annotations to apply
     *
     *///--------------------------------------------------------------------------     
    $.fn.annotate = function(){
        var args = arguments;
        if(args && args.length > 0){
            this.each(function(){

                $el = $(this);
                var a = $el.data("annotations") || [];

                $.each(args, function(){
                    $el.before("<"+"!-"+"-"+this+"-"+"-"+">");
                    
                    var ann = parse(this, prev($el[0]));                                        
                    $el.trigger("annotation:added", [$el, ann]);
                    
                    a.push(ann);
                });

                $el.data("annotations",a);
            });
            return this;
        }
    }
    
    /*-----------------------------------------------------------------------------
     *
     *  Unannotate Function
     *
     *-----------------------------------------------------------------------------
     *
     *  Removes some or all of the elements annotations depending on the input
     *
     *  @param Filter representing the annotaion types to remove. Optional
     *
     *///--------------------------------------------------------------------------     
    $.fn.unannotate = function(){

        var $args = arguments;
        var filter = $args && $args.length > 0;
        
        return this.each(function(){

            var $el = $(this);
            var filteredAnnotations = [];            
            
            $.each($el.data("annotations"), function(){        
                if(!filter || (filter && $inArray(this.name, $args) != -1)){
                    $(this.source).remove();
                    $el.trigger("annotation:removed", [this]);
                }else{
                    filteredAnnotations.push(this);
                }
            });
            
            $el.data("annotations", (filteredAnnotations.length > 0)?filteredAnnotations:null);
        });        
    }    

    /*-----------------------------------------------------------------------------
     *
     *  Annotations Function
     *
     *-----------------------------------------------------------------------------
     *
     *  Returns a list of annotations for this first object only.  Can be filtered 
     *  by annotation types.
     *
     *  @param Filter representing the annotaion types to collect. Optional
     *
     *///--------------------------------------------------------------------------      
    $.fn.annotations = function(){

        if(this.length === 0) return null;
        
        // attempt to load annotations from cache
        var annotations = this.data("annotations");

        if(!annotations){
            
            annotations = [];
            var p = prev(this[0]);
            
            while(isAnnotation(p)){
                annotations.push( parse($.trim(p.nodeValue), p) );
                p = prev(p);
            }

            this.data("annotations", annotations);
        }

        if(arguments.length > 0){
            var args = arguments;
            annotations = $.map(annotations, function(el){
                var found = false;
                $.each(args, function(){
                    return !(found = (el.name==this));
                });
                return found ? el : null;
            });
        }

        return annotations;
    }

    /*-----------------------------------------------------------------------------
     *
     *  Annotated Utility Function
     *
     *-----------------------------------------------------------------------------
     *
     *  Returns a list of all annotated elements from the root specified or the 
     *  document
     *
     *  @param Filter representing the annotaion types to collect
     *  @param Root element to look for annotations, defaults to document element
     *
     *///--------------------------------------------------------------------------      
    $.annotated = function(filter, root){

        var n = $(root || document)[0];
        var v = $.trim(n.nodeValue);

        if (isAnnotation(n)){
            if(filter){
                if(filter.constructor == String && (parse(v).name === filter)){
                    return [next(n,true)];
                }else if(filter.constructor == Array){
                    var found = false;
                    $.each(filter, function(){
                        return !(found = (parse(v).name === this));
                    });

                    return found?[next(n,true)]:[];
                }else{
                    return [];
                }
            }else{
                return [next(n,true)];
            }
        }

        var children = [];

        for(var m = n.firstChild; m != null; m = m.nextSibling) {
            $.each($.annotated(filter,m), function(){children.push(this)});
        }

        return $(jQuery.unique(children));
    }

    /*-----------------------------------------------------------------------------
     *
     *  Private Functions
     *
     *-----------------------------------------------------------------------------
     *
     *  Helper functions used through out the public API of the plugins
     *
     *///--------------------------------------------------------------------------

    var NODE_COMMENT = 8;
    var NODE_TEXT    = 3;

    function prev(root, skipComments){
        var ps = root ? root.previousSibling : null;        
        return (ps && (ps.nodeType === NODE_TEXT || (ps.nodeType === NODE_COMMENT && skipComments)))?prev(ps,skipComments):ps;
    }

    function next(root, skipComments){
        var ns = root ? root.nextSibling : null;
        return (ns && (ns.nodeType === NODE_TEXT || (ns.nodeType === NODE_COMMENT && skipComments)))?next(ns,skipComments):ns;
    }
    
    function parse(annotation, src){    
        var payload = (annotation.match(/\(.*\)/)||[""])[0];
        var name = (annotation.match(/^@[A-Z,a-z]*/)||[""])[0];        
        return {name:name, data:eval(payload), source:src};
    }    
        
    function isAnnotation(node){       
        return node && (node.nodeType === NODE_COMMENT && $.trim(node.nodeValue).substring(0,1) === "@");
    }
    
    function remove(idx, array){        
        var rest = array.slice(idx + 1 || array.length);  
        array.length = idx < 0 ? array.length + idx : idx;
        return array.push.apply(array, rest);   
    }

})(jQuery);

Revision: 9196
at October 25, 2008 12:52 by kouphax


Initial Code
/*---------------------------------------------------------------------------------
 *
 *  Annotations jQuery Plugin
 *
 *---------------------------------------------------------------------------------
 *
 *  The annotaion framework provides access to client side comment based DOM 
 *  annotations.  The framework does not imply any specific usage and is 
 *  essentially useless on it's own.  It merely provides another way to offer 
 *  metadata for elements.
 *
 *  An annotated DOM Element is any element in the DOM preceeded by one or many 
 *  comments of the form below.  NB. The payload ({...}) is optional.
 *
 *  <!--@Contraints({blank:true <mailto:!--@Contraints({blank:true> })-->
 *  <input type="text" value="test" id="textbox"/>
 *
 *  <!--@ReadOnly <mailto:!--@ReadOnly> -->;
 *  <input type="text" value="test" id="textbox"/>
 *
 *  -------------------------------------------------------------------------------
 *  24/10/2008 - Initial Version
 *  -------------------------------------------------------------------------------
 * 
 *///------------------------------------------------------------------------------ 
 
/*
 *  -------------------------------------------------------------------------------
 *  W A R N I N G   W A R N I N G   W A R N I N G   W A R N I N G   W A R N I N G  
 *  -------------------------------------------------------------------------------
 *
 *  XML Standards dictate that XML parsers are permitted to silently remove HTML
 *  comments before rendering the output.  It cannot be guarenteed that comments
 *  will make it into the final version of the HTML document.  All major browsers
 *  do not exhibit this behaviour.  It is simply a caveat!
 *
 */
(function($) {
    /*-----------------------------------------------------------------------------
     *
     *  Annotate Function
     *
     *-----------------------------------------------------------------------------
     *
     *  Adds comment based annotations to the scoped elements.
     *
     *  @param Annotations variable number of annotations to apply
     *
     *///--------------------------------------------------------------------------     
    $.fn.annotate = function(){
        var args = arguments;
        if(args && args.length > 0){
            this.each(function(){
                $el = $(this);
                var a = $el.data("annotations") || [];
                $.each(args, function(){
                    $el.before("<"+"!-"+"-"+this+"-"+"-"+">");
                    a.push( parse(this) );
                });
                $el.data("annotations",a);
            });
            return this;
        }
    }
    /*-----------------------------------------------------------------------------
     *
     *  Annotations Function
     *
     *-----------------------------------------------------------------------------
     *
     *  Returns a list of annotations for this first object only.  Can be filtered 
     *  by annotation types.
     *
     *  @param Filter representing the annotaion types to collect. Optional
     *
     *///--------------------------------------------------------------------------      
    $.fn.annotations = function(){
        if(this.length === 0) return null;
        
        // attempt to load annotations from cache
        var annotations = this.data("annotations");
        if(!annotations){
            
            annotations = [];
            var p = prev(this[0]);
            
            while(isAnnotation(p)){
                annotations.push( parse($.trim(p.nodeValue)) );
                p = prev(p);
            }
            this.data("annotations", annotations);
        }
        if(arguments.length > 0){
            var args = arguments;
            annotations = $.map(annotations, function(el){
                var found = false;
                $.each(args, function(){
                    return !(found = (el.name==this));
                });
                return found ? el : null;
            });
        }
        return annotations;
    }
    /*-----------------------------------------------------------------------------
     *
     *  Annotated Utility Function
     *
     *-----------------------------------------------------------------------------
     *
     *  Returns a list of all annotated elements from the root specified or the 
     *  document
     *
     *  @param Filter representing the annotaion types to collect
     *  @param Root element to look for annotations, defaults to document element
     *
     *///--------------------------------------------------------------------------      
    $.annotated = function(filter, root){
        var n = root || document;
        var v = $.trim(n.nodeValue);
        if (n.nodeType === NODE_COMMENT && v[0] === "@"){
            if(filter){
                if(filter.constructor == String && (parse(v).name === filter)){
                    return [next(n,true)];
                }else if(filter.constructor == Array){
                    var found = false;
                    $.each(filter, function(){
                        return !(found = (parse(v).name === this));
                    });
                    return found?[next(n,true)]:[];
                }else{
                    return [];
                }
            }else{
                return [next(n,true)];
            }
        }
        var children = [];
        for(var m = n.firstChild; m != null; m = m.nextSibling) {
            $.each($.annotated(filter,m), function(){children.push(this)});
        }
        return $(jQuery.unique(children));
    }
    /*-----------------------------------------------------------------------------
     *
     *  Private Functions
     *
     *-----------------------------------------------------------------------------
     *
     *  Helper functions used through out the public API of the plugins
     *
     *///--------------------------------------------------------------------------
    var NODE_COMMENT = 8;
    var NODE_TEXT    = 3;
    function prev(root, skipComments){
        var ps = root ? root.previousSibling : null;        
        return (ps && (ps.nodeType === NODE_TEXT || (ps.nodeType === NODE_COMMENT && skipComments)))?prev(ps,skipComments):ps;
    }
    function next(root, skipComments){
        var ns = root ? root.nextSibling : null;
        return (ns && (ns.nodeType === NODE_TEXT || (ns.nodeType === NODE_COMMENT && skipComments)))?next(ns,skipComments):ns;
    }
    
    function parse(annotation){    
        var payload = (annotation.match(/\(.*\)/)||[""])[0];
        var name = (annotation.match(/^@[A-Z,a-z]*/)||[""])[0];
        
        return {name:name, data:eval(payload)};
    }    
        
    function isAnnotation(node){       
        return node && (node.nodeType === NODE_COMMENT && $.trim(node.nodeValue).substring(0,1) === "@");
    }
})(jQuery);

Initial URL


Initial Description
The annotaion framework provides access to client side comment based DOM annotations. The framework does not imply any specific usage and is essentially useless on it's own.  It merely provides another way to offer metadata for elements.

An annotated DOM Element is any element in the DOM preceeded by one or many  comments of the form below.  NB. The payload ({...}) is optional.

Initial Title
Annotations jQuery Plugin

Initial Tags
javascript, plugin, jquery

Initial Language
JavaScript