├── README.md ├── bookmarklet.js ├── jquery.planize.js └── index.html /README.md: -------------------------------------------------------------------------------- 1 | jqPlanize 2 | ========= 3 | 4 | The planize [jQuery](http://jquery.com/) plugin adds some hierarchical data organization features to given DOM node containing HTML headings: 5 | 6 | * adds numerotation and anchors in front of all headings, 7 | * generates an HTML table of contents, 8 | * degrades gracefully if javascript is not available/enabled. 9 | 10 | You can see the plugin in action on [this page](http://prendreuncafe.com/work/jqplanize/index.html). 11 | 12 | How does this work? 13 | ------------------- 14 | 15 | The plugin parse the given DOM node children to find existing headings, and renumerote them according to their hierarchical structure. 16 | 17 | Have you got some examples? 18 | --------------------------- 19 | 20 | For example, if you have this HTML structure: 21 | 22 |

Main title

23 |

Sub section

24 |

Sub sub question

25 |

Other sub sub question

26 |

Other sub section

27 | 28 | Running this snippet: 29 | 30 | $(document).ready(function(){ 31 | $('html *').planize({ number_suffix: ')' }); 32 | }); 33 | 34 | Will produce: 35 | 36 |

1) Main title

37 |

1.1) Sub section

38 |

1.1.1) Sub sub question

39 |

1.1.2) Other sub sub question

40 |

1.2) Other sub section

41 | 42 | Optionnaly, the plugin can generate the corresponding Table Of Content: 43 | 44 | $(document).ready(function(){ 45 | $('html *').planize({ 46 | number_suffix: ')', 47 | generate_toc: true, 48 | toc_elem: $('#toc')}), 49 | }); 50 | 51 | Will produce: 52 | 53 |
54 | 67 |
68 | 69 |

1) Main title

70 |

1.1) Sub section

71 |

1.1.1) Sub sub question

72 |

1.1.2) Other sub sub question

73 |

1.2) Other sub section

