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

Report this snippet


Comments

RSS Icon Subscribe to comments

You need to login to post a comment.