'use strict';angular.module('wordWrapper',['tooltip']).directive('wrapEveryWord',['wrapStringOfWords',function(wrapStringOfWords){/** * wrap every word in ELEMENT and add tooltip to every word. */return{restrict:'A',link:function(scope,elm,attrs){wrapStringOfWords.traverse(elm[0])}};}]).factory('wrapStringOfWords',['processStringOfWords',function(processStringOfWords){/** * find all words in the element * @param {DOM element} */functiontraverse(elm){// 1: element nodeif(elm.nodeType==1){for(vari=0;i<elm.childNodes.length;i++)// recursively call self to processtraverse(elm.childNodes[i]);return;}// 3: text nodeif(elm.nodeType==3){varstring=elm.nodeValue;if(string.replace(/\s*/,'')!=='')// string is not whitespaceelm.parentNode.replaceChild(processStringOfWords.toDom(string),elm);return;}}varserviceInstance={traverse:traverse};returnserviceInstance;}]).factory('processStringOfWords',['AddTooltipToElement',function(AddTooltipToElement){functiontoDom(string){// wrap all words in spanvarspanContainer=htmlStr2Dom(markInSpan(string));// add tooltip to every wordfor(vari=0;i<spanContainer.childNodes.length;i++){varnode=spanContainer.childNodes[i];vartagName=node.tagName;if(tagName&&tagName.toLowerCase()==='span'){AddTooltipToElement.add(node)}}returnspanContainer;}functionhtmlStr2Dom(string){// @see http://stackoverflow.com/questions/3103962/converting-html-string-into-dom-elements// @see http://stackoverflow.com/questions/888875/how-to-parse-html-from-javascript-in-firefoxvarspanContainer=document.createElement('span');spanContainer.innerHTML=string;returnspanContainer;}functionmarkInSpan(string){returnstring.replace(/[AaBbCcDdEeGgHhIiJjKkLlMmNnOoPpRrSsTtUuVvYyĀāĪīŪūṀṁṂṃŊŋṆṇṄṅÑñṬṭḌḍḶḷ]+/g,'<span>$&</span>');}varserviceInstance={toDom:toDom};returnserviceInstance;}]);
'use strict';/* Services */angular.module('tooltip',[]).factory('tooltip',['$rootScope','$compile',function($rootScope,$compile){// init tooltipvarscope=$rootScope.$new(true);varisMouseInTooltip=false;scope.onmouseenter=function(){// mouse enters tooltipisMouseInTooltip=true;};scope.onmouseleave=function(){// mouse leaves tooltipisMouseInTooltip=false;hide();};var_left=0;var_top=0;vartooltip=$compile('<div class="tooltip" ng-mouseenter="onmouseenter()" ng-mouseleave="onmouseleave()"></div>')(scope);tooltip.css('max-width',viewWidth()+'px');// append tooltip to the end of body elementangular.element(document.getElementsByTagName('body')[0]).append(tooltip);functionviewWidth(){// FIXME: why -32 here?return(window.innerWidth||document.documentElement.clientWidth)-32;}functionsetContent(content){tooltip.children().remove();if(angular.isUndefined(content)){throw'In tooltip: content undefined!';}elseif(angular.isString(content)){tooltip.html(content);}else{tooltip.append(content);}}functionsetPosition(position){_left=parseInt(position.left.replace('px',''));_top=parseInt(position.top.replace('px',''));}functionshow(){// move tooltip to the right// (don't cross the right side of browser inner window)var_right=_left+tooltip.prop('offsetWidth');if(_right>viewWidth())_left-=(_right-viewWidth());tooltip.css('left',_left+'px');tooltip.css('top',_top+'px');}functionhide(){if(!isMouseInTooltip)tooltip.css('left','-9999px');}varserviceInstance={viewWidth:viewWidth,isHidden:function(){return(tooltip.css('left')==='-9999px');},getLeftSpace:function(){return_left;},getRightSpace:function(){returnviewWidth()-_left-tooltip.prop('offsetWidth');},setContent:setContent,setPosition:setPosition,show:show,hide:hide};returnserviceInstance;}]).factory('AddTooltipToElement',['tooltip',function(tooltip){// when user's mouse hovers over words,// delay a period of time before look up.varDELAY_INTERVAL=1000;// msvarisMouseInWord=false;functiononWordMouseOver(e){isMouseInWord=true;this.style.color='red';setTimeout(angular.bind(this,function(){// 'this' keyword here refers to raw dom elementif(this.style.color==='red'){// mouse is still on wordvarword=this.innerHTML.toLowerCase();tooltip.setContent(word+' '+word+'<br>'+'<span>'+word+'</span>'+' '+word);tooltip.setPosition({'left':(this.getBoundingClientRect().left+'px'),'top':(this.getBoundingClientRect().top+this.offsetHeight+'px'),});tooltip.show();}}),DELAY_INTERVAL);}functiononWordMouseOut(e){isMouseInWord=false;this.style.color='';setTimeout(angular.bind(this,function(){if(!isMouseInWord){tooltip.hide();}}),DELAY_INTERVAL);}functionadd(elm){elm.onmouseover=onWordMouseOver;elm.onmouseout=onWordMouseOut;}varserviceInstance={add:add};returnserviceInstance;}]);
/** * keyword: css long word force line break * http://webdesignerwall.com/tutorials/word-wrap-force-text-to-wrap */.tooltip{position:absolute;left:-9999px;background-color:#CCFFFF;border-radius:10px;font-family:Tahoma,Arial,serif;word-wrap:break-word;}
Tested on: Chromium Version 50.0.2661.102 Ubuntu 16.04 (64-bit), AngularJS 1.5.5.