74 | 75 | This work is in a very early state and has not been thoroughly tested, but your contributions are warmly welcome. 76 | 77 | Installation 78 | ------------ 79 | 80 | Installation is fairly simple. Just add these lines within the `` tag of your HTML document: 81 | 82 | 83 | 84 | 85 | Note: Maybe storing your own local copy of the jquery.planize.js would be a great idea. 86 | 87 | Known issues 88 | ------------ 89 | 90 | * Currently jqPlanize doesn't work in Opera. 91 | 92 | License 93 | ------- 94 | 95 | This work is released under the terms of the [MIT license](http://en.wikipedia.org/wiki/MIT_License). 96 | 97 | FAQ 98 | --- 99 | 100 | >Why didn't you use the ordered list features of the HTML/CSS2 specs? 101 | 102 | Because it's lame. Well okay, let's say, bloated and complex. I'll try to implement it when Internet Explorer will have full css2.1 support (read: never). 103 | 104 | > Why the hell the code is so ugly? 105 | 106 | This is my first jQuery plugin authoring attempt, that's maybe why. 107 | 108 | >Why didn't you use a recursive function to generate TOC subitems code? 109 | 110 | Because parsing a linear array of headings and generating a nested list is not that easy, but I can't wait seeing your patch. 111 | -------------------------------------------------------------------------------- /bookmarklet.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jqplanize script to be launched from this bookmarklet code: 3 | * 4 | * javascript:var%20s=document.createElement('script');s.setAttribute('src','http://prendreuncafe.com/work/jqplanize/bookmarklet.js');document.getElementsByTagName('head')[0].appendChild(s);void(0) 5 | * 6 | * @author Nicolas Perriault 7 | * @license MIT 8 | */ 9 | (function() { 10 | 11 | var debug = false; 12 | var h = document.getElementsByTagName('head')[0]; 13 | var scripts = { 14 | jquery: 'http://ajax.googleapis.com/ajax/libs/jquery/1.2/jquery.js', 15 | planize: 'http://prendreuncafe.com/work/jqplanize/jquery.planize.js', 16 | }; 17 | var t1, t2; 18 | var css = document.createTextNode ( 19 | "#jqplanize_bookmarklet_toc * { text-align: left; color: #fff; font-family: Arial, sans; font-size: 1em; font-weight: normal; font-variant: normal; line-height: 1.2em; }" + 20 | "#jqplanize_bookmarklet_toc { position: fixed; top: 0; right: 0; bottom: 0; width: 40%; margin: 0 0 0 1em; padding: .5em; background: #000; border-left: 5px solid #ccc; overflow: auto; z-index: 3000; opacity: 0.95 }" + 21 | "#jqplanize_bookmarklet_toc h4 { padding: 0; margin: 0; font-size: 1.2em; color: #fff; }" + 22 | "#jqplanize_bookmarklet_toc a { color: #fff; }" + 23 | "#jqplanize_bookmarklet_toc ul { list-style-type: none; margin-left: 1em; padding-left: 0; line-height: 1.5em; }" 24 | ); 25 | var loadInterval = 250; 26 | 27 | var process = function() { 28 | log('Start processing document'); 29 | var s3 = document.createElement('style'); 30 | s3.setAttribute('type', 'text/css'); 31 | s3.setAttribute('media', 'screen'); 32 | s3.appendChild(css); 33 | h.appendChild(s3); 34 | 35 | var toc = document.createElement('div'); 36 | toc.id = 'jqplanize_bookmarklet_toc'; 37 | var b = document.getElementsByTagName('body')[0]; 38 | b.appendChild(toc); 39 | 40 | jQuery('html *').planize({ 41 | debug: debug, 42 | generate_toc: true, 43 | toc_elem: jQuery('#jqplanize_bookmarklet_toc'), 44 | toc_title: 'jqPlanize generated TOC', 45 | callback: function(e) { 46 | jQuery(e).children('a:link').css('color', '#fff'); 47 | jQuery(e).prepend('[x]'); 48 | log('Processing done.'); 49 | }, 50 | }); 51 | }; 52 | 53 | var scriptEmbedded = function(name) { 54 | for (node in h.childNodes) { 55 | if (node.nodeType == 1 && node.tagName.toLowerCase() == 'script' && node.getAttribute('src') && node.getAttribute('src') == scripts[name]) { 56 | return true; 57 | } 58 | } 59 | return false; 60 | } 61 | 62 | var loadJQuery = function() { 63 | if (typeof jQuery == 'undefined') { 64 | if (!scriptEmbedded(scripts.jquery)) { 65 | log('Loading jQuery...'); 66 | var s1 = document.createElement('script'); 67 | s1.setAttribute('src', scripts.jquery); 68 | s1.setAttribute("type","text/javascript"); 69 | h.appendChild(s1); 70 | } 71 | } else { 72 | log('jQuery plugin loaded'); 73 | // Check for prototype 74 | if (typeof window.Prototype != 'undefined' && typeof window.Prototype.Version == 'string') 75 | { 76 | log('Prototype library detected, jQuery noconflict mode enabled'); 77 | jQuery.noConflict(); 78 | } 79 | clearInterval(t1); 80 | delete t1; 81 | t2 = setInterval(loadPlanize, loadInterval); 82 | } 83 | }; 84 | 85 | var loadPlanize = function() { 86 | if (typeof jQuery.fn.planize == 'undefined') { 87 | if (!scriptEmbedded(scripts.planize)) { 88 | log('Loading jqplanize plugin...'); 89 | var s2 = document.createElement('script'); 90 | s2.setAttribute('src', scripts.planize); 91 | s2.setAttribute("type","text/javascript"); 92 | h.appendChild(s2); 93 | } 94 | } else { 95 | log('jqplanize plugin loaded'); 96 | clearInterval(t2); 97 | delete t2; 98 | process(); 99 | } 100 | }; 101 | 102 | var log = function() { 103 | if (!debug) { 104 | return; 105 | } 106 | try { 107 | console.log.apply(console, arguments); 108 | } catch(e) { 109 | try { 110 | opera.postError.apply(opera, arguments); 111 | } catch(e){} 112 | } 113 | } 114 | 115 | t1 = setInterval(loadJQuery, loadInterval); 116 | 117 | })(); -------------------------------------------------------------------------------- /jquery.planize.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The planize jQuery plugin adds some features for dealing with hierarchical headings in a given DOM element. 3 | * 4 | * - adds enumerations and anchors in all headings, 5 | * - can generates an HTML table of content and append it to an existing DOM element, 6 | * - in an unobstrusive way. 7 | * 8 | * Example of use: 9 | * 10 | * $('html *').planize(); 11 | * 12 | * Configuration object parameters documentation: 13 | * - add_anchors : generates anchors for each header (automatically set to true if `generate_toc` is set to true) 14 | * - callback : a function called when processing is finished 15 | * - debug : prints pretty debug messages into the firebug or opera console, if available 16 | * - generate_toc : generates an html unordered list containing the table of content of the document 17 | * - min_level : min heading level needed to be included in toc and be renumbered (0 = all headings) 18 | * - max_level : max heading level needed to be included in toc and be renumbered (0 = all headings) 19 | * - number_suffix : heading identifier suffix, eg. ')' in "1.2.3)" 20 | * - number_separator : separator for numbers, eg. '.' in "1.2.3)" 21 | * - toc_elem : the dom element where the toc will be append 22 | * - toc_none : the message to display if no headings have been found in the current document 23 | * - toc_title : the title of the table of content 24 | * 25 | * @requires jQuery v1.2 or higher 26 | * @author Nicolas Perriault 27 | * @license MIT (http://www.opensource.org/licenses/mit-license.php) 28 | * @param Object config Plugin configuration 29 | * @return jQuery(this) 30 | * 31 | */ 32 | (function(jQuery){ 33 | 34 | jQuery.fn.planize = function(config) { 35 | 36 | var self = jQuery(this); 37 | var processed = false; 38 | var toc = ''; 39 | var defaultConfig = { 40 | add_anchors : false, 41 | callback : null, 42 | debug : false, 43 | generate_toc : false, 44 | min_level : 1, 45 | max_level : 6, 46 | number_suffix : '', 47 | number_separator : '.', 48 | toc_elem : null, 49 | toc_none : 'No heading found for this document', 50 | toc_title : 'Table of contents' 51 | }; 52 | config = jQuery.extend(defaultConfig, config); 53 | 54 | /** 55 | * Prepends all headers text with the current tree number reference 56 | 57 | * @return void 58 | */ 59 | var process = function() { 60 | var level = 0; 61 | var levels = [0,0,0,0,0,0,0]; 62 | var hLevelText = ''; 63 | var prependText = ''; 64 | var prevLevel = 0; 65 | var n = 0; 66 | self.children('*:header:visible').each(function(index, heading) { 67 | log('Processing heading %o', heading); 68 | level = parseInt(heading.tagName.substring(1)); 69 | if (config.min_level <= level && level <= config.max_level) { 70 | n++; 71 | levels[level]++; 72 | for (var l = 1; l <= level; l++) { 73 | hLevelText += levels[l] > 0 ? levels[l] + config.number_separator : ''; 74 | } 75 | levels[level + 1] = 0; 76 | hLevelText = hLevelText.substring(0, hLevelText.length - 1); 77 | prependText = hLevelText; 78 | if (config.generate_toc || config.add_anchors) { 79 | if (config.generate_toc) { 80 | var link = '' +jQuery('').text(jQuery(this).text()).html() + ''; 81 | var elem = "\n"+'
  • ' + hLevelText + (config.number_suffix ? config.number_suffix : '') + ' ' + link; 82 | if (level < prevLevel) { 83 | log(hLevelText + ', unnesting because:' + level + '<' + prevLevel); 84 | var unnest = ''; 85 | while (level < prevLevel) { 86 | unnest += ''; 87 | prevLevel--; 88 | } 89 | toc += unnest + elem + '
  • '; 90 | } else if (level > prevLevel) { 91 | log(hLevelText + ', nesting because:' + level + '>' + prevLevel); 92 | toc += '