diff --git a/js/extra/rainbow-display-stats.js b/js/extra/rainbow-display-stats.js new file mode 100644 index 00000000..a9d5070f --- /dev/null +++ b/js/extra/rainbow-display-stats.js @@ -0,0 +1,139 @@ +$(function() { + + 'use strict'; + + var labels = { + show: 'show usage counts', + hide: 'hide usage counts' + }; + + /** + * Rainbow.displayStats(); + * + * Adds a new element before
tag and displays usage counts. + * + * @param tag {String} optional - any element container oftag (applies to alltags if no param given) + * @param fullList {Boolean} optional - if true will expand the list of usages + */ + Rainbow.displayStatsOn = function(tag,fullList){ + + Rainbow.statsOn = tag || 'body'; + Rainbow.statsFull = fullList; + + getElementLink(); + + }; + + /******************** + * PRIVATE METHODS + ********************/ + + function getElementLink(){ + var $link = $('').attr('href','#').text(labels.show); + + $link.on('click',createOrToggleList); + + $(Rainbow.statsOn).find('pre').before($link); + } + + function getElementStats($target){ + var classes = getClasses($target.next()), + $stats = $('').attr('class','rainbow-stats'), + $class = $('').attr('class','rainbow-stats-class'), + $count = $('').attr('class','rainbow-stats-count'); + + for(var c in classes){ + if(classes.hasOwnProperty(c)){ + $stats.append( + $('') + .append($count.clone().text(classes[c].count)) + .append($class.clone().text(classes[c].name) + .on('click',doHighlight) + ) + ); + } + } + + $target.after($stats); + + return $stats; + } + + function getClasses($el){ + + var classes = {}; + + if(!$el || !$el.is || !$el.is('pre')) { + return null; + } + + $el.find('span').each(function(i,e){ + + var clsStr = $(e).attr('class'); + + classes[clsStr] || (classes[clsStr]=0); + classes[clsStr]++; + + if(Rainbow.statsFull === true){ + var clsArr = clsStr.split(' '); + for(var c in clsArr){ + if(clsArr.hasOwnProperty(c)){ + classes[clsArr[c]] || (classes[clsArr[c]]=0); + classes[clsArr[c]]++; + } + } + } + + }); + + var classesArray = []; + for(var cl in classes){ + if(classes.hasOwnProperty(cl)){ + classesArray.push({name:cl,count:classes[cl]}); + } + } + + return classesArray.sort(function(a,b){return a.name > b.name;}); + + } + + /******************** + * EVENT HANDLERS + ********************/ + + function createOrToggleList(e){ + e.preventDefault(); + + var $t = $(e.currentTarget), + $stats = $t.next('.rainbow-stats'); + + var action = $stats.length===0 ? 'create' : $stats.css('display')==='none' ? 'show' : 'hide'; + + switch(action){ + case 'create': + $t.text(labels.hide); + getElementStats($t); + break; + case 'show': + $t.text(labels.hide); + $stats.show(); + break; + case 'hide': + $t.text(labels.show); + $stats.hide(); + break; + } + } + + function doHighlight(e){ + var $t = $(e.currentTarget), + cl = $t.text().split(' '); + + $('.rainbow-stats-class').removeClass('rainbow-stats-selected'); + $t.addClass('rainbow-stats-selected'); + + cl.unshift(''); + Rainbow.doHighlight && Rainbow.doHighlight($t.closest('.rainbow-stats').next(),cl.join('.')); + } + +}); diff --git a/js/extra/rainbow-extensions.css b/js/extra/rainbow-extensions.css new file mode 100644 index 00000000..20a0cb83 --- /dev/null +++ b/js/extra/rainbow-extensions.css @@ -0,0 +1,49 @@ +/** + * TOGGLE HIGHLIGHT + * + * rainbow-toggle-highlight.js + */ +pre .highlighted { + background-color: rgba(255, 255, 0, 0.2); + box-shadow: 0 0 2px 2px #ffff00; +} + +/** + * TOGGLE INFO + * + * rainbow-toggle-info.js + */ +#rainbowOverlay { + background: #002b36; + border-radius: 4px; + bottom: 0; + color: #839496; + left: 0; + margin: 8px; + padding: 8px 16px; + position: fixed; +} + +#rainbowOverlay strong { + color: #acb8b9; +} + +/** + * DISPLAY STATS + * + * rainbow-display-stats.js + */ +.rainbow-stats-class { + cursor: pointer; +} + +.rainbow-stats-count { + display: inline-block; + margin: 0 8px 0 0; + text-align: right; + width: 30px; +} + +.rainbow-stats-selected { + font-weight: bold; +} diff --git a/js/extra/rainbow-toggle-highlight.js b/js/extra/rainbow-toggle-highlight.js new file mode 100644 index 00000000..e40b3aa8 --- /dev/null +++ b/js/extra/rainbow-toggle-highlight.js @@ -0,0 +1,117 @@ +$(function() { + + 'use strict'; + + /** + * Rainbow.toggleHighlightOn(tag:String, toggle:Boolean); + * + * Toggle click event for highlight every Rainbow generated span tags. + * + * @param tag {String} any ancestor tag of themodified by Rainbow + * @param toggle {Boolean} optional + * @returns {Boolean} returns false if no tags were found + */ + Rainbow.toggleHighlightOn = function(tag, toggle){ + + if($(tag).length===0) { + return false; + } + + typeof(toggle)!=='boolean' && (toggle = !$(tag).data('Rainbow.toggleHighlightState')); + + if(!!toggle){ + $(tag).on('click', 'pre', highlightClasses); + }else{ + $(tag).off('click', 'pre', highlightClasses); + } + + $(tag).data('Rainbow.toggleHighlightState', !toggle); + + return true; + }; + + /** + * Rainbow.doHighlight($parent:jQuery, selector:String); + * + * Publicly exposed method for a single highlight. + * + * @param $parent {jQuery} the element that contains thetag + * @param selector {String} the selector to be highlighted + */ + Rainbow.doHighlight = function($parent,selector){ + removeHighlight($parent); + addHighlight($parent,selector); + }; + + /** + * Rainbow.setHighlightClass(className:String); + * + * Call this method in order to customize the className for highlighted elements + * + * @param className {String} className for highlighted tags + */ + Rainbow.setHighlightClass = function(className){ + if(typeof(className)==='string') { + Rainbow.toggleHighlightClass = className; + } + }; + + // sets default className + Rainbow.setHighlightClass('highlighted'); + + /******************** + * PRIVATE METHODS + ********************/ + + function highlightClasses(e){ + e.stopPropagation(); + + if(!Rainbow.toggleHighlightClass || typeof(Rainbow.toggleHighlightClass)!=='string' || Rainbow.toggleHighlightClass.length===0){ + return; + } + + var $e = $(e.target), + $pre = $e.is('pre') ? $e : $e.closest('pre'); + + if(!$e.is('span')) { + removeHighlight($pre); + return; + } + + if($e.parents('.' + Rainbow.toggleHighlightClass).length){ + $e = $e.parents('.' + Rainbow.toggleHighlightClass).parents('span'); + }else if($e.hasClass(Rainbow.toggleHighlightClass)){ + $e = $e.parents('span'); + } + + removeHighlight($pre); + + if($e.length){ + var cl = $e.attr('class').split(' '), + classes = ['']; + + for(var c in cl){ + if(cl.hasOwnProperty(c)){ + classes.push(cl[c]); + } + } + + addHighlight($pre,classes.join('.')); + + console.log('highlight on: ',classes.join('.')); + }else { + console.log('highlight off'); + } + + Rainbow.refreshOverlay && Rainbow.refreshOverlay(); + } + + function addHighlight($parent,selector){ + $parent.find(selector).addClass(Rainbow.toggleHighlightClass); + } + + function removeHighlight($parent){ + $parent.find('.'+Rainbow.toggleHighlightClass).removeClass(Rainbow.toggleHighlightClass); + } + +}); diff --git a/js/extra/rainbow-toggle-info.js b/js/extra/rainbow-toggle-info.js new file mode 100644 index 00000000..49f9432f --- /dev/null +++ b/js/extra/rainbow-toggle-info.js @@ -0,0 +1,138 @@ +$(function() { + + 'use strict'; + + /** + * Rainbow.toggleInfoOn(tag:String, toggle:Boolean); + * + * Toggle mouseover/out events for display classes info on every Rainbow generated span tags. + * + * @param tag {String} any ancestor tag of themodified by Rainbow + * @param toggle {Boolean} optional + * @param keepOverlay {Boolean} optional - whether to keep the overlay on mouseOut or not + * @returns {boolean} returns false if no tags were found + */ + Rainbow.toggleInfoOn = function(tag, toggle, keepOverlay) { + + if($(tag).length===0) { + return false; + } + + typeof(toggle)!=='boolean' && (toggle = !$(tag).data('Rainbow.overlayInfoState')); + + if(!!toggle){ + $(tag).on('mouseover', 'pre span', showOverlay); + keepOverlay || $(tag).on('mouseout', 'pre span', hideOverlay); + }else{ + $(tag).off('mouseover', 'pre span', showOverlay); + $(tag).off('mouseout', 'pre span', hideOverlay); + } + + $(tag).data('Rainbow.overlayInfoState', !toggle); + + return true; + }; + + /** + * Rainbow.setOverlayContent(classes:Array); + * + * Override this method in order to customize the overlay content + * + * @param classes {Array} list of classes to be displayed as info + */ + Rainbow.setOverlayContent = function(classes){ + return ['',classes.join(',^ ').split(',').join('
'),'
'].join(''); + }; + + /** + * Rainbow.setOverlayContainer($dom:jQuery); + * + * Call this method in order to customize the overlay main element + * + * @param $dom {jQuery} a jquery object that will be used as overlay info + * @param ID {String} optional + */ + Rainbow.setOverlayContainer = function($dom, ID){ + ID || (ID='rainbowOverlay'); + + if(typeof($dom)==='object' && typeof(ID)==='string'){ + $dom.attr('id',ID); + Rainbow.$overlayInfoContainer = $dom; + } + }; + + // sets default className + Rainbow.setOverlayContainer($(''), 'rainbowOverlay'); + + /** + * Rainbow.refreshOverlay(); + * + * Publicly exposed method to let the overlay refresh upon new highlights. + */ + Rainbow.refreshOverlay = function(){ + showOverlay({target:Rainbow.$overlayedEl}); + }; + + /******************** + * PRIVATE METHODS + ********************/ + + function hideOverlay(){ + Rainbow.$overlayInfoContainer && Rainbow.$overlayInfoContainer.remove(); + } + + function showOverlay(e){ + e.stopPropagation && e.stopPropagation(); + + hideOverlay(); + + var $e = $(e.target), + classes = getClasses($e); + + Rainbow.$overlayedEl = $e; + + if(classes){ + var html = Rainbow.setOverlayContent(classes), + $overlay = Rainbow.$overlayInfoContainer; + + $overlay && $overlay.html(html).appendTo('body'); + } + } + + function getClasses($e){ + + var classes, $p; + + if($e.is('span')){ + classes = [getRainbowClasses($e)]; + + $p = $e.parent(); + while($p.is('span')){ + classes.push(getRainbowClasses($p)); + $p = $p.parent(); + } + + return classes; + } + + return null; + } + + // removes the class defined in Rainbow.toggleHighlightClass from the returning classes string + function getRainbowClasses($el){ + var classes = $el.attr('class'); + + if(~classes.indexOf(Rainbow.toggleHighlightClass)){ + var classes_arr = classes.split(' '), + idx = classes_arr.indexOf(Rainbow.toggleHighlightClass); + + if(idx>-1){ + classes_arr.splice(idx,1); + classes = ''+classes_arr.join(' ')+''; + } + } + + return classes; + } + +});