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 of tag (applies to all tags 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 the modified 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 the tag
+ * @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 the modified 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;
+ }
+
+});