Annotations jQuery Plugin


/ Published in: JavaScript
Save to your folder(s)

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.


Copy this code and paste it in your HTML
  1. /*---------------------------------------------------------------------------------
  2.  *
  3.  * Annotations jQuery Plugin
  4.  *
  5.  *---------------------------------------------------------------------------------
  6.  *
  7.  * The annotaion framework provides access to client side comment based DOM
  8.  * annotations. The framework does not imply any specific usage and is
  9.  * essentially useless on it's own. It merely provides another way to offer
  10.  * metadata for elements.
  11.  *
  12.  * An annotated DOM Element is any element in the DOM preceeded by one or many
  13.  * comments of the form below. NB. The payload ({...}) is optional.
  14.  *
  15.  * <[email protected]({blank:true})-->
  16.  * <input type="text" value="test" id="textbox"/>
  17.  *
  18.  * <input type="text" value="test" id="textbox"/>
  19.  *
  20.  * -------------------------------------------------------------------------------
  21.  * 24/10/2008 - Initial Version
  22.  * -------------------------------------------------------------------------------
  23.  *
  24.  *///------------------------------------------------------------------------------
  25.  
  26. /*
  27.  * -------------------------------------------------------------------------------
  28.  * 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
  29.  * -------------------------------------------------------------------------------
  30.  *
  31.  * XML Standards dictate that XML parsers are permitted to silently remove HTML
  32.  * comments before rendering the output. It cannot be guarenteed that comments
  33.  * will make it into the final version of the HTML document. All major browsers
  34.  * do not exhibit this behaviour. It is simply a caveat!
  35.  *
  36.  */
  37. (function($) {
  38.  
  39. /*-----------------------------------------------------------------------------
  40.   *
  41.   * Annotate Function
  42.   *
  43.   *-----------------------------------------------------------------------------
  44.   *
  45.   * Adds comment based annotations to the scoped elements.
  46.   *
  47.   * @param Annotations variable number of annotations to apply
  48.   *
  49.   *///--------------------------------------------------------------------------
  50. $.fn.annotate = function(){
  51. var args = arguments;
  52. if(args && args.length > 0){
  53. this.each(function(){
  54.  
  55. $el = $(this);
  56. var a = $el.data("annotations") || [];
  57.  
  58. $.each(args, function(){
  59. $el.before("<"+"!-"+"-"+this+"-"+"-"+">");
  60.  
  61. var ann = parse(this, prev($el[0]));
  62. $el.trigger("annotation:added", [$el, ann]);
  63.  
  64. a.push(ann);
  65. });
  66.  
  67. $el.data("annotations",a);
  68. });
  69. return this;
  70. }
  71. }
  72.  
  73. /*-----------------------------------------------------------------------------
  74.   *
  75.   * Unannotate Function
  76.   *
  77.   *-----------------------------------------------------------------------------
  78.   *
  79.   * Removes some or all of the elements annotations depending on the input
  80.   *
  81.   * @param Filter representing the annotaion types to remove. Optional
  82.   *
  83.   *///--------------------------------------------------------------------------
  84. $.fn.unannotate = function(){
  85.  
  86. var $args = arguments;
  87. var filter = $args && $args.length > 0;
  88.  
  89. return this.each(function(){
  90.  
  91. var $el = $(this);
  92. var filteredAnnotations = [];
  93.  
  94. $.each($el.data("annotations"), function(){
  95. if(!filter || (filter && $inArray(this.name, $args) != -1)){
  96. $(this.source).remove();
  97. $el.trigger("annotation:removed", [this]);
  98. }else{
  99. filteredAnnotations.push(this);
  100. }
  101. });
  102.  
  103. $el.data("annotations", (filteredAnnotations.length > 0)?filteredAnnotations:null);
  104. });
  105. }
  106.  
  107. /*-----------------------------------------------------------------------------
  108.   *
  109.   * Annotations Function
  110.   *
  111.   *-----------------------------------------------------------------------------
  112.   *
  113.   * Returns a list of annotations for this first object only. Can be filtered
  114.   * by annotation types.
  115.   *
  116.   * @param Filter representing the annotaion types to collect. Optional
  117.   *
  118.   *///--------------------------------------------------------------------------
  119. $.fn.annotations = function(){
  120.  
  121. if(this.length === 0) return null;
  122.  
  123. // attempt to load annotations from cache
  124. var annotations = this.data("annotations");
  125.  
  126. if(!annotations){
  127.  
  128. annotations = [];
  129. var p = prev(this[0]);
  130.  
  131. while(isAnnotation(p)){
  132. annotations.push( parse($.trim(p.nodeValue), p) );
  133. p = prev(p);
  134. }
  135.  
  136. this.data("annotations", annotations);
  137. }
  138.  
  139. if(arguments.length > 0){
  140. var args = arguments;
  141. annotations = $.map(annotations, function(el){
  142. var found = false;
  143. $.each(args, function(){
  144. return !(found = (el.name==this));
  145. });
  146. return found ? el : null;
  147. });
  148. }
  149.  
  150. return annotations;
  151. }
  152.  
  153. /*-----------------------------------------------------------------------------
  154.   *
  155.   * Annotated Utility Function
  156.   *
  157.   *-----------------------------------------------------------------------------
  158.   *
  159.   * Returns a list of all annotated elements from the root specified or the
  160.   * document
  161.   *
  162.   * @param Filter representing the annotaion types to collect
  163.   * @param Root element to look for annotations, defaults to document element
  164.   *
  165.   *///--------------------------------------------------------------------------
  166. $.annotated = function(filter, root){
  167.  
  168. var n = $(root || document)[0];
  169. var v = $.trim(n.nodeValue);
  170.  
  171. if (isAnnotation(n)){
  172. if(filter){
  173. if(filter.constructor == String && (parse(v).name === filter)){
  174. return [next(n,true)];
  175. }else if(filter.constructor == Array){
  176. var found = false;
  177. $.each(filter, function(){
  178. return !(found = (parse(v).name === this));
  179. });
  180.  
  181. return found?[next(n,true)]:[];
  182. }else{
  183. return [];
  184. }
  185. }else{
  186. return [next(n,true)];
  187. }
  188. }
  189.  
  190. var children = [];
  191.  
  192. for(var m = n.firstChild; m != null; m = m.nextSibling) {
  193. $.each($.annotated(filter,m), function(){children.push(this)});
  194. }
  195.  
  196. return $(jQuery.unique(children));
  197. }
  198.  
  199. /*-----------------------------------------------------------------------------
  200.   *
  201.   * Private Functions
  202.   *
  203.   *-----------------------------------------------------------------------------
  204.   *
  205.   * Helper functions used through out the public API of the plugins
  206.   *
  207.   *///--------------------------------------------------------------------------
  208.  
  209. var NODE_COMMENT = 8;
  210. var NODE_TEXT = 3;
  211.  
  212. function prev(root, skipComments){
  213. var ps = root ? root.previousSibling : null;
  214. return (ps && (ps.nodeType === NODE_TEXT || (ps.nodeType === NODE_COMMENT && skipComments)))?prev(ps,skipComments):ps;
  215. }
  216.  
  217. function next(root, skipComments){
  218. var ns = root ? root.nextSibling : null;
  219. return (ns && (ns.nodeType === NODE_TEXT || (ns.nodeType === NODE_COMMENT && skipComments)))?next(ns,skipComments):ns;
  220. }
  221.  
  222. function parse(annotation, src){
  223. var payload = (annotation.match(/\(.*\)/)||[""])[0];
  224. var name = (annotation.match(/^@[A-Z,a-z]*/)||[""])[0];
  225. return {name:name, data:eval(payload), source:src};
  226. }
  227.  
  228. function isAnnotation(node){
  229. return node && (node.nodeType === NODE_COMMENT && $.trim(node.nodeValue).substring(0,1) === "@");
  230. }
  231.  
  232. function remove(idx, array){
  233. var rest = array.slice(idx + 1 || array.length);
  234. array.length = idx < 0 ? array.length + idx : idx;
  235. return array.push.apply(array, rest);
  236. }
  237.  
  238. })(jQuery);

Report this snippet


Comments

RSS Icon Subscribe to comments

You need to login to post a comment.